/********************************************************************** * 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