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?
2 Answers 2
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
-
\$\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\$Becker– Becker2024年03月01日 04:01:49 +00:00Commented Mar 1, 2024 at 4:01
-
\$\begingroup\$ Happy that something helped. \$\endgroup\$Mikef– Mikef2024年03月01日 15:55:53 +00:00Commented Mar 1, 2024 at 15:55
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.
-
\$\begingroup\$ I tried this
a=14<<18; b=52<<18;#1;
in theinitial
block to create fixed point number with fractional part, but the result ofout_mul
is still allX
. By the way, I am using Vivado. \$\endgroup\$Becker– Becker2024年02月29日 13:50:35 +00:00Commented Feb 29, 2024 at 13:50
Explore related questions
See similar questions with these tags.