blob: 3ea3e2978228c2f5ec93da620c608c7b6f5b3f1e [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
module otbn_predecode
import otbn_pkg::*;
#(
parameter int ImemSizeByte = 4096,
localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte)
) (
input logic clk_i,
input logic rst_ni,
input logic [31:0] imem_rdata_i,
input logic imem_rvalid_i,
input logic [ImemAddrWidth-1:0] imem_raddr_i,
output rf_predec_bignum_t rf_predec_bignum_o,
output alu_predec_bignum_t alu_predec_bignum_o,
output ispr_predec_bignum_t ispr_predec_bignum_o,
output mac_predec_bignum_t mac_predec_bignum_o,
output logic lsu_addr_en_predec_o,
output ctrl_flow_predec_t ctrl_flow_predec_o,
output logic [ImemAddrWidth-1:0] ctrl_flow_target_predec_o
);
// The ISA has a fixed 12 bits for loop_bodysize. The maximum possible address for the end of a
// loop is the maximum address in Imem (2^ImemAddrWidth - 4) plus loop_bodysize instructions
// (which take 4 * (2^12 - 1) bytes), plus 4 extra bytes. This simplifies to
//
// (1 << ImemAddrWidth) + (1 << 14) - 4
//
// which is strictly less than (1 << (max(ImemAddrWidth, 14) + 1)), so can be represented with
// max(ImemAddrWidth, 14) + 1 bits.
localparam int unsigned LoopEndAddrWidth = 1 + (ImemAddrWidth < 14 ? 14 : ImemAddrWidth);
logic rf_ren_a_base;
logic rf_ren_b_base;
// Three seperate write enables as the indirect register accesses can write back to the a or
// b source registers. For all other instructions any base register write will be to the
// d destination register.
logic rf_we_a_base;
logic rf_we_b_base;
logic rf_we_d_base;
logic rf_ren_a_bignum;
logic rf_ren_b_bignum;
logic rf_we_bignum;
logic alu_bignum_adder_x_en;
logic alu_bignum_x_res_operand_a_sel;
logic alu_bignum_adder_y_op_a_en;
logic alu_bignum_adder_y_op_shifter_en;
logic alu_bignum_shifter_a_en;
logic alu_bignum_shifter_b_en;
logic alu_bignum_shift_right;
logic [$clog2(WLEN)-1:0] alu_bignum_shift_amt;
logic alu_bignum_shift_mod_sel;
logic alu_bignum_logic_a_en;
logic alu_bignum_logic_shifter_en;
logic [3:0] alu_bignum_logic_res_sel;
logic mac_bignum_op_en;
logic mac_bignum_acc_rd_en;
logic ispr_rd_en;
logic ispr_wr_en;
logic csr_addr_sel;
logic [4:0] insn_rs1, insn_rs2, insn_rd;
logic branch_insn;
logic jump_insn;
logic loop_insn;
wsr_e wsr_addr;
csr_e csr_addr;
ispr_e ispr_addr;
logic [31:0] imm_b_type_base;
logic [31:0] imm_j_type_base;
logic [LoopEndAddrWidth-1:0] loop_end_addr;
assign csr_addr = csr_e'(imem_rdata_i[31:20]);
assign wsr_addr = wsr_e'(imem_rdata_i[20 +: WsrNumWidth]);
assign imm_b_type_base = {{19{imem_rdata_i[31]}}, imem_rdata_i[31], imem_rdata_i[7],
imem_rdata_i[30:25], imem_rdata_i[11:8], 1'b0};
assign imm_j_type_base =
{{12{imem_rdata_i[31]}}, imem_rdata_i[19:12], imem_rdata_i[20], imem_rdata_i[30:21], 1'b0};
logic unused_imm_b_type_base;
assign unused_imm_b_type_base = ^imm_b_type_base[31:ImemAddrWidth];
logic unused_imm_j_type_base;
assign unused_imm_j_type_base = ^imm_j_type_base[31:ImemAddrWidth];
assign loop_end_addr = LoopEndAddrWidth'(imem_raddr_i) +
LoopEndAddrWidth'({imem_rdata_i[31:20], 2'b00}) + 'd4;
if (LoopEndAddrWidth > ImemAddrWidth) begin : g_unused_loop_end_addr
logic unused_loop_end_addr;
assign unused_loop_end_addr = ^loop_end_addr[LoopEndAddrWidth-1:ImemAddrWidth];
end
// Shift amount for ALU instructions other than BN.RSHI
logic [$clog2(WLEN)-1:0] shift_amt_a_type_bignum;
// Shift amount for BN.RSHI
logic [$clog2(WLEN)-1:0] shift_amt_s_type_bignum;
assign shift_amt_a_type_bignum = {imem_rdata_i[29:25], 3'b0};
assign shift_amt_s_type_bignum = {imem_rdata_i[31:25], imem_rdata_i[14]};
always_comb begin
rf_ren_a_base = 1'b0;
rf_ren_b_base = 1'b0;
rf_we_a_base = 1'b0;
rf_we_b_base = 1'b0;
rf_we_d_base = 1'b0;
rf_ren_a_bignum = 1'b0;
rf_ren_b_bignum = 1'b0;
rf_we_bignum = 1'b0;
alu_bignum_adder_x_en = 1'b0;
alu_bignum_x_res_operand_a_sel = 1'b0;
alu_bignum_adder_y_op_a_en = 1'b0;
alu_bignum_adder_y_op_shifter_en = 1'b0;
alu_bignum_shifter_a_en = 1'b0;
alu_bignum_shifter_b_en = 1'b0;
alu_bignum_shift_right = 1'b0;
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_shift_mod_sel = 1'b1;
alu_bignum_logic_a_en = 1'b0;
alu_bignum_logic_shifter_en = 1'b0;
alu_bignum_logic_res_sel = '0;
mac_bignum_op_en = 1'b0;
mac_bignum_acc_rd_en = 1'b0;
ispr_rd_en = 1'b0;
ispr_wr_en = 1'b0;
csr_addr_sel = 1'b0;
lsu_addr_en_predec_o = 1'b0;
branch_insn = 1'b0;
jump_insn = 1'b0;
loop_insn = 1'b0;
ctrl_flow_target_predec_o = '0;
if (imem_rvalid_i) begin
unique case (imem_rdata_i[6:0])
//////////////
// Base ALU //
//////////////
InsnOpcodeBaseLui: begin // Load Upper Immediate
rf_we_d_base = 1'b1;
end
InsnOpcodeBaseOpImm: begin // Register-Immediate ALU Operations
rf_ren_a_base = 1'b1;
rf_we_d_base = 1'b1;
end
InsnOpcodeBaseOp: begin // Register-Register ALU operation
rf_ren_a_base = 1'b1;
rf_ren_b_base = 1'b1;
rf_we_d_base = 1'b1;
end
///////////////////////
// Base Load / Store //
///////////////////////
InsnOpcodeBaseLoad: begin
rf_ren_a_base = 1'b1;
rf_we_d_base = 1'b1;
if (imem_rdata_i[14:12] == 3'b010) begin
lsu_addr_en_predec_o = 1'b1;
end
end
InsnOpcodeBaseStore: begin
rf_ren_a_base = 1'b1;
rf_ren_b_base = 1'b1;
if (imem_rdata_i[14:12] == 3'b010) begin
lsu_addr_en_predec_o = 1'b1;
end
end
////////////////////////
// Base Jump / Branch //
////////////////////////
InsnOpcodeBaseBranch: begin
rf_ren_a_base = 1'b1;
rf_ren_b_base = 1'b1;
branch_insn = 1'b1;
ctrl_flow_target_predec_o = imem_raddr_i + imm_b_type_base[ImemAddrWidth-1:0];
end
InsnOpcodeBaseJal: begin
rf_we_d_base = 1'b1;
jump_insn = 1'b1;
ctrl_flow_target_predec_o = imem_raddr_i + imm_j_type_base[ImemAddrWidth-1:0];
end
InsnOpcodeBaseJalr: begin
rf_ren_a_base = 1'b1;
rf_we_d_base = 1'b1;
jump_insn = 1'b1;
end
//////////////
// Base CSR //
//////////////
InsnOpcodeBaseSystem: begin
csr_addr_sel = 1'b1;
if (imem_rdata_i[14:12] != 3'b000) begin
// Any CSR access
rf_ren_a_base = 1'b1;
rf_we_d_base = 1'b1;
end
if (csr_addr == CsrRndPrefetch) begin
// Prefetch CSR does not access any ISPR
ispr_rd_en = 1'b0;
ispr_wr_en = 1'b0;
end else if (imem_rdata_i[14:12] == 3'b001) begin
// No read if destination is x0 unless read is to flags CSR. Both flag groups are in
// a single ISPR so to write one group the other must be read to write it back
// unchanged.
ispr_rd_en = (imem_rdata_i[11:7] != 5'b0) | (csr_addr == CsrFg0) | (csr_addr == CsrFg1);
ispr_wr_en = 1'b1;
end else if (imem_rdata_i[14:12] == 3'b010) begin
// Read and set if source register isn't x0, otherwise read only
if (imem_rdata_i[19:15] != 5'b0) begin
ispr_rd_en = 1'b1;
ispr_wr_en = 1'b1;
end else begin
ispr_rd_en = 1'b1;
end
end
end
////////////////
// Bignum ALU //
////////////////
InsnOpcodeBignumArith: begin
unique case (imem_rdata_i[14:12])
3'b000, 3'b001, 3'b010, 3'b011: begin
// BN.ADD/BN.SUB/BN.ADDC/BN.SUBB
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
rf_we_bignum = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = imem_rdata_i[30];
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_adder_y_op_a_en = 1'b1;
alu_bignum_adder_y_op_shifter_en = 1'b1;
end
3'b100: begin
// BN.ADDI/BN.SUBI
rf_ren_a_bignum = 1'b1;
rf_we_bignum = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = imem_rdata_i[30];
alu_bignum_shift_amt = '0;
alu_bignum_adder_y_op_a_en = 1'b1;
alu_bignum_adder_y_op_shifter_en = 1'b1;
end
3'b101: begin
// BN.ADDM/BN.SUBM
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
rf_we_bignum = 1'b1;
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_adder_x_en = 1'b1;
alu_bignum_x_res_operand_a_sel = 1'b1;
alu_bignum_shift_mod_sel = 1'b0;
end
default: ;
endcase
end
////////////////////////////
// Bignum logical/BN.RSHI //
////////////////////////////
InsnOpcodeBignumBaseMisc: begin
unique case (imem_rdata_i[14:12])
3'b000, 3'b001: begin // BN.LOOP[I]
rf_ren_a_base = ~imem_rdata_i[12];
loop_insn = 1'b1;
ctrl_flow_target_predec_o = loop_end_addr[ImemAddrWidth-1:0];
end
3'b010, 3'b100, 3'b110: begin // BN.AND/BN.OR/BN.XOR
rf_we_bignum = 1'b1;
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = imem_rdata_i[30];
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_logic_a_en = 1'b1;
alu_bignum_logic_shifter_en = 1'b1;
alu_bignum_logic_res_sel[AluOpLogicXor] = imem_rdata_i[14:12] == 3'b110;
alu_bignum_logic_res_sel[AluOpLogicOr] = imem_rdata_i[14:12] == 3'b100;
alu_bignum_logic_res_sel[AluOpLogicAnd] = imem_rdata_i[14:12] == 3'b010;
end
3'b111, 3'b011: begin // BN.RSHI
rf_we_bignum = 1'b1;
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
alu_bignum_shifter_a_en = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = 1'b1;
alu_bignum_shift_amt = shift_amt_s_type_bignum;
end
3'b101: begin // BN.NOT
rf_we_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = imem_rdata_i[30];
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_logic_shifter_en = 1'b1;
alu_bignum_logic_res_sel[AluOpLogicNot] = 1'b1;
end
default: ;
endcase
end
///////////////////////////////////////////////
// Bignum Misc WSR/LID/SID/MOV[R]/CMP[B]/SEL //
///////////////////////////////////////////////
InsnOpcodeBignumMisc: begin
unique case (imem_rdata_i[14:12])
3'b000: begin // BN.SEL
rf_we_bignum = 1'b1;
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
end
3'b011, 3'b001: begin // BN.CMP[B]
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
alu_bignum_shifter_b_en = 1'b1;
alu_bignum_shift_right = imem_rdata_i[30];
alu_bignum_shift_amt = shift_amt_a_type_bignum;
alu_bignum_adder_y_op_a_en = 1'b1;
alu_bignum_adder_y_op_shifter_en = 1'b1;
end
3'b100, 3'b101: begin // BN.LID, BN.SID
rf_ren_a_base = 1'b1;
rf_ren_b_base = 1'b1;
lsu_addr_en_predec_o = 1'b1;
if (imem_rdata_i[8]) begin
rf_we_a_base = 1'b1;
end
if (imem_rdata_i[7]) begin
rf_we_b_base = 1'b1;
end
end
3'b110: begin
if (imem_rdata_i[31]) begin // BN.MOVR
// bignum RF read and write occur in the following cycle due to the indirect
// register access so aren't set here. otbn_controller sets the appropriate read and
// write enables directly in the instruction fetch stage in the first cycle of the
// instruction's execution (so they can be used in the second cycle which performs
// the bignum RF access).
rf_ren_a_base = 1'b1;
rf_ren_b_base = 1'b1;
if (imem_rdata_i[9]) begin
rf_we_a_base = 1'b1;
end else if (imem_rdata_i[7]) begin
rf_we_b_base = 1'b1;
end
end else begin // BN.MOV
rf_we_bignum = 1'b1;
rf_ren_a_bignum = 1'b1;
end
end
3'b111: begin
if (imem_rdata_i[31]) begin // BN.WSRW
rf_ren_a_bignum = 1'b1;
ispr_wr_en = 1'b1;
end else begin // BN.WSRR
rf_we_bignum = 1'b1;
ispr_rd_en = 1'b1;
end
end
default: ;
endcase
end
////////////////////////////////////////////
// BN.MULQACC/BN.MULQACC.WO/BN.MULQACC.SO //
////////////////////////////////////////////
InsnOpcodeBignumMulqacc: begin
rf_ren_a_bignum = 1'b1;
rf_ren_b_bignum = 1'b1;
mac_bignum_op_en = 1'b1;
// BN.MULQACC.WO/BN.MULQACC.SO
if (imem_rdata_i[30] == 1'b1 || imem_rdata_i[29] == 1'b1) begin
rf_we_bignum = 1'b1;
end
if (imem_rdata_i[12] == 1'b0) begin
// zero_acc not set
mac_bignum_acc_rd_en = 1'b1;
end
end
default: ;
endcase
end
end
always_comb begin
ispr_addr = IsprMod;
if (csr_addr_sel) begin
unique case (csr_addr)
CsrFlags, CsrFg0, CsrFg1: ispr_addr = IsprFlags;
CsrMod0, CsrMod1, CsrMod2, CsrMod3,
CsrMod4, CsrMod5, CsrMod6, CsrMod7: ispr_addr = IsprMod;
CsrRnd: ispr_addr = IsprRnd;
CsrUrnd: ispr_addr = IsprUrnd;
default: ;
endcase
end else begin
unique case (wsr_addr)
WsrMod: ispr_addr = IsprMod;
WsrRnd: ispr_addr = IsprRnd;
WsrUrnd: ispr_addr = IsprUrnd;
WsrAcc: ispr_addr = IsprAcc;
WsrKeyS0L: ispr_addr = IsprKeyS0L;
WsrKeyS0H: ispr_addr = IsprKeyS0H;
WsrKeyS1L: ispr_addr = IsprKeyS1L;
WsrKeyS1H: ispr_addr = IsprKeyS1H;
default: ;
endcase
end
end
assign alu_predec_bignum_o.adder_x_en = alu_bignum_adder_x_en;
assign alu_predec_bignum_o.x_res_operand_a_sel = alu_bignum_x_res_operand_a_sel;
assign alu_predec_bignum_o.adder_y_op_a_en = alu_bignum_adder_y_op_a_en;
assign alu_predec_bignum_o.adder_y_op_shifter_en = alu_bignum_adder_y_op_shifter_en;
assign alu_predec_bignum_o.shifter_a_en = alu_bignum_shifter_a_en;
assign alu_predec_bignum_o.shifter_b_en = alu_bignum_shifter_b_en;
assign alu_predec_bignum_o.shift_right = alu_bignum_shift_right;
assign alu_predec_bignum_o.shift_amt = alu_bignum_shift_amt;
assign alu_predec_bignum_o.shift_mod_sel = alu_bignum_shift_mod_sel;
assign alu_predec_bignum_o.logic_a_en = alu_bignum_logic_a_en;
assign alu_predec_bignum_o.logic_shifter_en = alu_bignum_logic_shifter_en;
assign alu_predec_bignum_o.logic_res_sel = alu_bignum_logic_res_sel;
assign mac_predec_bignum_o.op_en = mac_bignum_op_en;
assign mac_predec_bignum_o.acc_rd_en = mac_bignum_acc_rd_en;
assign insn_rs1 = imem_rdata_i[19:15];
assign insn_rs2 = imem_rdata_i[24:20];
assign insn_rd = imem_rdata_i[11:7];
prim_onehot_enc #(
.OneHotWidth(NWdr)
) rf_ren_a_bignum_onehot_enc (
.in_i (insn_rs1),
.en_i (rf_ren_a_bignum),
.out_o (rf_predec_bignum_o.rf_ren_a)
);
prim_onehot_enc #(
.OneHotWidth(NWdr)
) rf_ren_b_bignum_onehot_enc (
.in_i (insn_rs2),
.en_i (rf_ren_b_bignum),
.out_o (rf_predec_bignum_o.rf_ren_b)
);
prim_onehot_enc #(
.OneHotWidth(NWdr)
) rf_we_bignum_onehot_enc (
.in_i (insn_rd),
.en_i (rf_we_bignum),
.out_o (rf_predec_bignum_o.rf_we)
);
prim_onehot_enc #(
.OneHotWidth(NIspr)
) ispr_rd_en_onehot_enc (
.in_i (ispr_addr),
.en_i (ispr_rd_en),
.out_o (ispr_predec_bignum_o.ispr_rd_en)
);
prim_onehot_enc #(
.OneHotWidth(NIspr)
) ispr_wr_en_onehot_enc (
.in_i (ispr_addr),
.en_i (ispr_wr_en),
.out_o (ispr_predec_bignum_o.ispr_wr_en)
);
assign ctrl_flow_predec_o.call_stack_pop = (rf_ren_a_base & insn_rs1 == 5'd1) |
(rf_ren_b_base & insn_rs2 == 5'd1);
assign ctrl_flow_predec_o.call_stack_push = (rf_we_a_base & insn_rs1 == 5'd1) |
(rf_we_b_base & insn_rs2 == 5'd1) |
(rf_we_d_base & insn_rd == 5'd1);
assign ctrl_flow_predec_o.branch_insn = branch_insn;
assign ctrl_flow_predec_o.jump_insn = jump_insn;
assign ctrl_flow_predec_o.loop_insn = loop_insn;
logic unused_clk, unused_rst;
assign unused_clk = clk_i;
assign unused_rst = rst_ni;
`ASSERT(RFRenABignumOnehot, $onehot0(rf_predec_bignum_o.rf_ren_a))
`ASSERT(RFRenBBignumOnehot, $onehot0(rf_predec_bignum_o.rf_ren_b))
`ASSERT(RFWeBignumOnehot, $onehot0(rf_predec_bignum_o.rf_we))
endmodule