mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-20 11:57:12 -04:00
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
244 lines
7.1 KiB
Systemverilog
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
|