ibex/rtl/ibex_alu.sv
Philipp Wagner 428d057c4a Rename ibex_[tracer_]define to ibex_[tracer_]pkg
This file doesn't contain defines any more, but a normal SV package.

The diff is best viewed without whitespace changes, as the reindents
cause a lof of diff noise.

Fixes lowrisc/ibex#173
2019-07-19 11:34:40 +01:00

244 lines
7.1 KiB
Systemverilog

// Copyright lowRISC contributors.
// Copyright 2018 ETH Zurich and University of Bologna.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
////////////////////////////////////////////////////////////////////////////////
// //
// Engineer: Markus Wegmann - markus.wegmann@technokrat.ch //
// //
// Additional contributions by: //
// Davide Schiavone - pschiavo@iis.ee.ethz.ch //
// //
// Design Name: ALU //
// Project Name: ibex //
// Language: SystemVerilog //
// //
// Description: Arithmetic logic unit of the pipelined processor. //
// //
////////////////////////////////////////////////////////////////////////////////
/**
* Arithmetic logic unit
*/
module ibex_alu (
input ibex_pkg::alu_op_e operator_i,
input logic [31:0] operand_a_i,
input logic [31:0] operand_b_i,
input logic [32:0] multdiv_operand_a_i,
input logic [32:0] multdiv_operand_b_i,
input logic multdiv_en_i,
output logic [31:0] adder_result_o,
output logic [33:0] adder_result_ext_o,
output logic [31:0] result_o,
output logic comparison_result_o,
output logic is_equal_result_o
);
import ibex_pkg::*;
logic [31:0] operand_a_rev;
logic [32:0] operand_b_neg;
// bit reverse operand_a for left shifts and bit counting
for (genvar k = 0; k < 32; k++) begin : gen_rev_operand_a
assign operand_a_rev[k] = operand_a_i[31-k];
end
///////////
// Adder //
///////////
logic adder_op_b_negate;
logic [32:0] adder_in_a, adder_in_b;
logic [31:0] adder_result;
always_comb begin
adder_op_b_negate = 1'b0;
unique case (operator_i)
// Adder OPs
ALU_SUB,
// Comparator OPs
ALU_EQ, ALU_NE,
ALU_GTU, ALU_GEU,
ALU_LTU, ALU_LEU,
ALU_GT, ALU_GE,
ALU_LT, ALU_LE,
ALU_SLT, ALU_SLTU,
ALU_SLET, ALU_SLETU: adder_op_b_negate = 1'b1;
default:;
endcase
end
// prepare operand a
assign adder_in_a = multdiv_en_i ? multdiv_operand_a_i : {operand_a_i,1'b1};
// prepare operand b
assign operand_b_neg = {operand_b_i,1'b0} ^ {33{adder_op_b_negate}};
assign adder_in_b = multdiv_en_i ? multdiv_operand_b_i : operand_b_neg ;
// actual adder
assign adder_result_ext_o = $unsigned(adder_in_a) + $unsigned(adder_in_b);
assign adder_result = adder_result_ext_o[32:1];
assign adder_result_o = adder_result;
///////////
// Shift //
///////////
logic shift_left; // should we shift left
logic shift_arithmetic;
logic [4:0] shift_amt; // amount of shift, to the right
logic [31:0] shift_op_a; // input of the shifter
logic [31:0] shift_result;
logic [31:0] shift_right_result;
logic [31:0] shift_left_result;
assign shift_amt = operand_b_i[4:0];
assign shift_left = (operator_i == ALU_SLL);
assign shift_arithmetic = (operator_i == ALU_SRA);
// choose the bit reversed or the normal input for shift operand a
assign shift_op_a = shift_left ? operand_a_rev : operand_a_i;
// right shifts, we let the synthesizer optimize this
logic [32:0] shift_op_a_32;
assign shift_op_a_32 = {shift_arithmetic & shift_op_a[31], shift_op_a};
// The MSB of shift_right_result_ext can safely be ignored. We just extend the input to always
// do arithmetic shifts.
logic signed [32:0] shift_right_result_signed;
logic [32:0] shift_right_result_ext;
assign shift_right_result_signed = $signed(shift_op_a_32) >>> shift_amt[4:0];
assign shift_right_result_ext = $unsigned(shift_right_result_signed);
assign shift_right_result = shift_right_result_ext[31:0];
// bit reverse the shift_right_result for left shifts
for (genvar j = 0; j < 32; j++) begin : gen_rev_shift_right_result
assign shift_left_result[j] = shift_right_result[31-j];
end
assign shift_result = shift_left ? shift_left_result : shift_right_result;
////////////////
// Comparison //
////////////////
logic is_equal;
logic is_greater_equal; // handles both signed and unsigned forms
logic cmp_signed;
always_comb begin
cmp_signed = 1'b0;
unique case (operator_i)
ALU_GT,
ALU_GE,
ALU_LT,
ALU_LE,
ALU_SLT,
ALU_SLET: begin
cmp_signed = 1'b1;
end
default:;
endcase
end
assign is_equal = (adder_result == 32'b0);
assign is_equal_result_o = is_equal;
// Is greater equal
always_comb begin
if ((operand_a_i[31] ^ operand_b_i[31]) == 1'b0) begin
is_greater_equal = (adder_result[31] == 1'b0);
end else begin
is_greater_equal = operand_a_i[31] ^ (cmp_signed);
end
end
// GTE unsigned:
// (a[31] == 1 && b[31] == 1) => adder_result[31] == 0
// (a[31] == 0 && b[31] == 0) => adder_result[31] == 0
// (a[31] == 1 && b[31] == 0) => 1
// (a[31] == 0 && b[31] == 1) => 0
// GTE signed:
// (a[31] == 1 && b[31] == 1) => adder_result[31] == 0
// (a[31] == 0 && b[31] == 0) => adder_result[31] == 0
// (a[31] == 1 && b[31] == 0) => 0
// (a[31] == 0 && b[31] == 1) => 1
// generate comparison result
logic cmp_result;
always_comb begin
cmp_result = is_equal;
unique case (operator_i)
ALU_EQ: cmp_result = is_equal;
ALU_NE: cmp_result = ~is_equal;
ALU_GT, ALU_GTU: cmp_result = is_greater_equal & ~is_equal;
ALU_GE, ALU_GEU: cmp_result = is_greater_equal;
ALU_LT, ALU_SLT,
ALU_LTU, ALU_SLTU: cmp_result = ~is_greater_equal;
ALU_SLET,
ALU_SLETU,
ALU_LE, ALU_LEU: cmp_result = ~is_greater_equal | is_equal;
default:;
endcase
end
assign comparison_result_o = cmp_result;
////////////////
// Result mux //
////////////////
always_comb begin
result_o = '0;
unique case (operator_i)
// Standard Operations
ALU_AND: result_o = operand_a_i & operand_b_i;
ALU_OR: result_o = operand_a_i | operand_b_i;
ALU_XOR: result_o = operand_a_i ^ operand_b_i;
// Adder Operations
ALU_ADD, ALU_SUB: result_o = adder_result;
// Shift Operations
ALU_SLL,
ALU_SRL, ALU_SRA: result_o = shift_result;
// Comparison Operations
ALU_EQ, ALU_NE,
ALU_GTU, ALU_GEU,
ALU_LTU, ALU_LEU,
ALU_GT, ALU_GE,
ALU_LT, ALU_LE,
ALU_SLT, ALU_SLTU,
ALU_SLET, ALU_SLETU: result_o = {31'h0,cmp_result};
default:;
endcase
end
endmodule