英文:
Division in Verilog and Q factor representation
问题
I am currently working on a design of an algorithm for signal processing. I created a model in software that appears to work fine and I am now trying to translate it to verilog. Below is what I do in the software.
I get a 16 bit input, I do the following:
a. Convert the hex to decimal.
b. Subtract 32768 from the above.
c. Divide the result by 32768.
d. Convert the result to a signed number in the Q(4,20) format (4 bits for integer, 20 bits fractional).
For example,
Case 1: value is 0x803f (i.e., value > 0x8000)
a. Convert 0x803f to decimal, i.e., 32831.
b. 32831 - 32768 = 63.
c. 63/32768 = 0.001922.
d. Convert to signed Q(4,20) 0x0007C8.
Case 2: value is 0x79fc (i.e., value < 0x8000)
a. Convert 0x79fc to decimal, i.e., 31228.
b. 32668 - 32768 = -100.
c. -100/32768 = -0.003051.
d. Convert to signed Q(4,20) 0xFFF3B7.
I started with the following code to translate the software model to verilog, and the obtained results don't match what I expected. For Case 1 and Case 2, the observed value for Y_DIV
in simulation is 0x1
. But I expect a value that is close to Y (0x003f
for case 1 and 0xff9c
for case 2) as 1/32768
is relatively small.
With regards to converting 0x003f
and 0xff9c
to Q(4,20), if I sign extend 0x003f
, how do we achieve the target value of 0x0007C8
?
I am not sure if the following is the right way to translate my algorithm:
module test(
input clk,
input rst,
input [15:0] a,
input [15:0] b,
output reg signed [15:0] y,
output reg signed [15:0] y_div
);
always @(posedge clk) begin
if (rst) begin
y <= 0;
y_div <= 0;
end else begin
y <= (a - b);
// since (1/32768 = 0.000030) are we better off using a multiply instead of divide?
y_div <= (a - b) / b; // returns wrong value, i.e., doesn't match the expected value
// Q(4,20) is yet to be done. First need to get the above working
end
end
endmodule
Below is my test bench:
module tb_test();
reg CLK, RST;
reg [15:0] A, B;
wire [15:0] Y, Y_DIV;
initial begin
CLK = 0;
forever #1 CLK = ~CLK;
end
initial begin
RST = 1;
#10;
RST = 0;
end
initial begin
A = 16'b0;
B = 16'b0;
#10;
A = 16'h803f; // case 1
B = 16'h8000;
#10;
A = 16'h79fc; // case 2
B = 16'h8000;
end
test dut (.clk(CLK), .rst(RST), .a(A), .b(B), .y(Y), .y_div(Y_DIV));
endmodule
英文:
I am currently working on a design of an algorithm for signal processing.
I created a model in software that appears to work fine and I am now trying to translate it to verilog.
Below is what I do in the software.
I get a 16 bit input, I do the following
a. convert the hex to decimal
b. subtract 32768 from the above
c. divide the result with 32768
d. convert the result to a signed number in the Q(4,20) format (4 bits for integer, 20 bits fractional)
For example,
case 1: value is 0x803f (ie, value > 0x8000)
a. Convert 0x803f to decimal, ie, 32831
b. 32831 - 32768 = 63
c. 63/32768 = 0.001922
d. convert to signed Q(4,20) 0x0007C8
case 2: value is 0x79fc (ie, value < 0x8000)
a. Convert 0x79fc to decimal, ie, 31228
b. 32668 - 32768 = -100
c. -100/32768 = -0.003051
d. convert to signed Q(4,20) 0xFFF3B7
I started with the following code to translate the software model to verilog and obtained result don't match what I expected. For Case 1 and Case 2, observed value for Y_DIV
in simulation is 0x1
. But I expect value that is close to Y (Ox003f
for case 1 and 0xff9c
for case 2) as 1/32768
is relatively small.
With regards to converting 0x003f
and 0xff9c
to Q(4,20), if I sign extend 0x003f
, how do we achieve the target value of 0x0007C8
?
I am not sure if the following is the right way to translate my algorithm
module test(
input clk,
input rst,
input [15:0] a,
input [15:0] b,
output reg signed [15:0] y,
output reg signed [15:0] y_div
);
always @(posedge clk) begin
if (rst) begin
y <= 0;
y_div <= 0;
end else begin
y <= (a-b);
// since (1/32768 = 0.000030) are we better off using a multiply instead of divide ?
y_div <= (a-b)/b; // returns wrong value, ie doesn't match expected value
// Q(4,20) is yet to be done. First need to get the above working
end
end
endmodule
Below is my test bench
module tb_test();
reg CLK, RST;
reg [15:0] A, B;
wire [15:0] Y, Y_DIV;
initial begin
CLK = 0;
forever #1 CLK = ~CLK;
end
initial begin
RST = 1;
#10;
RST = 0;
end
initial begin
A = 16'b0;
B = 16'b0;
#10;
A = 16'h803f; // case 1
B = 16'h8000;
#10;
A = 16'h7f9c; // case 2
B = 16'h8000;
end
test dut (.clk(CLK), .rst(RST), .a(A), .b(B), .y(Y), .y_div(Y_DIV));
endmodule
答案1
得分: 1
Verilog的'/'操作符,在给定整数n/d时,执行整数除法,得到整数商并丢弃余数。
如果商小于1,Verilog显示0。
这对于对分数商感兴趣的设计不直接有用。
让我们避免'/'操作符,使用'>>>'(算术移位,从左边进行符号扩展)代替。
除以32768 = 2^15 相当于向右移动15位。
让我们将a - b项左移20位,以创建20个分数位的表示。
a - b需要为16位加法/减法的16位,需要17位。
将测试台与时钟沿边缘同步。
这个模块的输出接近您的向量。
(需要对浮点数进行量化,以获得与所有固定点的位精确匹配)
RTL(寄存器传输级):
module test
#(
parameter DI_IN_WIDTH = 16,
parameter FRACTIONAL_OUT_BITS = 20,
parameter OUT_BITS_4_DOT_20 = 24
)
(
input clk,
input rst,
input [DI_IN_WIDTH - 1:0] a,
input [DI_IN_WIDTH - 1:0] b,
//
output reg [OUT_BITS_4_DOT_20 - 1 :0] y
);
localparam SUM_WIDTH = DI_IN_WIDTH + 1;
localparam SUM_PLUS_FRACT_BITS = SUM_WIDTH + FRACTIONAL_OUT_BITS;
reg signed [SUM_WIDTH - 1:0] a_minus_b;
// 17.20 numbers
reg signed [SUM_PLUS_FRACT_BITS - 1:0] a_minus_b_w_fract_bits;
reg signed [SUM_PLUS_FRACT_BITS - 1:0] a_minus_b_w_fract_bits_div;
// a - b
always @ * begin
a_minus_b = a - b;
a_minus_b_w_fract_bits = a_minus_b << FRACTIONAL_OUT_BITS;
end
always @(posedge clk) begin
if (rst) begin
a_minus_b_w_fract_bits_div <= 'b0;
end else begin
// divide by 2^15
a_minus_b_w_fract_bits_div <= a_minus_b_w_fract_bits >>> 15;
end
end
assign y = a_minus_b_w_fract_bits_div[OUT_BITS_4_DOT_20 - 1 : 0];
endmodule
测试台:
module tb_test();
localparam DI_IN_WIDTH = 16;
localparam FRACTIONAL_OUT_BITS = 20;
localparam OUT_BITS_4_DOT_20 = 24;
reg CLK, RST;
reg signed [DI_IN_WIDTH - 1:0] A, B;
reg signed [OUT_BITS_4_DOT_20 - 1 :0] Y;
initial begin
CLK = 0;
forever #1 CLK = ~CLK;
end
initial begin
RST = 1;
repeat(2) @(posedge CLK);
RST = 0;
end
initial begin
$display("Test Starting");
A <= 16'h803f; // case 1 32831
B <= 16'h8000; // 32,768
repeat(3) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
A <= 16'h7f9c; // case 2
B <= 16'h8000;
repeat(1) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
A <= 16'h8000; // case 3, max value numerator
B <= 16'h0000;
repeat(1) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
repeat(1) @(posedge CLK);
$display("Test Done");
$finish;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
test dut (
.clk(CLK),
.rst(RST),
.a(A),
.b(B),
.y(Y)
);
endmodule
结果:
xcelium> run
Test Starting
t= 5, a - b = 63, Y_hex = 0007e0, Y_dec = 2016, Y_real_FMT_4_20 = 0.001923
t= 7, a - b = -100, Y_hex = fff380, Y_dec = -3200, Y_real_FMT_4_20 = -0.003052
t= 9, a - b = 32768, Y_hex = 100000, Y_dec = 1048576, Y_real_FMT_4_20 = 1.000000
Test Done
Simulation complete via $finish(1) at time 11 NS + 0
以上是您提供的Verilog代码的翻译部分。
英文:
The Verilog '/' operator, when given integers for n/d, performs integer division which yields the integer quotient and throws the remainder away.
If the quotient is < 1, Verilog shows 0.
This is not directly useful in designs that are interested in fractional quotients.
Lets avoid the '/' operator, use >>> (arithmetic shift, sign extends from the LHS) instead.
Divide by 32768 =2**15 is a shift of 15 bits to the right.
Lets shift the a - b term 20 bits to the left to create a 20 fractional bits representation.
a - b need to be 17 bits for the 16 bit add/sub.
Synchronized the testbench to the clock edge.
The output of this module gets close to your vectors.
(Would need to quantize floating point numbers to get a bit-exact match to all the fixed point)
RTL:
module test
#(
parameter DI_IN_WIDTH = 16,
parameter FRACTIONAL_OUT_BITS = 20,
parameter OUT_BITS_4_DOT_20 = 24
)
(
input clk,
input rst,
input [DI_IN_WIDTH - 1:0] a,
input [DI_IN_WIDTH - 1:0] b,
//
output reg [OUT_BITS_4_DOT_20 - 1 :0] y
);
localparam SUM_WIDTH = DI_IN_WIDTH + 1;
localparam SUM_PLUS_FRACT_BITS = SUM_WIDTH + FRACTIONAL_OUT_BITS;
reg signed [SUM_WIDTH - 1:0] a_minus_b;
// 17.20 numbers
reg signed [SUM_PLUS_FRACT_BITS - 1:0] a_minus_b_w_fract_bits;
reg signed [SUM_PLUS_FRACT_BITS - 1:0] a_minus_b_w_fract_bits_div;
// a - b
always @ * begin
a_minus_b = a - b;
a_minus_b_w_fract_bits = a_minus_b << FRACTIONAL_OUT_BITS;
end
always @(posedge clk) begin
if (rst) begin
a_minus_b_w_fract_bits_div <= '0;
end else begin
// divide by 2^15
a_minus_b_w_fract_bits_div <= a_minus_b_w_fract_bits >>> 15;
end
end
assign y = a_minus_b_w_fract_bits_div[OUT_BITS_4_DOT_20 - 1 : 0];
// initial begin
// $display("DI_IN_WIDTH = %0d",DI_IN_WIDTH);
// $display("SUM_WIDTH = %0d",SUM_WIDTH);
// $display("FRACTIONAL_OUT_BITS = %0d",FRACTIONAL_OUT_BITS);
// $display("SUM_PLUS_FRACT_BITS = %0d",SUM_PLUS_FRACT_BITS);
// $display("OUT_BITS_4_DOT_20 = %0d",OUT_BITS_4_DOT_20);
// end
endmodule
Testbench:
module tb_test();
localparam DI_IN_WIDTH = 16;
localparam FRACTIONAL_OUT_BITS = 20;
localparam OUT_BITS_4_DOT_20 = 24;
reg CLK, RST;
reg signed [DI_IN_WIDTH - 1:0] A, B;
reg signed [OUT_BITS_4_DOT_20 - 1 :0] Y;
initial begin
CLK = 0;
forever #1 CLK = ~CLK;
end
initial begin
RST = 1;
repeat(2) @(posedge CLK);
RST = 0;
end
initial begin
$display("Test Starting");
A <= 16'h803f; // case 1 32831
B <= 16'h8000; // 32,768
repeat(3) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
A <= 16'h7f9c; // case 2
B <= 16'h8000;
repeat(1) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
A <= 16'h8000; // case 3, max value numerator
B <= 16'h0000;
repeat(1) @(posedge CLK);
$strobe(" t= %0t, a - b = %0d, Y_hex = %h, Y_dec = %0d, Y_real_FMT_4_20 = %f",
$time,dut.a_minus_b,Y,Y,$itor(Y)/2**20);
//
repeat(1) @(posedge CLK);
$display("Test Done");
$finish;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
test dut (
.clk(CLK),
.rst(RST),
.a(A),
.b(B),
.y(Y)
);
endmodule
Result:
xcelium> run
Test Starting
t= 5, a - b = 63, Y_hex = 0007e0, Y_dec = 2016, Y_real_FMT_4_20 = 0.001923
t= 7, a - b = -100, Y_hex = fff380, Y_dec = -3200, Y_real_FMT_4_20 = -0.003052
t= 9, a - b = 32768, Y_hex = 100000, Y_dec = 1048576, Y_real_FMT_4_20 = 1.000000
Test Done
Simulation complete via $finish(1) at time 11 NS + 0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论