I wrote a Verilog code for fpadder, and when I simulate it with the ModelSim software, it generates a series of incorrect numbers with the provided test bench. So far, I have realized that the rounding part, the final power calculation part (which I marked with the name final_exponent) and a part of the fraction that I don't know where it is wrong. Could you please help me to debug it?
`timescale 1ns/1ns
// `default_nettype none
module fp_adder(
input [31:0] a,
input [31:0] b,
output [31:0] s
);
wire sign_a = a[31];
wire sign_b = b[31];
wire [7:0] exponent_A = a[30:23];
wire [7:0] exponent_B = b[30:23];
wire [22:0] fraction_A = a[22:0];
wire [22:0] fraction_B = b[22:0];
wire bitwise_or_EA = |exponent_A;
wire bitwise_or_EB = |exponent_B;
wire bitwise_or_FA = |fraction_A;
wire bitwise_or_FB = |fraction_B;
wire [8:0] difference;
// wire [8:0] shift;
wire [25:0] value_of_A;
wire [25:0] value_of_B;
wire [25:0] shifted_A;
wire [25:0] shifted_B;
wire [26:0] valueA_after_s;
wire [26:0] valueB_after_s;
wire [26:0] signed_A;
wire [26:0] signed_B;
wire [27:0] sum;
wire [0:0] new_sign;
wire [26:0] new_value;
wire [27:0] after_complement;
wire [26:0] new_value_shifted;
wire [26:0] fraction_after_shift;
wire [22:0] final_fraction;
wire [7:0] initial_exponent;
wire [23:0] s_fraction;
wire [23:0] fraction_ended;
wire [22:0] fraction_go_home;
wire [7:0] final_exponent;
wire [4:0] shift_for_norm;
wire Sticky;
wire [25:0] to_help_for_sticky;
wire [9:0] how;
wire [7:0] after_final_exponent;
assign difference = exponent_A > exponent_B ? { 1'b0, exponent_A} + ~{ 1'b0, exponent_B} +1: { 1'b0, exponent_B} + ~{ 1'b0, exponent_A} + 1; //differnce of power for shifting
assign value_of_A = bitwise_or_EA ? {1'b1, fraction_A, 2'b0} : ( bitwise_or_FA ? {1'b0, fraction_A, 2'b0} : { 23'b0, 2'b0} ); //to write hidden one or not; table1
assign value_of_B = bitwise_or_EB ? {1'b1, fraction_B, 2'b0} : ( bitwise_or_FB ? {1'b0, fraction_B, 2'b0} : { 23'b0, 2'b0} );
assign how = 9'b000011010 + ~difference +1;
assign shifted_A = exponent_A < exponent_B ? value_of_A >> difference : value_of_A; //to equalize the powers; if difference[8] = 1, A is smaller numb.
assign shifted_B = exponent_B < exponent_A ? value_of_B >> difference : value_of_B;
assign to_help_for_sticky = exponent_B < exponent_A ? value_of_B << how : value_of_A << how; //to find which bits should be save for sticky
assign Sticky = |to_help_for_sticky;
assign initial_exponent = exponent_B > exponent_A ? exponent_B : exponent_A;
assign valueA_after_s = a > b ? { 1'b0, shifted_A} : { 1'b0, shifted_A} + Sticky; //add Sticky to number; all remaining number which had been shifted
assign valueB_after_s = b > a ? { 1'b0, shifted_B} : { 1'b0, shifted_B} + Sticky;
assign signed_A = sign_a ? ~valueA_after_s +1 : valueA_after_s; //changing unsigned number to 2's complement
assign signed_B = sign_b ? ~valueB_after_s +1 : valueB_after_s;
assign sum = {signed_A[26], signed_A} + {signed_B[26], signed_B};
assign new_sign = a[30:0] > b[30:0] ? sign_a:
a[30:0] < b[30:0] ? sign_b :
a[31:0] == b[31:0] ? sign_a :
1'b0;
assign after_complement = new_sign ? ~sum +1 : sum;
assign new_value = after_complement[26:0]; //deleting sign bit
assign new_value_shifted = new_value << 1;
assign shift_for_norm = new_value_shifted[26] ? 0 : //how much shift for normalizing
new_value_shifted[25] ? 1 :
new_value_shifted[24] ? 2 :
new_value_shifted[23] ? 3 :
new_value_shifted[22] ? 4 :
new_value_shifted[21] ? 5 :
new_value_shifted[20] ? 6 :
new_value_shifted[19] ? 7 :
new_value_shifted[18] ? 8 :
new_value_shifted[17] ? 9 :
new_value_shifted[16] ? 10 :
new_value_shifted[15] ? 11 :
new_value_shifted[14] ? 12 :
new_value_shifted[13] ? 13 :
new_value_shifted[12] ? 14 :
new_value_shifted[11] ? 15 :
new_value_shifted[10] ? 16 :
new_value_shifted[9] ? 17 :
new_value_shifted[8] ? 18 :
new_value_shifted[7] ? 10 :
new_value_shifted[6] ? 20 :
new_value_shifted[5] ? 21 :
new_value_shifted[4] ? 22 :
new_value_shifted[3] ? 23 :
new_value_shifted[2] ? 24 :
new_value_shifted[1] ? 25 :
new_value_shifted[0] ? 26 : 0;
assign fraction_after_shift = new_value_shifted << shift_for_norm;
assign final_exponent = shift_for_norm == 5'b00000 ? initial_exponent :
initial_exponent > shift_for_norm ? initial_exponent + shift_for_norm :
initial_exponent;
assign final_fraction = fraction_after_shift[25:3]; //limitimng fraction to 23bit(maybe wrong)!
//rounding
assign s_fraction = !fraction_after_shift[2] ? {1'b0, final_fraction} :
|fraction_after_shift[1:0] ? {1'b0, final_fraction} +1 :
fraction_after_shift[3] ? {1'b0, final_fraction} :
{1'b0, final_fraction} +1;
assign fraction_ended = s_fraction[23] ? s_fraction >> 1 : s_fraction; //after rounding, fraction has a carry? shift to right...
assign after_final_exponent = s_fraction[23] ? final_exponent +1 : final_exponent;
assign fraction_go_home = fraction_ended[22:0];
assign s = { new_sign, after_final_exponent, fraction_go_home};
endmodule
Footnotes:
- we have to just use wire and assign in our codes.
- In the line from one to the last for the rounding part (s_fraction): it has to check according to the 754_IEEE standard to add one if it is odd, but when I do this, some samples will make mistakes, and if I don't, some other samples will also make mistakes.
- In line one to the end for the power part (final_exponent): I have to reduce the amount of shifts (shift_for_norm), but in this case, some samples will be made, and again if I don't, some others will be wrong.
- By fpAdder, I mean the fixed-point number adder according to the standard 754-IEEE.
this is test bentch:
`timescale 1ns/1ns
module fp_adder__tb2();
reg [31:0] s_true, a, b;
shortreal ar,br;
int errors, no_of_tests;
initial begin
errors = 0;
no_of_tests = 2000000;
for (int i = 0; i < no_of_tests; i++) begin
a = $random();
b = $random();
ar = $bitstoshortreal(a);
br = $bitstoshortreal(b);
s_true = $shortrealtobits(ar+ br);
#10;
if (uut.s != s_true && s_true[30:23] != 255 && a[30:23] != 255 && b[30:23] != 255) begin
$write("\tError: %8x + %8x, expected: %8x, but got: %8x\n", a, b, s_true, uut.s);
$display("No. of random tests applied: %0d", i);
errors++;
$stop;
end
if(i && i % 20000 == 0)
$display("No. of random tests applied: %0d", i);
end
$display("%d (%%%0d) errors are found.\n", errors, (errors*100.0 + no_of_tests/2)/(no_of_tests + 1));
$stop;
end
fp_adder uut( .a(a), .b(b), .s());
endmodule
there are some bugs like:
Error: 41a10583 + be75427c, expected: 419f1afe, but got: 419f1aff
Error: 0a6e9314 + 8919b412, expected: 0a482610, but got: 0a48260f
Error: 1444df28 + 9684e02d, expected: 967d7268, but got: 977d7267
Error: 549efda9 + d0ca8ca1, expected: 549e331c, but got: 549e331d
Error: 4a74bf94 + 49c65d93, expected: 4aabf72f, but got: 4aafdcbb
-
\$\begingroup\$ I'm still at a loss with the combination of fixed point and handling exponents. \$\endgroup\$greybeard– greybeard2025年03月28日 07:50:14 +00:00Commented Mar 28 at 7:50
-
\$\begingroup\$ Thank you very much for your answer, but in the modelsim simulation, you can also check the waves separately directly, but the main question is here, I don't understand where the problem with my algorithm comes from. \$\endgroup\$Zahra_Alishah– Zahra_Alishah2025年03月30日 21:10:32 +00:00Commented Mar 30 at 21:10
1 Answer 1
I am able to reproduce your first error result by modifying the testbench to simulate only this one set of inputs:
no_of_tests = 1;
for (int i = 0; i < no_of_tests; i++) begin
a = 32'h41a1_0583;
b = 32'hbe75_427c;
ar = $bitstoshortreal(a);
br = $bitstoshortreal(b);
s_true = $shortrealtobits(ar+ br);
#10;
if (uut.s != s_true && s_true[30:23] != 255 && a[30:23] != 255 && b[30:23] != 255) begin
$display($time, " Error: 'h%8x + 'h%8x, expected: 'h%8x, but got: 'h%8x", a, b, s_true, uut.s);
$display("No. of random tests applied: %0d", i);
errors++;
#20;
$finish;
end
if(i && i % 20000 == 0)
$display("No. of random tests applied: %0d", i);
end
This small change will make it easier to debug. I added the $time
to the $display
message because it is important to know the simulation time at which an error occurred. I also added a little delay (#20
) after the error before the simulation finishes.
It is insufficient to rely only on $display
messages when debugging Verilog code. It is much more efficient to dump waveforms for the entire hierarchy so that you can view all the internal signal values at once. You can add this to your testbench:
initial $dumpvars;
This will dump out a standard VCD file which you will be able to load into your simulation waveform viewer. Then you will be able to look at each internal signal to see where the values start to deviate from your expected behavior.
Also, try comparing your signals without the $bitstoshortreal
and $shortrealtobits
conversions.
Explore related questions
See similar questions with these tags.