// Copyright © 2019-2023 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. `include "VX_define.vh" module VX_int_unit #( parameter CORE_ID = 0, parameter BLOCK_IDX = 0, parameter NUM_LANES = 1 ) ( input wire clk, input wire reset, // Inputs VX_execute_if.slave execute_if, // Outputs VX_commit_if.master commit_if, VX_branch_ctl_if.master branch_ctl_if ); `UNUSED_PARAM (CORE_ID) localparam LANE_BITS = `CLOG2(NUM_LANES); localparam LANE_WIDTH = `UP(LANE_BITS); localparam PID_BITS = `CLOG2(`NUM_THREADS / NUM_LANES); localparam PID_WIDTH = `UP(PID_BITS); localparam SHIFT_IMM_BITS = `CLOG2(`XLEN); `UNUSED_VAR (execute_if.data.rs3_data) wire [NUM_LANES-1:0][`XLEN-1:0] add_result; wire [NUM_LANES-1:0][`XLEN:0] sub_result; // +1 bit for branch compare wire [NUM_LANES-1:0][`XLEN-1:0] shr_result; reg [NUM_LANES-1:0][`XLEN-1:0] msc_result; wire [NUM_LANES-1:0][`XLEN-1:0] add_result_w; wire [NUM_LANES-1:0][`XLEN-1:0] sub_result_w; wire [NUM_LANES-1:0][`XLEN-1:0] shr_result_w; reg [NUM_LANES-1:0][`XLEN-1:0] msc_result_w; reg [NUM_LANES-1:0][`XLEN-1:0] alu_result; wire [NUM_LANES-1:0][`XLEN-1:0] alu_result_r; `ifdef XLEN_64 wire is_alu_w = `INST_ALU_IS_W(execute_if.data.op_mod); `else wire is_alu_w = 0; `endif `UNUSED_VAR (execute_if.data.op_mod) wire [`INST_ALU_BITS-1:0] alu_op = `INST_ALU_BITS'(execute_if.data.op_type); wire [`INST_BR_BITS-1:0] br_op = `INST_BR_BITS'(execute_if.data.op_type); wire is_br_op = `INST_ALU_IS_BR(execute_if.data.op_mod); wire is_sub_op = `INST_ALU_IS_SUB(alu_op); wire is_signed = `INST_ALU_SIGNED(alu_op); wire [1:0] op_class = is_br_op ? `INST_BR_CLASS(alu_op) : `INST_ALU_CLASS(alu_op); wire [NUM_LANES-1:0][`XLEN-1:0] alu_in1 = execute_if.data.rs1_data; wire [NUM_LANES-1:0][`XLEN-1:0] alu_in2 = execute_if.data.rs2_data; wire [NUM_LANES-1:0][`XLEN-1:0] alu_in1_PC = execute_if.data.use_PC ? {NUM_LANES{execute_if.data.PC}} : alu_in1; wire [NUM_LANES-1:0][`XLEN-1:0] alu_in2_imm = execute_if.data.use_imm ? {NUM_LANES{execute_if.data.imm}} : alu_in2; wire [NUM_LANES-1:0][`XLEN-1:0] alu_in2_br = (execute_if.data.use_imm && ~is_br_op) ? {NUM_LANES{execute_if.data.imm}} : alu_in2; for (genvar i = 0; i < NUM_LANES; ++i) begin assign add_result[i] = alu_in1_PC[i] + alu_in2_imm[i]; assign add_result_w[i] = `XLEN'($signed(alu_in1[i][31:0] + alu_in2_imm[i][31:0])); end for (genvar i = 0; i < NUM_LANES; ++i) begin wire [`XLEN:0] sub_in1 = {is_signed & alu_in1[i][`XLEN-1], alu_in1[i]}; wire [`XLEN:0] sub_in2 = {is_signed & alu_in2_br[i][`XLEN-1], alu_in2_br[i]}; assign sub_result[i] = sub_in1 - sub_in2; assign sub_result_w[i] = `XLEN'($signed(alu_in1[i][31:0] - alu_in2_imm[i][31:0])); end for (genvar i = 0; i < NUM_LANES; ++i) begin wire [`XLEN:0] shr_in1 = {is_signed && alu_in1[i][`XLEN-1], alu_in1[i]}; assign shr_result[i] = `XLEN'($signed(shr_in1) >>> alu_in2_imm[i][SHIFT_IMM_BITS-1:0]); wire [32:0] shr_in1_w = {is_signed && alu_in1[i][31], alu_in1[i][31:0]}; wire [31:0] shr_res_w = 32'($signed(shr_in1_w) >>> alu_in2_imm[i][4:0]); assign shr_result_w[i] = `XLEN'($signed(shr_res_w)); end for (genvar i = 0; i < NUM_LANES; ++i) begin always @(*) begin case (alu_op[1:0]) 2'b00: msc_result[i] = alu_in1[i] & alu_in2_imm[i]; // AND 2'b01: msc_result[i] = alu_in1[i] | alu_in2_imm[i]; // OR 2'b10: msc_result[i] = alu_in1[i] ^ alu_in2_imm[i]; // XOR 2'b11: msc_result[i] = alu_in1[i] << alu_in2_imm[i][SHIFT_IMM_BITS-1:0]; // SLL endcase end assign msc_result_w[i] = `XLEN'($signed(alu_in1[i][31:0] << alu_in2_imm[i][4:0])); end for (genvar i = 0; i < NUM_LANES; ++i) begin wire [`XLEN-1:0] slt_br_result = `XLEN'({is_br_op && ~(| sub_result[i][`XLEN-1:0]), sub_result[i][`XLEN]}); wire [`XLEN-1:0] sub_slt_br_result = (is_sub_op && ~is_br_op) ? sub_result[i][`XLEN-1:0] : slt_br_result; always @(*) begin case ({is_alu_w, op_class}) 3'b000: alu_result[i] = add_result[i]; // ADD, LUI, AUIPC 3'b001: alu_result[i] = sub_slt_br_result; // SUB, SLTU, SLTI, BR* 3'b010: alu_result[i] = shr_result[i]; // SRL, SRA, SRLI, SRAI 3'b011: alu_result[i] = msc_result[i]; // AND, OR, XOR, SLL, SLLI 3'b100: alu_result[i] = add_result_w[i]; // ADDIW, ADDW 3'b101: alu_result[i] = sub_result_w[i]; // SUBW 3'b110: alu_result[i] = shr_result_w[i]; // SRLW, SRAW, SRLIW, SRAIW 3'b111: alu_result[i] = msc_result_w[i]; // SLLW endcase end end // branch wire [`XLEN-1:0] PC_r, imm_r; wire [`INST_BR_BITS-1:0] br_op_r; wire [LANE_WIDTH-1:0] tid, tid_r; wire is_br_op_r; if (LANE_BITS != 0) begin assign tid = execute_if.data.tid[0 +: LANE_BITS]; end else begin assign tid = 0; end VX_elastic_buffer #( .DATAW (`UUID_WIDTH + `NW_WIDTH + NUM_LANES + `NR_BITS + 1 + PID_WIDTH + 1 + 1 + (NUM_LANES * `XLEN) + `XLEN + `XLEN + 1 + `INST_BR_BITS + LANE_WIDTH) ) rsp_buf ( .clk (clk), .reset (reset), .valid_in (execute_if.valid), .ready_in (execute_if.ready), .data_in ({execute_if.data.uuid, execute_if.data.wid, execute_if.data.tmask, execute_if.data.rd, execute_if.data.wb, execute_if.data.pid, execute_if.data.sop, execute_if.data.eop, alu_result, execute_if.data.PC, execute_if.data.imm, is_br_op, br_op, tid}), .data_out ({commit_if.data.uuid, commit_if.data.wid, commit_if.data.tmask, commit_if.data.rd, commit_if.data.wb, commit_if.data.pid, commit_if.data.sop, commit_if.data.eop, alu_result_r, PC_r, imm_r, is_br_op_r, br_op_r, tid_r}), .valid_out (commit_if.valid), .ready_out (commit_if.ready) ); `UNUSED_VAR (br_op_r) wire is_br_neg = `INST_BR_IS_NEG(br_op_r); wire is_br_less = `INST_BR_IS_LESS(br_op_r); wire is_br_static = `INST_BR_IS_STATIC(br_op_r); wire [`XLEN-1:0] br_result = alu_result_r[tid_r]; wire is_less = br_result[0]; wire is_equal = br_result[1]; wire br_enable = is_br_op_r && commit_if.valid && commit_if.ready && commit_if.data.eop; wire br_taken = ((is_br_less ? is_less : is_equal) ^ is_br_neg) | is_br_static; wire [`XLEN-1:0] br_dest = is_br_static ? br_result : (PC_r + imm_r); wire [`NW_WIDTH-1:0] br_wid; `ASSIGN_BLOCKED_WID (br_wid, commit_if.data.wid, BLOCK_IDX, `NUM_ALU_BLOCKS) VX_pipe_register #( .DATAW (1 + `NW_WIDTH + 1 + `XLEN) ) branch_reg ( .clk (clk), .reset (reset), .enable (1'b1), .data_in ({br_enable, br_wid, br_taken, br_dest}), .data_out ({branch_ctl_if.valid, branch_ctl_if.wid, branch_ctl_if.taken, branch_ctl_if.dest}) ); for (genvar i = 0; i < NUM_LANES; ++i) begin assign commit_if.data.data[i] = (is_br_op_r && is_br_static) ? (PC_r + 4) : alu_result_r[i]; end assign commit_if.data.PC = PC_r; `ifdef DBG_TRACE_CORE_PIPELINE always @(posedge clk) begin if (branch_ctl_if.valid) begin `TRACE(1, ("%d: core%0d-branch: wid=%0d, PC=0x%0h, taken=%b, dest=0x%0h (#%0d)\n", $time, CORE_ID, branch_ctl_if.wid, commit_if.data.PC, branch_ctl_if.taken, branch_ctl_if.dest, commit_if.data.uuid)); end end `endif endmodule