Shifting on signed arithmetic

I came across following problem while trying to understand how shift operators work with signed numbers in SV.


logic signed [3 : 0] a = 4'b0101 ; 
logic signed [5 : 0] b, c ;

assign b = a << 1 ; 
assign c = signed'(a << 1) ;

The above statements produce result b = 001010, which is correct as per my requirements.
But the second statement produces c = 111010, which I don’t understand because I expected the same result 001010.

I thought both statements should sign-extend the 4-bit vector a = 0101 to 6-bit operand, 000101 and then shift it by one place to left. Then a signed data type conversion happens which does nothing to the context.

I know there is no need for that signed’() conversion but I tried it because initially I thought shifting a signed number may result in unsigned context (like happening with part select). But still, even with signed’() it should produce the same result. But it didn’t. Can anyone tell me what’s happening here?

In reply to Mitu Raj:

So having a signed logic with only 4 bits means you can decode numbers from -7 up to 8. If you shift on left (multiply by 2) a signed number 0101 (5) you are going to overflow producing 10 unsigned but 1010 which is -6 since it will wrap around.

On top of that the << operator is a logical operator the arithmetic one which will take care of signed operands is <<<.

For the signed you a casting so the result 1010 after the shift will be considered as signed and so extended giving you the result 111010 which is correct (-6).

Your issue is with the dimensions, as stated in the LRM the <<< operator will fill with 0s staring from the bottom so your result will still be 1010 it cannot be further extended since there is no further space. Then it will be assigned to be (which is signed) but is 6 bits instead of 4 and 1010 is still in the interval [-2^(n-1)-1:2^(n-1)].

adding signed as casting then will consider the result as signed on 6 bits and so will extend it as consequence.


module tb;
  	logic signed [5 : 0] a = 6'b011101; 
	logic signed [5 : 0] b, c;
 	
  	// these 2 are identical 
  	assign b = (a <<< 1); 
	assign c = signed'(a << 1);
  	
  	initial begin
      #1;
      $display("a: %b",a);
      $display("b: %b",b);
      $display("c: %b",c);
    end
endmodule 

Looking at the above code adjusting the dimension will give proper result.

Regards

In reply to Rsignori92:

Actually << and <<< behaves the same on

assign b = a << 1 ; 

statement.

Both give me the behavior that I actually want on all numbers I tried. So I guess that’s not a problem to me. I cannot change the sizes of a and b because I want a to represent numbers from -16 to +15, and b to represent numbers from -64 to +63.

After a bit trial and error, this is what I concluded about the compiler behavior:

  1. The first statement does sign-extending to 6 bits first, and then shifting-
    eg: 0101 (+5) → 000101 → 001010 (+10)
    eg: 1010 (-6) → 111010 → 110100 (-12)

  2. The second statement does shifting first, and then sign extends to 6 bits because of signed’(), which results in loss of MSB-
    eg: 0101 (+5) → 1010 → 111010 (-6)
    eg: 1010 (-6) → 0100 → 000100 (+4)

In reply to Mitu Raj:

The Sign extension is not take care by the simple << operator you are getting the extension because the result is signed as well, and this extension happens after the shift.

As far as I remember the arithmetic shift will take care of in case of signed operands.


module tb;
  logic signed [3 : 0] a = 4'b1101; 
  logic signed [5 : 0] b, c;
 
  // these 2 are identical 
  assign b = (a << 1); 
  // This is not working properly since is always considering the value as negative 
  assign c = signed'(a << 1);
  
  initial begin
      #1;
      $display("a: %d or %b",a,a);
      $display("b: %d or %b",b,b);
      $display("c: %d or %b",c,c);
    end
endmodule 

Result:
// Case A = -3
a: -3 or 1101
b: -6 or 111010
c: -6 or 111010
// Case A = 5
a: 5 or 0101
b: 10 or 001010
c: -6 or 111010

  1. The first statement does sign-extending to 6 bits first, and then shifting-
    eg: 0101 (+5) → 000101 → 001010 (+10)
    eg: 1010 (-6) → 111010 → 110100 (-12)

I do not think so how do you know the final sign if you do not perform the operation first. I believe the extension happens after. Regards

In reply to Rsignori92:
I confirmed my results with simulation. The operand a is ‘signed’, so it’s automatically sign-extended to the size of largest operand, based on its MSB, before doing the shift. This is what I noticed.