abstractaccelerator/design/exu/exu_alu_ctl.sv

276 lines
7.9 KiB
Systemverilog

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