Hazard3/hdl/arith/hazard3_alu.v

116 lines
3.7 KiB
Verilog

/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2018 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
module hazard3_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 "hazard3_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;
wire shift_right_nleft = aluop == ALUOP_SRL || aluop == ALUOP_SRA;
wire shift_arith = aluop == ALUOP_SRA;
hazard3_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
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 result = shift_dout; end
ALUOP_SRA: begin 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