Bit Width Casting in SystemVerilog


I must have taught SystemVerilog more than 20 times. My customers this week are privileged: they are the first to be taught after I have thought of a use for SystemVerilog bit width casting:

in Verilog (and hence SystemVerilog), the simulator (and hence the synthesiser) must decide how many bits to use when calculating an arithmetic expression. This

    logic [7:0] A, B, F;
    F = (A * B) >> 8;

is an example of a so-called context sensitive expression. With a context sensitive expression, the simulator looks at the widths of the operands (to the right of the assignment operator) and the result (to its left) and finds the widest. It then uses this as the number of bits to use in the calculation. So, in the example above, the simulator will look at the widths of A, B and F, discover that they are all 8, and so will do 8-bit arithmetic. (So, it is highly likely that the result of (A * B) will be truncated, because the result of the multiplication will be truncated to 8 bits before the right shift. Which is clearly nonsense, because 16 bits are required to store the result of multiplying 8 bits by 8 bits.

So, how to fix that. One way is to use a bit width cast:

    F = (16'(A) * B) >> 8;

this forces 16-bit arithmetic to be used in the calculation, which is what is actually necessary. 

This is an example of Verilog's liberal, lax behaviour catching you out. Verilog's liberal, lax behaviour is all very well. It certainly is less frustrating writing Verilog than VHDL. But, if you do take advantage of this liberal, lax behaviour then you do need to be fully aware of the detail of how Verilog works, otherwise you are going to get caught out.

module M;
 
  logic [7:0] A, B, F;
 
  initial begin
    A = 16; B = 16;
    F = (A * B) >> 8;
    $display("F = (A * B) >> 8 gives F = %b (%d)", F, F);
    F = (16'(A) * B) >> 8;
    $display("F = (16'(A) * B) >> 8 gives F = %b (%d)", F, F);
  end
 
endmodule


gives

# KERNEL: F = (A * B) >> 8 gives F = 00000000 ( 0)
# KERNEL: F = (16'(A) * B) >> 8 gives F = 00000001 ( 1)


https://www.edaplayground.com/x/4Jfz


A synthesiser is also going to get caught out, because the job of a synthesiser is to design some hardware which behaves exactly the same way as the simulation. Therefore, the hardware is going to behave exactly the same way as the simulation and the same truncation will occur in the hardware as does in the simulation.

Here is the result of synthesising

    F = (A * B) >> 8;

where you can see that the output of the multiplier is 8 bits:


Here is the result of synthesising

    F = (16'(A) * B) >> 8;

where you can see that the output of the multiplier is 8 bits:



Comments

Popular posts from this blog

How to convert a std_logic_vector to a hex string?

Can you add/remove more than 1 item from a queue?