Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is the best thermometer coder? (needs to be easily parametrizable)

1,316 views
Skip to first unread message

Przemek

unread,
Mar 25, 2009, 12:50:06 AM3/25/09
to
I was musing what is a best simple, efficient and parametrizable
thermometer coder design pattern.

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

Poojan Wagh

unread,
Mar 25, 2009, 7:05:55 AM3/25/09
to
On Mar 24, 11:50 pm, Przemek <bazar...@hotmail.com> wrote:
> I was musing what is a best simple, efficient and parametrizable
> thermometer coder design pattern.
>
> 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/158b565...

Just modify your 1-hot encoder:
x[a:0] = {(a+1){1'b1}};

News123

unread,
Mar 25, 2009, 3:25:55 PM3/25/09
to
Anything wrong about?

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

Przemek

unread,
Mar 25, 2009, 9:44:11 PM3/25/09
to
Poojan,

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.

Przemek

unread,
Mar 25, 2009, 9:47:00 PM3/25/09
to
But isn't it a one hot coder still? (just shifted right)
You need to subtract 1 from x and this makes it expensive because x
has 2^(a width) bits

jprov...@yahoo.com

unread,
Mar 26, 2009, 1:02:57 AM3/26/09
to

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

News123

unread,
Mar 26, 2009, 4:26:33 AM3/26/09
to
Thanks Przemek for pinting out.

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

News123

unread,
Mar 26, 2009, 4:47:08 AM3/26/09
to
>> NIn acse your synth tool has problems optimizing

assign x = 1 << (a-1);

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

Przemek

unread,
Mar 26, 2009, 6:20:00 AM3/26/09
to
John, News123,

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

Przemek

unread,
Mar 26, 2009, 7:00:24 AM3/26/09
to
Results for N=10

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.

sh...@cadence.com

unread,
Apr 2, 2009, 1:21:30 PM4/2/09
to
An alternate version with a shifter:

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.

sh...@cadence.com

unread,
Apr 2, 2009, 1:23:58 PM4/2/09
to
On Apr 2, 1:21 pm, sh...@cadence.com wrote:
>
> 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.

0 new messages