3

I've read multiple questions on how to implement an arithmetic shift in Verilog. All suggest using $signed(…) >>> ….

This works when directly assigning the result to a wire or register, but not when using it as part of a larger expression. That is:

  • reg[2:0] a = $signed(3'b100) >>> 1 ; produces 3'b110
  • reg[2:0] a = 1'b1 ? $signed(3'b100) >>> 1 : 3'b000; produces 3'b010

The fix seems to be to wrap the computation into $unsigned:

  • reg[2:0] a = $unsigned($signed(3'b100) >>> 1) ; produces 3'b110
  • reg[2:0] a = 1'b1 ? $unsigned($signed(3'b100) >>> 1) : 3'b000; produces 3'b110

Why does inlining the arithmetic shift cause it to act like a logical one, and why does adding $unsigned around it fix the issue?

Here is a minimal working example:

module puzzle();
   reg [2:0] a_u = 3'b100 >>> 1; // 3'b010
   reg [2:0] a_s = $signed(3'b100) >>> 1; // 3'b110
   reg [2:0] a_mux = 1'b1 ? $signed(3'b100) >>> 1 : 3'b000; // 3'b010

   reg [2:0] b_u = $unsigned(3'b100 >>> 1); // 3'b010
   reg [2:0] b_s = $unsigned($signed(3'b100) >>> 1); // 3'b110
   reg [2:0] b_mux = 1'b1 ? $unsigned($signed(3'b100) >>> 1) : 3'b000; // 3'b110

   initial begin
      $display("a_u = 3'b%3b", a_u);
      $display("a_s = 3'b%3b", a_s);
      $display("a_mux = 3'b%3b", a_mux);

      $display("b_u = 3'b%3b", b_u);
      $display("b_s = 3'b%3b", b_s);
      $display("b_mux = 3'b%3b", b_mux);
   end
endmodule

Output:

a_u = 3'b010
a_s = 3'b110
a_mux = 3'b010
b_u = 3'b010
b_s = 3'b110
b_mux = 3'b110
Clément
  • 12,299
  • 15
  • 75
  • 115

2 Answers2

4

There are two Verilog expression rules at play here:

  • Context-determined versus self-determined expressions
  • In context, mixing signed and unsigned operand results in everything unsigned

When you have the expression

C ? A : B

The operands A and B are in context with each other. Each operand will be resized to largest operand, and both must be signed to remain signed. This happens before applying the operator during compilation. S is self-determined—nothing outside whatever C is has any effect on its size or signedness. Also, C has no effect on the context of A and B

Similarly when you have the expression

A >>> S

A is context-determined and S is self-determined

Now if we combine the two expressions into one:

C ? A >>> S : B

Since A and B are in the same context and B is unsigned, A gets converted to unsigned. As soon as you wrap A>>>S in a function call (any kind of function call works the same), each input argument to function has its own context independent of the rest of the expression it's part of. The return value will be treated in context with the rest of the expression.

dave_59
  • 39,096
  • 3
  • 24
  • 63
  • Brilliant, thanks! Is there a cleaner way to give an expression it's own context, rather than using `$unsigned`? – Clément Feb 22 '20 at 03:06
  • 1
    I wouldn't call it *clean*, but it's fewer characters using a concatenation. `1'b1 ? {3'sb100) >>> 1} : 3'b000`. All operands in a concatenation are self-determined. – dave_59 Feb 22 '20 at 07:30
1

I do not know the reason but it seems that converting 3'b000 into 3'sb000 also solves your problem.

reg [2:0] a_mux = 1'b1 ? $signed(3'b100) >>> 1 : 3'sb000;

Also, using $signed() instead of $unsigned() works as well:

reg [2:0] b_mux = 1'b1 ? $signed($signed(3'b100) >>> 1) : 3'b000;
acmert
  • 129
  • 1
  • 3