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