116 lines
3.7 KiB
Verilog
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
|