2
\$\begingroup\$

I am implementing a fixed point multiplication circuit in SystemVerilog to multiply 2 64-bit numbers, each has 20 bits of decimal part (which remains 44 bits of integer part). The problem is the res signal is full of X, which causes the out also full of X.

module fix_point_mul #(parameter N = 64, parameter P = 20)(
 input logic signed [N-1:0] a, b,
 output logic signed [N-1:0] out
 );
 
 logic [2*N-1:0] res = ({{N{a[N-1]}},a} * {{N{b[N-1]}},b}) >> P;
 assign out = res[N-1:0];
 
endmodule

This is my testbench:

module fix_point_tb #(parameter N = 64, parameter P = 20);
 
 logic [N-1:0] a, b;
 logic [N-1:0] out_mul;
 logic carry;
 fix_point_mul mul_uut (.a(a), .b(b), .out(out_mul));
 
 initial begin
 a = 14.26 << 20;
 b = 52.732 << 20;
 
 end
endmodule

What are the problems? And what is the proper way of doing it?

toolic
10.8k11 gold badges31 silver badges35 bronze badges
asked Feb 29, 2024 at 6:53
\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

The post has this line

logic [2*N-1:0] res = ({{N{a[N-1]}},a} * {{N{b[N-1]}},b}) >> P

That will initialize a signal but not behave as a continuously assigned multiply . This may not work in some synthesis tools even to initialize. I recommend not having the expectation to to declare and initialize or to declare-initialize-drive on the same line in Verilog/SV.

Cleaning up the multiply, and realizing the total is 128 bits internally then shifting to retain as many fractional bits as you want. Shifting zero retains all fractional bits.

module fix_point_mul #(parameter N = 64, parameter P = 0)(
 input logic signed [N - 1 : 0] a, b,
 output logic signed [2*N - 1 : 0] c_out
 );
 
 // N bits * N bits is 2N bits
 logic signed [(2 * N) - 1 : 0] c;
 
 // Full precision multiply
 assign c = a * b ;
 // Shift and throw away some of the fractional bits because we don't 
 // care about full precision.
 assign c_out = c >> P;
 
endmodule

For the testbench, if you want to shift the real number 14.26 twenty bits to the left multiply the real by 2**20, then cast to an integer like this: int_val = int'(14.26 * (2 ** 20)). Its not possible to drive a bit-vector bus in verilog from a real variable. Using type'(value) to change value to the type is called static casting in SystemVerilog.

To retain only the integer bits to the left of the decimal, shift by 40 bits (20 fractional bits * 20 fractional bits), here I kept 39 (one decimal place).

module fix_point_tb #(parameter N = 64);
 
 logic signed [N - 1 : 0] a, b;
 logic signed [(2 * N) - 1 : 0] c;
 
 real c_scale_real;
 fix_point_mul
 // shift 39 bits to retain 1 fractional bit of fixed point number 
 #(.P(39))
 mul_uut (.a(a), .b(b), .c_out(c));
 
 // shifting by 2^1 to print the bit vector as a one decimal place real
 // See note at the bottom
 assign c_scale_real = real'(c)/2;
 
 initial begin
 a = 14.26 * 2**20;
 b = 52.732 * 2**20;
 #1;
 $display("c = %0h,c_scale float one decimal place= %0f",c,c_scale_real);
 $finish;
 end
endmodule

Produces

c = 5df,c_scale float one decimal place = 751.500000

A floating point calculator says 14.26*52.723 = 751.82998 so we are getting close. You can add more fractional bits to approach the float value. We are throwing away bits when casting the stimulus from real->int, so for many numbers the multiply will not perfectly agree with the real number even for full precision fixed point.

If you want to print anything in Verilog/SV with a decimal, you will need to change the bits to float. As an example if you have two bits 2'b11 Verilog will always print the bit vector as 3; to view it as one fractional bit (1.5) convert it to a float by dividing by the 2^number of fractional bits you want to represent, in this case: 2^1 = 2. If you have the two bit vector 2'b11 and want to consider it two fractional bits, divide the integer value by 2^2 to get the fixed point value. So we have 3/2^2 = 3/4 = .75

answered Feb 29, 2024 at 20:17
\$\endgroup\$
2
  • \$\begingroup\$ The problem is really about this: "having the expectation to declare and initialize or to declare-initialize-drive on the same line", I separated it and it worked. Thanks for all the information you provided. \$\endgroup\$ Commented Mar 1, 2024 at 4:01
  • \$\begingroup\$ Happy that something helped. \$\endgroup\$ Commented Mar 1, 2024 at 15:55
2
\$\begingroup\$

Your Verilog code has syntax errors. When I try to run it on multiple simulators, I get compile errors like this:

 a = 14.26 << 20;
 |
xmvlog: *E,NOSHRL : Illegal type real shift operand (left operand) [4.1.1][4.1.12(IEEE)].

This was run on the Cadence simulator on EDA Playground. If your simulator did not generate compile errors, you can try your code on the free simulators available on EDA Playground.

You can not perform a shift (<<) on a real (floating point) numeric value like 14.26. If you want to use the shift operator, you can only use integers, like 14. The following code compiles without errors:

initial begin
 a = 14 << 20;
 b = 52 << 20;
 #1; 
end

I added the #1 delay so that the simulator can advance beyond time 0. If you try to display signal values at time 0, you will often see X, which is the default value for logic types at time 0.

answered Feb 29, 2024 at 11:43
\$\endgroup\$
1
  • \$\begingroup\$ I tried this a=14<<18; b=52<<18;#1; in the initial block to create fixed point number with fractional part, but the result of out_mul is still all X. By the way, I am using Vivado. \$\endgroup\$ Commented Feb 29, 2024 at 13:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.