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

P

Przemek

Guest
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
 
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...

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}};
 
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 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/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,

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.

Just modify your 1-hot encoder:
x[a:0] = {(a+1){1'b1}};
 
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


On Mar 25, 11:25 am, News123 <news...@free.fr> wrote:
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
 
On Mar 25, 6:47 pm, Przemek <bazar...@hotmail.com> wrote:
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

On Mar 25, 11:25 am, News123 <news...@free.fr> wrote:

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
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
 
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


Przemek wrote:
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


On Mar 25, 11:25 am, News123 <news...@free.fr> wrote:
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 wrote:
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


On Mar 25, 11:25 am, News123 <news...@free.fr> wrote:
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.

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
 
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
 
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.




On Mar 26, 2:20 am, Przemek <bazar...@hotmail.com> wrote:
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
 
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.
 
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.
 

Welcome to EDABoard.com

Sponsor

Back
Top