2

I'm writing ALU for MIPS.

I used to write it as a sequential logic (or a always block? I don't know why but c must be a reg so I guess it's a sequential logic?)

It seems to be combinational logic

always @* begin
    case (op)
        4'b0000: c = mux_a + mux_b; 
        4'b0001: c = mux_a - mux_b;
        4'b0010: c = mux_b << mux_a[4:0];
        4'b0011: c = mux_b >> mux_a[4:0];
        4'b0100: c = $signed(mux_b) >>> mux_a[4:0]; 
        4'b0101: c = mux_a & mux_b;
        4'b0110: c = mux_a | mux_b;
        4'b0111: c = mux_a ^ mux_b;
        4'b1000: c = ~(mux_a | mux_b);    
        4'b1001: c = (mux_a < mux_b) ? 32'b1 : 32'b0; // sltu
        4'b1010: c = ($signed(mux_a) < $signed(mux_b)) ? 32'b1 : 32'b0; // slt
        4'b1011: c = mux_a + mux_b - 4;
        default: c = 0;
    endcase
end

And it works well, 4'b0100: c = $signed(mux_b) >>> mux_a[4:0] can get correct output. However due to some reasons I decided to use a combinational logic conditional expression.

assign c =  (op == 4'b0000) ? (mux_a + mux_b) : 
            (op == 4'b0001) ? (mux_a - mux_b) : 
            (op == 4'b0010) ? (mux_b << mux_a[4:0]) : 
            (op == 4'b0011) ? (mux_b >> mux_a[4:0]) : 
            (op == 4'b0100) ? ($signed(mux_b) >>> mux_a[4:0]) : 
            (op == 4'b0101) ? (mux_a & mux_b) : 
            (op == 4'b0110) ? (mux_a | mux_b) : 
            (op == 4'b0111) ? (mux_a ^ mux_b) : 
            (op == 4'b1000) ? (~(mux_a | mux_b)) : 
            (op == 4'b1001) ? ((mux_a < mux_b) ? 32'b1 : 32'b0) : 
            (op == 4'b1010) ? (($signed(mux_a) < $signed(mux_b)) ? 32'b1 : 32'b0) : 
            (op == 4'b1011) ? (mux_a + mux_b - 4) : 
            0;

Which is almost the same except c is a wire.

And I run this code and it tells me that, 4294967280 (FFFFFFF0) >>> 8 = 33554431 (1FFFFFF) which is ridiculous.

PC=        388, Fetched 00000000000100011001101000000011.
  Decoder done. alu=4294967280.
  ALUOP=0100 ALUMASK=10 Calc Result=  33554431
  rs=         0 rt=4294967280
  aluflag = 0000
  Written(0000) 01ffffff to RegAddr:19

But if I use (op == 4'b0100) ? ({ {31{mux_b[32]}}, mux_b} >> mux_a[4:0]) : instead, I can get correct result.

Can anyone tell me the reason? Following is how these variable defined (use 33 bits for overflow flags)

input [31:0] a;
input [31:0] b;
input [31:0] imm1;
input [31:0] imm0;
input [3:0] op;
input [1:0] mask;

output [31:0] result;
output [3:0] flags;

wire [31:0] _mux_a;
wire [31:0] _mux_b;

wire [32:0] mux_a;
wire [32:0] mux_b;
wire [32:0] c;

assign _mux_a = mask[1] ? imm1 : a;
assign _mux_b = mask[0] ? imm0 : b;
assign mux_a = {_mux_a[31], _mux_a};
assign mux_b = {_mux_b[31], _mux_b};
assign result = c[31:0];

//zf of uf
assign flags = {c[31], result == 0,  c[32:31] == 2'b01,  c[32:31] == 2'b10};
Yangff
  • 87
  • 7
  • FYI `c` is still considered combainational in the first always block. Sequential logic is triggered in a clock edge (ex `always @(posedge clk)`). `always @*` is combinational logic when the new value is determinate (not depended of its prior value), otherwise it is latching logic. `reg` does not mean flop. – Greg Jun 02 '17 at 16:49
  • That confused me.. What's the difference of `$signed(mux_b) >>> mux_a[4:0]` in a `always @*` or in a conditional operator if they are both combinational logic.. – Yangff Jun 03 '17 at 08:01

1 Answers1

1

It is a bug in iverilog. Something about combining $signed and the conditional operator ?:. It does not matter if you are using continuous assignment (assign statement) or an always block.

When mux_b[32] is one, $signed(mux_b) >>> mux_a[4:0] will give the correct output while (1'b1) ? ($signed(mux_b) >>> mux_a[4:0]) : 33'h0 will give the wrong output.

If you want, you can file a bug here and here

A work around is to separate the operations. Example:

assign op4 = $signed(mux_b) >>> mux_a[4:0];
assign c = (op == 4'b0000) ...
           (op == 4'b0100) ? op4 :
           (op == 4'b0101) ...

Or (op == 4'b0100) ? ({ {31{mux_b[32]}}, mux_b} >> mux_a[4:0]) : as already pointed out in the question.

FYI: From my experience the conditional operator ?: often synthesizes to an explicit 2:1 mux even when a 4:1 or other mux type is more ideal. Your experience may differ. In general, I recommend using case statement for any muxing greater that 3:1.

Greg
  • 18,111
  • 5
  • 46
  • 68
  • Greg, I tried the two implementations (using `case` and `assign` statement) on `VCS 2014.10` and to my surprise the two gave different outputs. The `case statement` one gave the correct result whereas the `assign statement` gave the incorrect result. Here are the two implementations - [case statement implementation](https://www.edaplayground.com/x/4VZU) and [assign statement](https://www.edaplayground.com/x/5a8F) – Rahul Behl Jun 09 '17 at 07:17
  • Please suggest if I misunderstood anything here or are there any errors in the implementations? As they look fine to me and as you suggested the continuous assignment and always block shouldn't matter. Not sure if I am missing something here. – Rahul Behl Jun 09 '17 at 07:21
  • @RahulBehl , This has nothing to do with `always` vs `assign`. Its with using a signed algorithmic inside a `?:`. If you use the `?:` approach inside the always block you should get the same incorrect result. I don't have access to VCS or Incisive on EDA playground. Riviera Pro also as the problem (I don't remember having it last time). I created a [targeted test](https://www.edaplayground.com/x/5FyA) to prove the inconsistencies (used macros to ensure inconsistencies were not from typos). If this is not a bug _across multiple simulators_ then it is an extremely non-intuitive feature. – Greg Jun 09 '17 at 17:26
  • Thank you for taking the effort to write the test. It makes sense to me know. – Rahul Behl Jun 15 '17 at 06:46