Floating point to ieee convertor in verilog - verilog

I'm trying create a module that will convert 3.817 floating number or any number to its binary representation in IEEE 754 single precision format and writing a test bench to display the output.
I wrote the following code:
module float_to_ieee754_single_precision (
input wire [31:0] float, // input floating point number
output wire [31:0] ieee754_single_precision // output binary representation in IEEE 754 single precision format
);
// Declare variables to store the sign, exponent, and mantissa of the input floating point number
wire signed [31:31] sign;
wire signed [30:23] exponent;
wire [22:0] mantissa;
// Split the input floating point number into its sign, exponent, and mantissa parts
assign sign = float[31];
assign exponent = float[30:23];
assign mantissa = {float[22:0], 23'b0};
// Convert the exponent from excess-127 to 2's complement representation
wire signed [7:0] exponent_2s_complement = exponent - 127;
// Shift the mantissa to the right by the exponent value, adding an implicit leading 1 to the mantissa
wire [22:0] mantissa_normalized = mantissa >> exponent_2s_complement;
// Concatenate the sign, exponent, and mantissa to obtain the binary representation in IEEE 754 single precision format
assign ieee754_single_precision = {sign, exponent_2s_complement, mantissa_normalized};
endmodule
`timescale 1ns / 1ps
module float_to_ieee754_single_precision_tb;
// Declare input and output wires
wire [31:0] float;
wire [31:0] ieee754_single_precision;
// Instantiate the module under test
float_to_ieee754_single_precision dut (
.float(float),
.ieee754_single_precision(ieee754_single_precision)
);
// Declare a test vector with a floating point number and its expected binary representation in IEEE 754 single precision format
localparam test_vector [][2] = {
{32'h3F800000, 32'h3F800000}, // 1.0
{32'h3F8CCCCD, 32'h3F8CCCCD}, // 1.1
{32'hC3F8CCCD, 32'hBF8CCCCD}, // -1.1
{32'h40A00000, 32'h40A00000}, // 10.0
{32'hC2D60000, 32'hBE280000} // -0.1
};
// Iterate through the test vector and check the module output against the expected value
integer i;
initial begin
for (i = 0; i < 5; i = i + 1) begin
float = test_vector[i][0];
#1;
if (ieee754_single_precision != test_vector[i][1]) begin
$display("Test case %d failed:", i);
$display(" Expected output: %h", test_vector[i][1]);
$display(" Actual output: %h", ieee754_single_precision);
end
end
end
endmodule
I'm stuck on the same error and I don't know what to do.
The error are:
Error (10170): Verilog HDL syntax error at float_to_ieee754_single_precision.v(42) near text "]"; expecting an operand.
Error (10137): Verilog HDL Procedural Assignment error at floating_point_to_ieee754.v(53): object "float" on left-hand side of assignment must have a variable data type
Error (10560): Verilog HDL Expression error at floating_point_to_ieee754.v(53): indexed name specifies too many indices for array "test_vector"
Error (10560): Verilog HDL Expression error at floating_point_to_ieee754.v(55): indexed name specifies too many indices for array "test_vector"
Error (10560): Verilog HDL Expression error at floating_point_to_ieee754.v(57): indexed name specifies too many indices for array "test_vector"
Error: Quartus II 64-Bit Analysis & Synthesis was unsuccessful. 4 errors, 1 warning
Error: Peak virtual memory: 4655 megabytes
Error: Processing ended: Wed Dec 28 23:05:10 2022
Error: Elapsed time: 00:00:00
Error: Total CPU time (on all processors): 00:00:00
create a module that will convert 3.817 floating number or any number to its binary representation in IEEE 754 single precision format and writing a test bench to display the output and fix the errors!

The 1st error points to this line:
localparam test_vector [][2] = {
Change it to:
localparam bit [31:0] test_vector [5][2] = '{
Refer to IEEE Std 1800-2017, section 10.9.1 Array assignment patterns.
The 2nd error means that you need to declare float as a reg, not a wire since you assign to it in a procedural block (initial).
This code compiles without errors on 2 simulators:
module float_to_ieee754_single_precision_tb;
// Declare input and output wires
reg [31:0] float;
wire [31:0] ieee754_single_precision;
// Instantiate the module under test
float_to_ieee754_single_precision dut (
.float(float),
.ieee754_single_precision(ieee754_single_precision)
);
// Declare a test vector with a floating point number and its expected binary representation in IEEE 754 single precision format
localparam bit [31:0] test_vector [5][2] = '{
{32'h3F800000, 32'h3F800000}, // 1.0
{32'h3F8CCCCD, 32'h3F8CCCCD}, // 1.1
{32'hC3F8CCCD, 32'hBF8CCCCD}, // -1.1
{32'h40A00000, 32'h40A00000}, // 10.0
{32'hC2D60000, 32'hBE280000} // -0.1
};
// Iterate through the test vector and check the module output against the expected value
integer i;
initial begin
for (i = 0; i < 5; i = i + 1) begin
float = test_vector[i][0];
#1;
if (ieee754_single_precision != test_vector[i][1]) begin
$display("Test case %d failed:", i);
$display(" Expected output: %h", test_vector[i][1]);
$display(" Actual output: %h", ieee754_single_precision);
end
end
end
endmodule

Related

Signed Number Multiplication using Karatsuba Algorithm in Verilog

Tried implementing Karatsuba multiplier for multiplying two binary numbers, the logic below works well for unsigned numbers, but getting incorrect answer when I change one of the inputs to a negative. In the example below a=1010011111000000(equals -88.25) and b= 0001010001000000(equals 20.25). The answer should be 11111001000001001111000000000000(equals:-1787.0625)but I end up getting the incorect answer. Have used fixed point logic, with inputs 16 bits and fraction point 8 bits, output being 32 bits with fraction bits 16.
module karatsuba( input signed [15:0] a,
input signed [15:0] b,
output signed [31:0] out
);
reg [15:0] ac,bd;
reg [31:0] t1;
reg [31:0]t2;
reg [24:0] psum;
initial begin
assign ac = a[15:8]*b[15:8];
assign bd = a[7:0]*b[7:0];
assign t2= bd;
assign t1={ac,16'b0000000000000000};
assign psum = {(a[15:8]+a[7:0])*(b[15:8]+b[7:0])-ac-bd,8'b00000000};
end
assign out= t1+psum+t2;
endmodule
module karatsuba_tb();
reg signed [15:0]a,b;
wire signed [31:0]out;
karatsuba uut(.a(a),.b(b),.out(out));
initial begin
a=16'b0101100001000000;
b=16'b0001010001000000;
end
endmodule
enter image description here
enter image description here
There are two issues pertaining to the signed multiply in the post:
Slices of vector variables (even slices of signed vectors) are treated as unsigned in Verilog. This is because when a slice is taken there is no way to identify the original sign bit, therefore it must be treated as unsigned
The solution is to cast the slices to signed so that Verilog treats them as signed. Like this:
assign ac = signed'(a[3:2]) * signed'(b[3:2]);
Make the line that defines the variable ac,bd to be treated as signed using the signed keyword (default is unsigned).
You will need to propagate these changes to other places in the posted code which have the same issues.
Here is a simplified version of the post using small numbers to illustrate the cast and keyword use:
module karatsuba(
input signed [3:0] a,
input signed [3:0] b
);
reg signed [3:0] ac;
assign ac = signed'(a[3:2]) * signed'(b[3:2]);
endmodule
module karatsuba_tb();
reg signed [3:0]a,b;
karatsuba uut(.a(a),.b(b));
initial begin
a = 4'b1110;
b = 4'b1111;
#1;
//
$display("---------------");
$display("uut.a[3:2] = %b",uut.a[3:2]);
$display("uut.b[3:2] = %b",uut.b[3:2]);
$display("uut.ac = %b",uut.ac);
$display("---------------");
//
$display("uut.a[3:2] = %d",signed'(uut.a[3:2]));
$display("uut.b[3:2] = %d",signed'(uut.b[3:2]));
$display("uut.ac = %d",uut.ac);
$display("---------------");
end
endmodule
The example displays this message at runtime:
---------------
uut.a[3:2] = 11
uut.b[3:2] = 11
uut.ac = 0001
---------------
uut.a[3:2] = -1
uut.b[3:2] = -1
uut.ac = 1
---------------

Verilog Design of a 32-bit ALU

Can you help me guys do a 32-bit ALU and explain me some things?
Wanna do:
0 bitwise AND: out = inA & inB.
1 bitwise OR: out = inA | inB.
2 addition: out = inA + inB.
6 subtraction: out = inA – inB //2's complement
7 Set On Less Than: out = ((inA < inB)?1:0)
12 NOR: out = ~( inA | inB)
Done this so far:
module ALU #(parameter N=32)(ALUOut, Zero, ALUinA, ALUinB, ALUop);
output [N-1:0] ALUOut;
reg [N-1:0] ALUOut;
output Zero;
reg Zero;
input [3:0] ALUop;
input [N-1:0] ALUinA, ALUinB;
always #(ALUinA or ALUinB or ALUop)
begin
case (ALUop)
4'b0000: ALUOut = ALUinA & ALUinB ; // 0:AND
Your code is good. Just some modifications required. ALUOut must be [N:0], since you'll require a carry bit in case of addition. Also, borrow bit must be required in case of subtraction.
Referring to SystemVerilog LRM 1800-2012 Section 11.6 Expression bit lengths,
SystemVerilog uses the bit length of the operands to determine how many bits to use while evaluating an
expression.
So, ALUOut[N-1:0] = ALUinA[N-1:0] + ALUinB[N-1:0]; will strictly evaluate an expression of N, while ALUOut = ALUinA + ALUinB; will
evaluate depending on size of ALUOut. Here, you can not see the difference, since all youe operands are N bits wide, but when ALUOut is increased to N+1 bits(including carry), then it can create a difference.
For example,
module top();
bit [3:0] a,b;
logic [3:0] sum;
bit carry;
assign sum[3:0] = a[3:0] + b[3:0];
// assign {carry,sum}= a + b;
initial
$monitor("a = %0d b = %0d carry = %0d sum = %0d",a,b,carry,sum);
initial
begin
a = 5; b = 1;
#5 ; a = 15; b = 1;
end
endmodule
shall execute to a = 15 b = 1 carry = 0 sum = 0 while, using the commented assign statement executes to a = 15 b = 1 carry = 1 sum = 0
Refer to LRM 1800-2012, Section 11.6 for further information.
Also, this and this links regarding ALU design can be useful.
In 2's complement -B is ~B+1 (~ is bit invert). Therefor A - B == A + (-B) == A + ~B + 1. But your doing RTL, so you don't need to write the 2's complement for subtraction as it is default. A - B and A + ~B + 1 will synthesize the same.
A[N-1:0] + B[N-1:0] is always an unsigned operation. A + B can be a signed operation if A and B are declared as input signed [N-1:0] A, B, otherwise it is an unsigned operation.
Other notes:
There is an issue with your header. Many simulators, synthesizers, and other Verilog tools will accept what you have, but it is not complaint with the IEEE standard. There are two header styles, ANSI and non-ANSI. I recommend ANSI unless required to follow the IEEE1364-1995 version of the standard.
ANSI style (IEEE Std 1364-2001 and above):
module ALU #(parameter N=32)(
output reg [N-1:0] ALUOut,
output reg Zero,
input [N-1:0] ALUinA, ALUinB,
input [3:0] ALUop );
Non-ANSI style (IEEE Std 1364-1995 and above):
module ALU (ALUOut, Zero, ALUinA, ALUinB, ALUop);
parameter N=32;
output [N-1:0] ALUOut;
output Zero;
input [3:0] ALUop;
input [N-1:0] ALUinA, ALUinB;
reg [N-1:0] ALUOut;
reg Zero;
always #(ALUinA or ALUinB or ALUop) is syntax legal. However since IEEE1364-2001 combinational logic is recommenced to be written as always #* or always #(*) (#* and #(*) are synonymous, user preference). With SystemVerilog (IEEE1800), the successor of Verilog (IEEE1364), always_comb is recommend over always #* for combinational logic, and always_latch for level-sensitive latching logic.

How to convert Signed Binary to Integer in Verilog?

I am trying to convert signed binary numbers to integer in verilog for synthesis to display, I have a couple of questions. Below is my code,
.....
if(acc[i][j]>10) //acc is a 2d register
begin
m_reg <= j-const_10; // const_10 is 16'b0000000000001010
m_int <= m_reg;
$display("Current value of M(bits)=%b",m_reg);
$display("Current value of M(int)=%d",m_int);
end
else
....
j can be less than 10, meaning m_reg can be negative. In that case, I am assuming m_reg will give me a signed binary negative number.
If it does, how do I convert it to an integer to display because I guess m_int = m_reg will give me only unsigned.
All data is 'binary' when displaying we have the choice of visualising in binary, decimal of hexadecimal. When inputing data we have the same choice but what is set and stored remains the same.
These are all the same:
a = 4'b1111;
a = 4'd15;
a = 4'hf;
To display in the given format:
$display("Binary %b", a);
$display("Decimal %d", a);
$display("Hex %h", a);
Leading 0's are not displayed, at least for decimal so min widths can be used.
$display("min of 2 Decimal %2d", a);
Dealing with signed numbers: declare the reg, logic or wire as signed, or convert when displaying.
reg [3:0] a;
reg signed [3:0] a_s;
initial begin
a = 4'b1111; // is this 15 or -1 depends on if you read as signed
a_s = 4'b1111; // -1
#1ns;
$display("Decimal converted to signed %d", $signed(a));
$display("Signed Decimal %d", a_s);
end

display a real in verilog but bitstoreal returning only 0.000000

I am trying to display a real number during the simulation of my verilog code in modelsim. But I only get 0 as output. I am trying to use the bitstoreal system function. I'm not so good at verilog so it could be a stupid beginner's mistake.
Following is my code:
reg [31:0] y[1:0];
integer file;
localparam [31:0] test = 32'h3fb0d05d;
task read_data_from_fifo();
begin
file = $fopen("/tmp/data.fifo", "r");
$fread(y, file);
$display("y0 = %d, %f, %h", $bitstoreal(y[0]), $bitstoreal(test), $bitstoreal(y[0]));
$display("y1 = %f, %f, %h", y[1], $bitstoreal(32'h5dd0_b03f), y[1]);
end
endtask
(the task is called from an initial begin block)
Outputs:
# y0 = 0, 0.000000, 00000000
# y1 = 3742779199.000000, 0.000000, df16473f
All help appreciated.
Update
Looks like bitstoreal only supports double precision floats (64-bit). Because
localparam [63:0] test = 64'h_3FF61A0BE5109071;
$display("%f", $bitstoreal(test));
results in
1.381359
use $bitstoshortreal:
...
$shortrealtobits converts values from a shortreal type to the 32-bit vector representation of the real
number.
$bitstoshortreal converts a bit pattern created by $shortrealtobits to a value of the shortreal
type.
...
$bitstoreal takes a 64bit input double precision float.
Solution: Do a bit conversion from single to double precision float. Like this:
reg [31:0] z; // single precision float
reg [63:0] double; // double precision float
double = {z[31], z[30], {3{~z[30]}}, z[29:23], z[22:0], {29{1'b0}}};
$display("%f", $bitstoreal(double));
Disclaimer: I'm not sure if this single to double conversion is safe/correct.
Update
This is to be read in the IEEE document mentioned by toolic:
$realtobits converts values from a real type to a 64-bit vector representation of the real number.
$bitstoreal converts a bit pattern created by $realtobits to a value of the real type.

Converting a wire value in Verilog for further processing

I'm new to Verilog.
I have written code to convert a wire value to an integer:
wire [31:0] w1;
integer k;
always # (w1) k = w1;
Source: converting a wire value to an integer in verilog
Now, for the next part I get an ERROR!
wire [63:0] w2; // Suppose it contains some value
wire [63:0] w3;
assign w3[k-1:0] = w2[k-1:0]; // ERROR in this line
ERROR : k is not a constant.
How do I solve this issue?
Verilog requires that part selects (code like [msb:lsb] to select part of a vector) be constant. To access a variable-sized group of bits requires something more complicated. Here is one way to do it:
wire [63:0] src;
wire [6:0] k;
wire [127:0] mask = { { 64 { 1'b0 } }, { 64 { 1'b1 } } } << k;
wire [63:0] dest;
assign dest = mask[127:64] & src;
The technique here is to construct a vector of 64 zeros followed by 64 ones, shift that vector by a variable amount, and then use a portion of the vector as a qualifying mask to control which bits are transferred from src to dest.
A related concept which does not help in your example but which is worth being aware of: Verilog-2001 introduced the "indexed part-select". An indexed part select specifies a base index and a width. The width is required to be constant but the base index does not need to be constant. The syntax for an indexed part select is vec[base+:width] or vec[base-:width].
The part select operators in Verilog 2001 could be useful for what you want to achieve.
Basically verilog allows for the starting index to be variable but needs the width of the assignment to be constant. The "+:" operator indicates counting upwards from the index value and vice-versa for "-:".
You can do something like,
assign w3[k-1 -: 8 ] = w2[k-1 -: 8]; // Where 8 bits is copied downwards
Search for "+:" in the below document.
http://www.sutherland-hdl.com/papers/2001-Wescon-tutorial_using_Verilog-2001_part1.pdf
Word of caution, generally variable part selects is considered as bad verilog.

Resources