The simple but not easily parametrizable approach is to direct code
the logic using the case:
always @*
case(a)
0: x = 8'b0000_0001;
1: x = 8'b0000_0011;
2: x = 8'b0000_0111;
3: x = 8'b0000_1111;
4: x = 8'b0001_1111;
5: x = 8'b0011_1111;
6: x = 8'b0111_1111;
default: x = 8'b1111_1111;
endcase
The other approach suggested here:
http://groups.google.com/group/comp.arch/browse_thread/thread/158b5652984c77e5/8bd9ec4178c6bf74?hl=en&lnk=gst&q=thermometer#
Is to implement one-hot and convert to thermometer
1) Build n -> 2^n one-hot coder
always @*
begin
x= 0;
x[a] = 1'b1;
end
2) Convert one-hot to thermometer
x_therm = (x-1) | x;
I took challenge and coded for laughs a recursive thermometer scale
coder. (see below)
The design halves the input width on every call so it will converge
very fast.
I have done some synthesis tests to compare the area for some larger
output widths ((x width = 256)
The logic input and output are registered and synthesized at a
challenging 2ns clock period.
The recursive code synthesizes to a size slightly larger than one hot
coder.
The case statement code is about x2 bigger while the conversion method
resulted in more than x4 area of recursive code.
The conversion is not efficient because it results in subtractor of x
bits width.
In the case of case statement it seems that synthesis tool logic
optimizer gave up.
Any other ideas?
Cheers,
Przemek
Recursive generate thermometer coder:
module decoder_n_2n_therm_rec (
a,
x
);
parameter N=1;
// local params
parameter X_WIDTH = (1<<N);
parameter X0_WIDTH = (1<<(N>>1));
input [N-1:0] a;
output [X_WIDTH-1:0] x;
wire [X0_WIDTH-1:0] x_0;
wire [X0_WIDTH:0] x_1;
assign x_1[X0_WIDTH]=1'b0;
generate
genvar i0, i1;
if(N>1) begin : rec
decoder_n_2n_therm_rec
#(
.N(N>>1)
)
decoder_n_2n_therm_rec_0
(
.a(a[(N>>1)-1:0]),
.x(x_0)
);
decoder_n_2n_therm_rec
#(
.N(N>>1)
)
decoder_n_2n_therm_rec_1
(
.a(a[N-1:(N>>1)]),
.x(x_1[X0_WIDTH-1:0])
);
for(i0=0;i0<X0_WIDTH;i0=i0+1) begin : x0_loop
for(i1=0;i1<X0_WIDTH;i1=i1+1) begin : x1_loop
assign x[i1*X0_WIDTH + i0] = x_1[i1+1] | (~x_1[i1+1] & x_1[i1] & x_0
[i0]);
end
end
end
else begin : rec_end
assign x[0] = 1'b1;
assign x[1] = a[0];
end
endgenerate
endmodule
Just modify your 1-hot encoder:
x[a:0] = {(a+1){1'b1}};
assign x = 1 << (a-1);
smarter syntehsis tools should be able to reduce it to the same amount
of logic than your case statement.
N
It would be a good idea but it is not a valid Verilog. (repetition
multiplier and bit range must be constant expressions)
Pity because having ability to use signal for repetition and range
definitions would open a lot of new expressivness in Verilog.
Here's an attempt that's fairly short and doesn't use a adder/
subtracter.
The parameter is the log2 of the desired width of the thermometer, ie,
set
it to 4 for a 16 bit thermometer. Note that the top bit never gets
set. An
input code of 0 gives a 0 output, an input of 2 gives an output of 3,
an input
of 15 gives an output of 16'h7fff
module thermometer #(
parameter WIDTH_LOG2 = 3
) (
input [WIDTH_LOG2 : 1] code,
output [1<<WIDTH_LOG2 : 1] result
);
wire [(2<<WIDTH_LOG2) : 1] shifter;
assign shifter = ( {1<<WIDTH_LOG2{1'b1}} ) << code;
assign result = shifter >> (1<<WIDTH_LOG2);
endmodule
John Providenza
Yes, I forgot the - 1 ,
or better the -1 is at the wrong place and 1 << a should be 2 << a if
you want, that a == 0 has already one led lit.
assign x= (2 << a) - 1;
this would give
'b001 for a == 0,
'b011 for a == 1
'b111 for a == 2
What I'd expect is, that smarter synthesis tools should be able to
reduce the logic. You have to try this with your synth SW though.
bye
N
you could reduce the subtract unit from N bits to log_2(N) bits
A
parameter N=8;
parameter LOG_N = 3;
input [LOG_N:0] a;
wire [LOG_N-1:0] rshiftval;
wire [N-1:0] x;
assign rshiftval = {N{1'b1}} - a;
assign x = {N{1'b1}} >> rshiftval;
N
Yes, the shifter is the solution. It is as efficient in terms of area
as the recursive logic
and it is much more conscious.
FYI the synthesis results:
shifter:
Total cell area: 17280.614178
Total area: 175014.770977
dc_shell used shifter component (DW_leftsh) from Designware library to
implement this code
decoder_n_2n_therm_rec
Total cell area: 17432.082979
Total area: 172366.223848
Cheers,
Przemek
shifter:
Total cell area: 74406.303181
Total area: 716449.689045
rec:
Total cell area: 73381.144846
Total area: 688077.723154
Both seem to scale about the same with number of bits.
BTW The issue may seem trivial but most recent Synopsys Designware
bulletin mentions amongst all
two new components added to new version of library:
DW_thermdec - thermometer scale decoder with enable
DW_decode_en - one hot decoder with enable
Looks like they thought this was important enough to justify the
effort off adding it to library.
assign x = ~(~0 << a);
This version produces 0 for an input of 0. If you want a single 1 for
an input of 0, you could add 1 to a, or just concatenate a constant 1
to the bottom end of x before connecting it to the output.
Or use
assign x = ~(~1 << a);
Don't know how well synthesis would handle each of these.