Skip to content

Instantly share code, notes, and snippets.

@Wren6991
Created August 23, 2020 15:18
Show Gist options
  • Save Wren6991/f08c14bbcb759f4a76fd81c9aee47879 to your computer and use it in GitHub Desktop.
Save Wren6991/f08c14bbcb759f4a76fd81c9aee47879 to your computer and use it in GitHub Desktop.
hazard5_alu CXXRTL repro
read_verilog hazard5_alu.v hazard5_shift_barrel.v
write_cxxrtl foo.cpp
module hazard5_alu #(
parameter W_DATA = 32
) (
input wire [3:0] aluop,
input wire [W_DATA-1:0] op_a,
input wire [W_DATA-1:0] op_b,
output reg [W_DATA-1:0] result,
output wire [W_DATA-1:0] result_add, // for load/stores
output wire cmp
);
`include "hazard5_ops.vh"
function msb;
input [W_DATA-1:0] x;
begin
msb = x[W_DATA-1];
end
endfunction
wire sub = aluop != ALUOP_ADD;
wire [W_DATA-1:0] sum = op_a + (op_b ^ {W_DATA{sub}}) + sub;
wire [W_DATA-1:0] op_xor = op_a ^ op_b;
wire lt = msb(op_a) == msb(op_b) ? msb(sum) :
aluop == ALUOP_LTU ? msb(op_b) :
msb(op_a) ;
assign cmp = aluop == ALUOP_SUB ? |op_xor : lt;
assign result_add = sum;
wire [W_DATA-1:0] shift_dout;
reg shift_right_nleft;
reg shift_arith;
hazard5_shift_barrel #(
.W_DATA(W_DATA),
.W_SHAMT(5)
) shifter (
.din(op_a),
.shamt(op_b[4:0]),
.right_nleft(shift_right_nleft),
.arith(shift_arith),
.dout(shift_dout)
);
// We can implement all bitwise ops with 1 LUT4/bit total, since each result bit
// uses only two operand bits. Much better than feeding each into main mux tree.
reg [W_DATA-1:0] bitwise;
always @ (*) begin: bitwise_ops
case (aluop[1:0])
ALUOP_AND[1:0]: bitwise = op_a & op_b;
ALUOP_OR[1:0]: bitwise = op_a | op_b;
default: bitwise = op_a ^ op_b;
endcase
end
always @ (*) begin
shift_right_nleft = 1'b0;
shift_arith = 1'b0;
case (aluop)
ALUOP_ADD: begin result = sum; end
ALUOP_SUB: begin result = sum; end
ALUOP_LT: begin result = {{W_DATA-1{1'b0}}, lt}; end
ALUOP_LTU: begin result = {{W_DATA-1{1'b0}}, lt}; end
ALUOP_SRL: begin shift_right_nleft = 1'b1; result = shift_dout; end
ALUOP_SRA: begin shift_right_nleft = 1'b1; shift_arith = 1'b1; result = shift_dout; end
ALUOP_SLL: begin result = shift_dout; end
default: begin result = bitwise; end
endcase
end
`ifdef FORMAL
`ifndef RISCV_FORMAL
// Really we're just interested in the shifts and comparisons, as these are
// the nontrivial ones. However, easier to test everything!
wire clk;
always @ (posedge clk) begin
case(aluop)
default: begin end
ALUOP_ADD: assert(result == op_a + op_b);
ALUOP_SUB: assert(result == op_a - op_b);
ALUOP_LT: assert(result == $signed(op_a) < $signed(op_b));
ALUOP_LTU: assert(result == op_a < op_b);
ALUOP_AND: assert(result == (op_a & op_b));
ALUOP_OR: assert(result == (op_a | op_b));
ALUOP_XOR: assert(result == (op_a ^ op_b));
ALUOP_SRL: assert(result == op_a >> op_b[4:0]);
ALUOP_SRA: assert($signed(result) == $signed(op_a) >>> $signed(op_b[4:0]));
ALUOP_SLL: assert(result == op_a << op_b[4:0]);
endcase
end
`endif
`endif
endmodule
localparam W_ALUOP = 4;
localparam W_ALUSRC = 2;
localparam W_MEMOP = 4;
localparam W_BCOND = 2;
// ALU operation selectors
localparam ALUOP_ADD = 4'h0;
localparam ALUOP_SUB = 4'h1;
localparam ALUOP_LT = 4'h2;
localparam ALUOP_LTU = 4'h4;
localparam ALUOP_AND = 4'h6;
localparam ALUOP_OR = 4'h7;
localparam ALUOP_XOR = 4'h8;
localparam ALUOP_SRL = 4'h9;
localparam ALUOP_SRA = 4'ha;
localparam ALUOP_SLL = 4'hb;
localparam ALUOP_MULDIV = 4'hc;
// Parameters to control ALU input muxes. Bypass mux paths are
// controlled by X, so D has no parameters to choose these.
localparam ALUSRCA_RS1 = 2'h0;
localparam ALUSRCA_PC = 2'h1;
localparam ALUSRCB_RS2 = 2'h0;
localparam ALUSRCB_IMM = 2'h1;
localparam MEMOP_LW = 4'h0;
localparam MEMOP_LH = 4'h1;
localparam MEMOP_LB = 4'h2;
localparam MEMOP_LHU = 4'h3;
localparam MEMOP_LBU = 4'h4;
localparam MEMOP_SW = 4'h5;
localparam MEMOP_SH = 4'h6;
localparam MEMOP_SB = 4'h7;
localparam MEMOP_NONE = 4'h8;
localparam BCOND_NEVER = 2'h0;
localparam BCOND_ALWAYS = 2'h1;
localparam BCOND_ZERO = 2'h2;
localparam BCOND_NZERO = 2'h3;
// CSR access types
localparam CSR_WTYPE_W = 2'h0;
localparam CSR_WTYPE_S = 2'h1;
localparam CSR_WTYPE_C = 2'h2;
// Exceptional condition signals which travel alongside (or instead of)
// instructions in the pipeline. These are speculative and can be flushed
// on e.g. branch mispredict
localparam W_EXCEPT = 3;
localparam EXCEPT_NONE = 3'h0;
localparam EXCEPT_ECALL = 3'h1;
localparam EXCEPT_EBREAK = 3'h2;
localparam EXCEPT_MRET = 3'h3; // separate, but handled similarly
localparam EXCEPT_INSTR_ILLEGAL = 3'h4;
localparam EXCEPT_INSTR_MISALIGN = 3'h5;
localparam EXCEPT_INSTR_FAULT = 3'h6;
// Operations for M extension (these are just instr[14:12])
localparam W_MULOP = 3;
localparam M_OP_MUL = 3'h0;
localparam M_OP_MULH = 3'h1;
localparam M_OP_MULHSU = 3'h2;
localparam M_OP_MULHU = 3'h3;
localparam M_OP_DIV = 3'h4;
localparam M_OP_DIVU = 3'h5;
localparam M_OP_REM = 3'h6;
localparam M_OP_REMU = 3'h7;
module hazard5_shift_barrel #(
parameter W_DATA = 32,
parameter W_SHAMT = 5
) (
input wire [W_DATA-1:0] din,
input wire [W_SHAMT-1:0] shamt,
input wire right_nleft,
input wire arith,
output reg [W_DATA-1:0] dout
);
integer i;
reg [W_DATA-1:0] din_rev;
reg [W_DATA-1:0] shift_accum;
wire sext = arith && din_rev[0]; // haha
always @ (*) begin
for (i = 0; i < W_DATA; i = i + 1)
din_rev[i] = right_nleft ? din[W_DATA - 1 - i] : din[i];
end
always @ (*) begin
shift_accum = din_rev;
for (i = 0; i < W_SHAMT; i = i + 1) begin
if (shamt[i]) begin
shift_accum = (shift_accum << (1 << i)) |
({W_DATA{sext}} & ~({W_DATA{1'b1}} << (1 << i)));
end
end
end
always @ (*) begin
for (i = 0; i < W_DATA; i = i + 1)
dout[i] = right_nleft ? shift_accum[W_DATA - 1 - i] : shift_accum[i];
end
`ifdef FORMAL
always @ (*) begin
if (right_nleft && arith) begin: asr
assert($signed(dout) == $signed(din) >>> $signed(shamt));
end else if (right_nleft && !arith) begin
assert(dout == din >> shamt);
end else if (!right_nleft && !arith) begin
assert(dout == din << shamt);
end
end
`endif
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment