verilogsystem-verilogiverilog

Unexpected results in fixed-point conversion in Verilog


Why regA and regB have two different values, while regC and regD work as I would expect?

reg signed [31:0] regA;
reg signed [31:0] regB;
reg signed [15:0] regC;
reg signed [15:0] regD;
initial begin
    regA = 0.006 * 2**31;
    regB = 0.006 * (32'b1 << 31);
    regC = 0.006 * 2**15;
    regD = 0.006 * (16'b1 << 15);
    $display("regA = 0x%b, %d", regA, regA);
    $display("regB = 0x%b, %d", regB, regB);
    $display("regC = 0x%b, %d", regC, regC);
    $display("regD = 0x%b, %d", regD, regD);
end

Icarus Verilog returns this

regA = 0x11111111001110110110010001011010,   -12884902
regB = 0x00000000110001001001101110100110,    12884902
regC = 0x0000000011000101,    197
regD = 0x0000000011000101,    197

Solution

  • In the case of regA, you have a signed 32-bit data type that is overflowing and wrapping around to a negative value. The other cases are not overflowing.

    When you write a bare numeric literal like 2, you get a signed 32-bit value. With 32'b1 you get an unsigned 32-bit value. So regA/regC are signed 32-bit, regB/regD are unsigned 32-bit. And 2**31 is the point at which the signed 32-bit type will overflow, vs. 2**32 for unsigned 32-bit. So only the regA calculation is overflowing.

    Note that all of the integer calculations are happening before the conversion to floating point, so the multiply by 0.006 doesn't come into play here.