// SPDX-License-Identifier: Apache-2.0 // Copyright 2019 Western Digital Corporation or its affiliates. // // 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. module exu_alu_ctl import swerv_types::*; ( input logic clk, // Top level clock input logic active_clk, // Level 1 free clock input logic rst_l, // Reset input logic scan_mode, // Scan control input predict_pkt_t predict_p, // Predicted branch structure input logic freeze, // Clock enable for valid input logic [31:0] a, // A operand input logic [31:0] b, // B operand input logic [31:1] pc, // for pc=pc+2,4 calculations input logic valid, // Valid input logic flush, // Flush pipeline input logic [12:1] brimm, // Branch offset input alu_pkt_t ap, // {valid,predecodes} input logic enable, // Clock enable output logic [31:0] out, // final result output logic flush_upper, // Branch flush output logic [31:1] flush_path, // Branch flush PC output logic [31:1] pc_ff, // flopped PC output logic pred_correct, // NPC control output predict_pkt_t predict_p_ff // Predicted branch structure ); logic [31:0] aout,bm; logic cout,ov,neg; logic [3:1] logic_sel; logic [31:0] lout; logic [31:0] sout; logic sel_logic,sel_shift,sel_adder; logic slt_one; logic actual_taken; logic signed [31:0] a_ff; logic [31:0] b_ff; logic [12:1] brimm_ff; logic [31:1] pcout; logic valid_ff; logic [31:0] ashift; logic cond_mispredict; logic target_mispredict; logic eq, ne, lt, ge; rvdffs #(1) validff (.*, .clk(active_clk), .en(~freeze), .din(valid & ~flush), .dout(valid_ff)); rvdffe #(32) aff (.*, .en(enable & valid), .din(a[31:0]), .dout(a_ff[31:0])); rvdffe #(32) bff (.*, .en(enable & valid), .din(b[31:0]), .dout(b_ff[31:0])); // any PC is run through here - doesn't have to be alu rvdffe #(31) pcff (.*, .en(enable), .din(pc[31:1]), .dout(pc_ff[31:1])); rvdffe #(12) brimmff (.*, .en(enable), .din(brimm[12:1]), .dout(brimm_ff[12:1])); predict_pkt_t pp_ff; rvdffe #($bits(predict_pkt_t)) predictpacketff (.*, .en(enable), .din(predict_p), .dout(pp_ff) ); // immediates are just muxed into rs2 // add => add=1; // sub => add=1; sub=1; // and => lctl=3 // or => lctl=2 // xor => lctl=1 // sll => sctl=3 // srl => sctl=2 // sra => sctl=1 // slt => slt // lui => lctl=2; or x0, imm20 previously << 12 // auipc => add; add pc, imm20 previously << 12 // beq => bctl=4; add; add x0, pc, sext(offset[12:1]) // bne => bctl=3; add; add x0, pc, sext(offset[12:1]) // blt => bctl=2; add; add x0, pc, sext(offset[12:1]) // bge => bctl=1; add; add x0, pc, sext(offset[12:1]) // jal => rs1=pc {pc[31:1],1'b0}, rs2=sext(offset20:1]); rd=pc+[2,4] // jalr => rs1=rs1, rs2=sext(offset20:1]); rd=pc+[2,4] assign bm[31:0] = ( ap.sub ) ? ~b_ff[31:0] : b_ff[31:0]; assign {cout, aout[31:0]} = {1'b0, a_ff[31:0]} + {1'b0, bm[31:0]} + {32'b0, ap.sub}; assign ov = (~a_ff[31] & ~bm[31] & aout[31]) | ( a_ff[31] & bm[31] & ~aout[31] ); assign neg = aout[31]; assign eq = a_ff[31:0] == b_ff[31:0]; assign ne = ~eq; assign logic_sel[3] = ap.land | ap.lor; assign logic_sel[2] = ap.lor | ap.lxor; assign logic_sel[1] = ap.lor | ap.lxor; assign lout[31:0] = ( a_ff[31:0] & b_ff[31:0] & {32{logic_sel[3]}} ) | ( a_ff[31:0] & ~b_ff[31:0] & {32{logic_sel[2]}} ) | ( ~a_ff[31:0] & b_ff[31:0] & {32{logic_sel[1]}} ); assign ashift[31:0] = a_ff >>> b_ff[4:0]; assign sout[31:0] = ( {32{ap.sll}} & (a_ff[31:0] << b_ff[4:0]) ) | ( {32{ap.srl}} & (a_ff[31:0] >> b_ff[4:0]) ) | ( {32{ap.sra}} & ashift[31:0] ); assign sel_logic = |{ap.land,ap.lor,ap.lxor}; assign sel_shift = |{ap.sll,ap.srl,ap.sra}; assign sel_adder = (ap.add | ap.sub) & ~ap.slt; assign lt = (~ap.unsign & (neg ^ ov)) | ( ap.unsign & ~cout); assign ge = ~lt; assign slt_one = (ap.slt & lt); assign out[31:0] = ({32{sel_logic}} & lout[31:0]) | ({32{sel_shift}} & sout[31:0]) | ({32{sel_adder}} & aout[31:0]) | ({32{ap.jal | pp_ff.pcall | pp_ff.pja | pp_ff.pret}} & {pcout[31:1],1'b0}) | ({32{ap.csr_write}} & ((ap.csr_imm) ? b_ff[31:0] : a_ff[31:0])) | // csr_write: if csr_imm rs2 else rs1 ({31'b0, slt_one}); // branch handling logic any_jal; assign any_jal = ap.jal | pp_ff.pcall | pp_ff.pja | pp_ff.pret; assign actual_taken = (ap.beq & eq) | (ap.bne & ne) | (ap.blt & lt) | (ap.bge & ge) | (any_jal); // for a conditional br pcout[] will be the opposite of the branch prediction // for jal or pcall, it will be the link address pc+2 or pc+4 rvbradder ibradder ( .pc(pc_ff[31:1]), .offset(brimm_ff[12:1]), .dout(pcout[31:1]) ); // pred_correct is for the npc logic // pred_correct indicates not to use the flush_path // for any_jal pred_correct==0 assign pred_correct = ((ap.predict_nt & ~actual_taken) | (ap.predict_t & actual_taken)) & ~any_jal; // for any_jal adder output is the flush path assign flush_path[31:1] = (any_jal) ? aout[31:1] : pcout[31:1]; // pcall and pret are included here assign cond_mispredict = (ap.predict_t & ~actual_taken) | (ap.predict_nt & actual_taken); // target mispredicts on ret's assign target_mispredict = pp_ff.pret & (pp_ff.prett[31:1] != aout[31:1]); assign flush_upper = ( ap.jal | cond_mispredict | target_mispredict) & valid_ff & ~flush & ~freeze; // .i 3 // .o 2 // .ilb hist[1] hist[0] taken // .ob newhist[1] newhist[0] // .type fd // // 00 0 01 // 01 0 01 // 10 0 00 // 11 0 10 // 00 1 10 // 01 1 00 // 10 1 11 // 11 1 11 logic [1:0] newhist; assign newhist[1] = (pp_ff.hist[1]&pp_ff.hist[0]) | (!pp_ff.hist[0]&actual_taken); assign newhist[0] = (!pp_ff.hist[1]&!actual_taken) | (pp_ff.hist[1]&actual_taken); always_comb begin predict_p_ff = pp_ff; predict_p_ff.misp = (valid_ff) ? (cond_mispredict | target_mispredict) & ~flush : pp_ff.misp; predict_p_ff.ataken = (valid_ff) ? actual_taken : pp_ff.ataken; predict_p_ff.hist[1] = (valid_ff) ? newhist[1] : pp_ff.hist[1]; predict_p_ff.hist[0] = (valid_ff) ? newhist[0] : pp_ff.hist[0]; end endmodule // exu_alu_ctl