|  | // 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" | 
|  |  | 
|  | /** | 
|  | * OTBN Controller | 
|  | */ | 
|  | module otbn_controller | 
|  | import otbn_pkg::*; | 
|  | #( | 
|  | // Size of the instruction memory, in bytes | 
|  | parameter int ImemSizeByte = 4096, | 
|  | // Size of the data memory, in bytes | 
|  | parameter int DmemSizeByte = 4096, | 
|  |  | 
|  | localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte), | 
|  | localparam int DmemAddrWidth = prim_util_pkg::vbits(DmemSizeByte) | 
|  | ) ( | 
|  | input logic clk_i, | 
|  | input logic rst_ni, | 
|  |  | 
|  | input  logic start_i,   // start the processing at address zero | 
|  | output logic locking_o, // Controller is in or is entering the locked state | 
|  | input  logic err_bit_clear_i, | 
|  |  | 
|  | input prim_mubi_pkg::mubi4_t fatal_escalate_en_i, | 
|  | input prim_mubi_pkg::mubi4_t recov_escalate_en_i, | 
|  | input prim_mubi_pkg::mubi4_t rma_req_i, | 
|  | output controller_err_bits_t err_bits_o, | 
|  | output logic                 recoverable_err_o, | 
|  |  | 
|  | // Next instruction selection (to instruction fetch) | 
|  | output logic                     insn_fetch_req_valid_o, | 
|  | output logic                     insn_fetch_req_valid_raw_o, | 
|  | output logic [ImemAddrWidth-1:0] insn_fetch_req_addr_o, | 
|  | output logic                     insn_fetch_resp_clear_o, | 
|  |  | 
|  | // Fetched/decoded instruction | 
|  | input logic                     insn_valid_i, | 
|  | input logic                     insn_illegal_i, | 
|  | input logic [ImemAddrWidth-1:0] insn_addr_i, | 
|  |  | 
|  | // Decoded instruction data | 
|  | input insn_dec_base_t   insn_dec_base_i, | 
|  | input insn_dec_bignum_t insn_dec_bignum_i, | 
|  | input insn_dec_shared_t insn_dec_shared_i, | 
|  |  | 
|  | // Base register file | 
|  | output logic [4:0]               rf_base_wr_addr_o, | 
|  | output logic                     rf_base_wr_en_o, | 
|  | output logic                     rf_base_wr_commit_o, | 
|  | output logic [31:0]              rf_base_wr_data_no_intg_o, | 
|  | output logic [BaseIntgWidth-1:0] rf_base_wr_data_intg_o, | 
|  | output logic                     rf_base_wr_data_intg_sel_o, | 
|  |  | 
|  | output logic [4:0]               rf_base_rd_addr_a_o, | 
|  | output logic                     rf_base_rd_en_a_o, | 
|  | input  logic [BaseIntgWidth-1:0] rf_base_rd_data_a_intg_i, | 
|  | output logic [4:0]               rf_base_rd_addr_b_o, | 
|  | output logic                     rf_base_rd_en_b_o, | 
|  | input  logic [BaseIntgWidth-1:0] rf_base_rd_data_b_intg_i, | 
|  | output logic                     rf_base_rd_commit_o, | 
|  |  | 
|  | input logic rf_base_call_stack_sw_err_i, | 
|  | input logic rf_base_call_stack_hw_err_i, | 
|  |  | 
|  | // Bignum register file (WDRs) | 
|  | output logic [4:0]         rf_bignum_wr_addr_o, | 
|  | output logic [1:0]         rf_bignum_wr_en_o, | 
|  | output logic               rf_bignum_wr_commit_o, | 
|  | output logic [WLEN-1:0]    rf_bignum_wr_data_no_intg_o, | 
|  | output logic [ExtWLEN-1:0] rf_bignum_wr_data_intg_o, | 
|  | output logic               rf_bignum_wr_data_intg_sel_o, | 
|  |  | 
|  | output logic [4:0]         rf_bignum_rd_addr_a_o, | 
|  | output logic               rf_bignum_rd_en_a_o, | 
|  | input  logic [ExtWLEN-1:0] rf_bignum_rd_data_a_intg_i, | 
|  |  | 
|  | output logic [4:0]         rf_bignum_rd_addr_b_o, | 
|  | output logic               rf_bignum_rd_en_b_o, | 
|  | input  logic [ExtWLEN-1:0] rf_bignum_rd_data_b_intg_i, | 
|  |  | 
|  | input logic rf_bignum_intg_err_i, | 
|  | input logic rf_bignum_spurious_we_err_i, | 
|  |  | 
|  | output logic [NWdr-1:0] rf_bignum_rd_a_indirect_onehot_o, | 
|  | output logic [NWdr-1:0] rf_bignum_rd_b_indirect_onehot_o, | 
|  | output logic [NWdr-1:0] rf_bignum_wr_indirect_onehot_o, | 
|  | output logic            rf_bignum_indirect_en_o, | 
|  |  | 
|  | // Execution units | 
|  |  | 
|  | // Base ALU | 
|  | output alu_base_operation_t  alu_base_operation_o, | 
|  | output alu_base_comparison_t alu_base_comparison_o, | 
|  | input  logic [31:0]          alu_base_operation_result_i, | 
|  | input  logic                 alu_base_comparison_result_i, | 
|  |  | 
|  | // Bignum ALU | 
|  | output alu_bignum_operation_t alu_bignum_operation_o, | 
|  | output logic                  alu_bignum_operation_valid_o, | 
|  | output logic                  alu_bignum_operation_commit_o, | 
|  | input  logic [WLEN-1:0]       alu_bignum_operation_result_i, | 
|  | input  logic                  alu_bignum_selection_flag_i, | 
|  |  | 
|  | // Bignum MAC | 
|  | output mac_bignum_operation_t mac_bignum_operation_o, | 
|  | input  logic [WLEN-1:0]       mac_bignum_operation_result_i, | 
|  | output logic                  mac_bignum_en_o, | 
|  | output logic                  mac_bignum_commit_o, | 
|  |  | 
|  | // LSU | 
|  | output logic                     lsu_load_req_o, | 
|  | output logic                     lsu_store_req_o, | 
|  | output insn_subset_e             lsu_req_subset_o, | 
|  | output logic [DmemAddrWidth-1:0] lsu_addr_o, | 
|  | input  logic                     lsu_addr_en_predec_i, | 
|  |  | 
|  | output logic [BaseIntgWidth-1:0] lsu_base_wdata_o, | 
|  | output logic [ExtWLEN-1:0]       lsu_bignum_wdata_o, | 
|  |  | 
|  | input  logic [BaseIntgWidth-1:0] lsu_base_rdata_i, | 
|  | input  logic [ExtWLEN-1:0]       lsu_bignum_rdata_i, | 
|  |  | 
|  | // Internal Special-Purpose Registers (ISPRs) | 
|  | output ispr_e                       ispr_addr_o, | 
|  | output logic [31:0]                 ispr_base_wdata_o, | 
|  | output logic [BaseWordsPerWLEN-1:0] ispr_base_wr_en_o, | 
|  | output logic [ExtWLEN-1:0]          ispr_bignum_wdata_intg_o, | 
|  | output logic                        ispr_bignum_wr_en_o, | 
|  | output logic                        ispr_wr_commit_o, | 
|  | input  logic [ExtWLEN-1:0]          ispr_rdata_intg_i, | 
|  | output logic                        ispr_rd_en_o, | 
|  |  | 
|  | // RND interface | 
|  | output logic rnd_req_o, | 
|  | output logic rnd_prefetch_req_o, | 
|  | input  logic rnd_valid_i, | 
|  |  | 
|  | input  logic urnd_reseed_err_i, | 
|  |  | 
|  | // Secure Wipe | 
|  | output logic secure_wipe_req_o, | 
|  | input  logic secure_wipe_ack_i, | 
|  | input  logic sec_wipe_zero_i, | 
|  | input  logic secure_wipe_running_i, | 
|  |  | 
|  | input  logic        state_reset_i, | 
|  | output logic [31:0] insn_cnt_o, | 
|  | input  logic        insn_cnt_clear_i, | 
|  | output logic        mems_sec_wipe_o, | 
|  |  | 
|  | input  logic        software_errs_fatal_i, | 
|  |  | 
|  | input logic [1:0] sideload_key_shares_valid_i, | 
|  |  | 
|  | // Prefetch stage control | 
|  | output logic                     prefetch_en_o, | 
|  | output logic                     prefetch_loop_active_o, | 
|  | output logic [31:0]              prefetch_loop_iterations_o, | 
|  | output logic [ImemAddrWidth:0]   prefetch_loop_end_addr_o, | 
|  | output logic [ImemAddrWidth-1:0] prefetch_loop_jump_addr_o, | 
|  | output logic                     prefetch_ignore_errs_o, | 
|  |  | 
|  | // Predecoded control | 
|  | input  ctrl_flow_predec_t        ctrl_flow_predec_i, | 
|  | input  logic [ImemAddrWidth-1:0] ctrl_flow_target_predec_i, | 
|  | output logic                     predec_error_o | 
|  | ); | 
|  | import prim_mubi_pkg::*; | 
|  |  | 
|  | otbn_state_e state_q, state_d; | 
|  |  | 
|  |  | 
|  | controller_err_bits_t err_bits_q, err_bits_d; | 
|  |  | 
|  | // The specific error signals that go into err_bits_d | 
|  | logic fatal_software_err, bad_internal_state_err, reg_intg_violation_err, key_invalid_err; | 
|  | logic illegal_insn_err, bad_data_addr_err, call_stack_sw_err, bad_insn_addr_err; | 
|  |  | 
|  | logic err; | 
|  | logic internal_err; | 
|  | logic recoverable_err; | 
|  | logic software_err; | 
|  | logic non_insn_addr_software_err; | 
|  | logic fatal_err; | 
|  | logic internal_fatal_err; | 
|  | logic done_complete; | 
|  | logic executing; | 
|  | logic state_error, state_error_d, state_error_q; | 
|  | logic spurious_secure_wipe_ack_q, spurious_secure_wipe_ack_d; | 
|  | logic mubi_err_q, mubi_err_d; | 
|  |  | 
|  | logic                     insn_fetch_req_valid_raw; | 
|  | logic [ImemAddrWidth-1:0] insn_fetch_req_addr_last; | 
|  |  | 
|  | logic stall; | 
|  | logic ispr_stall; | 
|  | logic mem_stall; | 
|  | logic rf_indirect_stall; | 
|  | logic jump_or_branch; | 
|  | logic branch_taken; | 
|  | logic insn_executing; | 
|  | logic ld_insn_with_addr_from_call_stack, st_insn_with_addr_from_call_stack; | 
|  | logic [ImemAddrWidth-1:0] branch_target; | 
|  | logic                     branch_target_overflow; | 
|  | logic [ImemAddrWidth:0]   next_insn_addr_wide; | 
|  | logic [ImemAddrWidth-1:0] next_insn_addr; | 
|  |  | 
|  | csr_e                                csr_addr; | 
|  | logic [$clog2(BaseWordsPerWLEN)-1:0] csr_sub_addr; | 
|  | logic [31:0]                         csr_rdata_raw; | 
|  | logic [31:0]                         csr_rdata; | 
|  | logic [BaseWordsPerWLEN-1:0]         csr_rdata_mux [32]; | 
|  | logic [31:0]                         csr_wdata_raw; | 
|  | logic [31:0]                         csr_wdata; | 
|  |  | 
|  | wsr_e                                wsr_addr; | 
|  | logic [WLEN-1:0]                     wsr_wdata; | 
|  |  | 
|  | ispr_e                               ispr_addr_base; | 
|  | logic [$clog2(BaseWordsPerWLEN)-1:0] ispr_word_addr_base; | 
|  | logic [BaseWordsPerWLEN-1:0]         ispr_word_sel_base; | 
|  |  | 
|  | ispr_e                               ispr_addr_bignum; | 
|  |  | 
|  | logic                                ispr_wr_insn, ispr_rd_insn; | 
|  | logic                                ispr_wr_base_insn; | 
|  | logic                                ispr_wr_bignum_insn; | 
|  | logic                                ispr_rd_bignum_insn; | 
|  |  | 
|  | logic                     lsu_load_req_raw; | 
|  | logic                     lsu_store_req_raw; | 
|  | logic [DmemAddrWidth-1:0] lsu_addr, lsu_addr_blanked, lsu_addr_saved_d, lsu_addr_saved_q; | 
|  | logic                     lsu_addr_saved_sel; | 
|  | logic                     expected_lsu_addr_en; | 
|  |  | 
|  | logic                     expected_call_stack_push, expected_call_stack_pop; | 
|  | logic                     lsu_predec_error, branch_target_predec_error, ctrl_predec_error; | 
|  |  | 
|  | logic rnd_req_raw; | 
|  |  | 
|  | // Register read data with integrity stripped off | 
|  | logic [31:0]     rf_base_rd_data_a_no_intg; | 
|  | logic [31:0]     rf_base_rd_data_b_no_intg; | 
|  | logic [WLEN-1:0] rf_bignum_rd_data_a_no_intg; | 
|  | logic [WLEN-1:0] rf_bignum_rd_data_b_no_intg; | 
|  |  | 
|  | logic [ExtWLEN-1:0] selection_result; | 
|  |  | 
|  | logic [1:0] rf_bignum_wr_en_unbuf; | 
|  | logic [4:0] rf_bignum_wr_addr_unbuf; | 
|  | logic [4:0] rf_bignum_rd_addr_a_unbuf; | 
|  | logic       rf_bignum_rd_en_a_unbuf; | 
|  | logic [4:0] rf_bignum_rd_addr_b_unbuf; | 
|  | logic       rf_bignum_rd_en_b_unbuf; | 
|  |  | 
|  | logic rf_bignum_rd_a_indirect_en; | 
|  | logic rf_bignum_rd_b_indirect_en; | 
|  | logic rf_bignum_wr_indirect_en; | 
|  |  | 
|  | // Computed increments for indirect register index and memory address in BN.LID/BN.SID/BN.MOVR | 
|  | // instructions. | 
|  | logic [5:0]  rf_base_rd_data_a_inc; | 
|  | logic [5:0]  rf_base_rd_data_b_inc; | 
|  | logic [26:0] rf_base_rd_data_a_wlen_word_inc; | 
|  |  | 
|  | // Read/Write enables for base register file before illegal instruction encoding are factored in | 
|  | logic rf_base_rd_en_a_raw, rf_base_rd_en_b_raw, rf_base_wr_en_raw; | 
|  |  | 
|  | // Output of mux taking the above increments as inputs and choosing one to write back to base | 
|  | // register file with appropriate zero extension and padding to give a 32-bit result. | 
|  | logic [31:0]              increment_out; | 
|  |  | 
|  | // Loop control, used to start a new loop | 
|  | logic        loop_start_req; | 
|  | logic        loop_start_commit; | 
|  | logic        loop_reset; | 
|  | logic [11:0] loop_bodysize; | 
|  | logic [31:0] loop_iterations; | 
|  |  | 
|  | // Loop generated jumps. The loop controller asks to jump when execution reaches the end of a loop | 
|  | // body that hasn't completed all of its iterations. | 
|  | logic                     loop_jump; | 
|  | logic [ImemAddrWidth-1:0] loop_jump_addr; | 
|  |  | 
|  | logic [WLEN-1:0] mac_bignum_rf_wr_data; | 
|  |  | 
|  | logic loop_hw_err, loop_predec_err; | 
|  | logic csr_illegal_addr, wsr_illegal_addr, ispr_illegal_addr; | 
|  | logic imem_addr_err, loop_sw_err, ispr_err; | 
|  | logic dmem_addr_err_check, dmem_addr_err; | 
|  | logic dmem_addr_unaligned_base, dmem_addr_unaligned_bignum, dmem_addr_overflow; | 
|  | logic illegal_insn_static; | 
|  | logic key_invalid; | 
|  |  | 
|  | logic rf_a_indirect_err, rf_b_indirect_err, rf_d_indirect_err, rf_indirect_err; | 
|  |  | 
|  | // If we are doing an indirect access to the bignum register file, it's possible that the | 
|  | // address that we use for the access is architecturally unknown. This happens if it came from x1 | 
|  | // and we've underflowed the call stack. When this happens, we want to ignore any read data | 
|  | // integrity errors and spurious write enable errors since the access to the bignum register file | 
|  | // didn't happen architecturally anyway. | 
|  | logic ignore_rf_bignum_intg_errs; | 
|  | logic rf_bignum_intg_err; | 
|  | logic ignore_rf_bignum_spurious_we_errs; | 
|  | logic rf_bignum_spurious_we_err; | 
|  |  | 
|  | logic ispr_rdata_intg_err; | 
|  |  | 
|  | logic [31:0] insn_cnt_d, insn_cnt_q; | 
|  | logic        insn_cnt_clear; | 
|  |  | 
|  | logic [4:0] insn_bignum_rd_addr_a_q, insn_bignum_rd_addr_b_q, insn_bignum_wr_addr_q; | 
|  |  | 
|  | logic       start_secure_wipe; | 
|  | logic       secure_wipe_running_q, secure_wipe_running_d; | 
|  |  | 
|  | assign secure_wipe_running_d = start_secure_wipe | (secure_wipe_running_q & ~secure_wipe_ack_i); | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | secure_wipe_running_q <= 1'b0; | 
|  | end else begin | 
|  | secure_wipe_running_q <= secure_wipe_running_d; | 
|  | end | 
|  | end | 
|  | assign secure_wipe_req_o = start_secure_wipe | secure_wipe_running_q; | 
|  |  | 
|  | // Spot spurious acks on the secure wipe interface. There is a an ack at the end of the initial | 
|  | // secure wipe, and as `secure_wipe_running_q` is only high during secure wipes triggered by this | 
|  | // controller, we have to ignore acks before the initial secure wipe is done.  Register this | 
|  | // signal to break a circular path (a secure wipe can be triggered by a stop, and a spurious | 
|  | // secure wipe ack can trigger a stop). | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | spurious_secure_wipe_ack_q <= 1'b0; | 
|  | end else begin | 
|  | spurious_secure_wipe_ack_q <= spurious_secure_wipe_ack_d; | 
|  | end | 
|  | end | 
|  | assign spurious_secure_wipe_ack_d = spurious_secure_wipe_ack_q | | 
|  | (secure_wipe_ack_i      & | 
|  | ~secure_wipe_running_q & | 
|  | ~secure_wipe_running_i); | 
|  |  | 
|  | // Stall a cycle on loads to allow load data writeback to happen the following cycle. Stall not | 
|  | // required on stores as there is no response to deal with. | 
|  | assign mem_stall = lsu_load_req_raw; | 
|  |  | 
|  | // Reads to RND must stall until data is available | 
|  | assign ispr_stall = rnd_req_raw & ~rnd_valid_i; | 
|  |  | 
|  | assign rf_indirect_stall = insn_valid_i & | 
|  | (state_q != OtbnStateStall) & | 
|  | (insn_dec_shared_i.subset == InsnSubsetBignum) & | 
|  | (insn_dec_bignum_i.rf_a_indirect | | 
|  | insn_dec_bignum_i.rf_b_indirect | | 
|  | insn_dec_bignum_i.rf_d_indirect); | 
|  |  | 
|  | assign stall = mem_stall | ispr_stall | rf_indirect_stall; | 
|  |  | 
|  | // OTBN is done when it was executing something (in state OtbnStateRun or OtbnStateStall) | 
|  | // and either it executes an ecall or an error occurs. A pulse on the done signal raises the | 
|  | // 'done' interrupt and also tells the top-level to update its ERR_BITS status | 
|  | // register. The calculation that ecall triggered done is factored out as `done_complete` to | 
|  | // avoid logic loops in the error handling logic. | 
|  | assign done_complete = (insn_valid_i & insn_dec_shared_i.ecall_insn); | 
|  | assign executing = (state_q == OtbnStateRun) || | 
|  | (state_q == OtbnStateStall); | 
|  |  | 
|  | // Set the *locking* output when the next state is the *locked* state and no secure wipe is | 
|  | // running or there is a URND reseed error.  `locking_o` is thus set only after the secure wipe | 
|  | // has completed or if it cannot complete due to an URND reseed error (in which case | 
|  | // `secure_wipe_req_o` and `urnd_reseed_err_i` will remain high).  The condition for secure wipe | 
|  | // running involves `secure_wipe_running_i`, which is high for the initial secure wipe, and | 
|  | // `secure_wipe_req_o`, which is high for post-execution secure wipes. | 
|  | assign locking_o = (state_d == OtbnStateLocked) & (~(secure_wipe_running_i | secure_wipe_req_o) | | 
|  | urnd_reseed_err_i | mubi_err_d); | 
|  |  | 
|  | assign start_secure_wipe = executing & (done_complete | err); | 
|  |  | 
|  | assign jump_or_branch = (insn_valid_i & | 
|  | (insn_dec_shared_i.branch_insn | insn_dec_shared_i.jump_insn)); | 
|  |  | 
|  | // Branch taken when there is a valid branch instruction and comparison passes or a valid jump | 
|  | // instruction (which is always taken) | 
|  | assign branch_taken = insn_valid_i & | 
|  | ((insn_dec_shared_i.branch_insn & alu_base_comparison_result_i) | | 
|  | insn_dec_shared_i.jump_insn); | 
|  | // Branch target computed by base ALU (PC + imm) | 
|  | assign branch_target = alu_base_operation_result_i[ImemAddrWidth-1:0]; | 
|  | assign branch_target_overflow = |alu_base_operation_result_i[31:ImemAddrWidth]; | 
|  |  | 
|  | assign next_insn_addr_wide = {1'b0, insn_addr_i} + 'd4; | 
|  | assign next_insn_addr = next_insn_addr_wide[ImemAddrWidth-1:0]; | 
|  |  | 
|  | // Record address for fetch request so it can be retried when an invalid response is received | 
|  | always_ff @(posedge clk_i) begin | 
|  | if (insn_fetch_req_valid_raw) begin | 
|  | insn_fetch_req_addr_last <= insn_fetch_req_addr_o; | 
|  | end | 
|  | end | 
|  |  | 
|  | always_comb begin | 
|  | state_d                  = state_q; | 
|  | // `insn_fetch_req_valid_raw` is the value `insn_fetch_req_valid_o` before any errors are | 
|  | // considered. | 
|  | insn_fetch_req_valid_raw = 1'b0; | 
|  | insn_fetch_req_addr_o    = '0; | 
|  | insn_fetch_resp_clear_o  = 1'b1; | 
|  | prefetch_en_o            = 1'b0; | 
|  |  | 
|  | state_error = 1'b0; | 
|  |  | 
|  | unique case (state_q) | 
|  | OtbnStateHalt: begin | 
|  | if (start_i) begin | 
|  | state_d = OtbnStateRun; | 
|  |  | 
|  | insn_fetch_req_addr_o    = '0; | 
|  | insn_fetch_req_valid_raw = 1'b1; | 
|  | prefetch_en_o            = 1'b1; | 
|  | end | 
|  | end | 
|  | OtbnStateRun: begin | 
|  | insn_fetch_req_valid_raw = 1'b1; | 
|  | prefetch_en_o            = 1'b1; | 
|  |  | 
|  | if (!insn_valid_i) begin | 
|  | insn_fetch_req_addr_o = insn_fetch_req_addr_last; | 
|  | end else if (done_complete) begin | 
|  | state_d                  = OtbnStateHalt; | 
|  | insn_fetch_req_valid_raw = 1'b0; | 
|  | prefetch_en_o            = 1'b0; | 
|  | end else begin | 
|  | if (stall) begin | 
|  | // When stalling don't request a new fetch and don't clear response either to keep | 
|  | // current instruction. | 
|  | state_d                  = OtbnStateStall; | 
|  | insn_fetch_req_valid_raw = 1'b0; | 
|  | insn_fetch_resp_clear_o  = 1'b0; | 
|  | end else begin | 
|  | if (branch_taken) begin | 
|  | insn_fetch_req_addr_o = branch_target; | 
|  | end else if (loop_jump) begin | 
|  | insn_fetch_req_addr_o = loop_jump_addr; | 
|  | end else begin | 
|  | insn_fetch_req_addr_o = next_insn_addr; | 
|  | end | 
|  | end | 
|  | end | 
|  | end | 
|  | OtbnStateStall: begin | 
|  | prefetch_en_o = 1'b1; | 
|  | // When stalling refetch the same instruction to keep decode inputs constant | 
|  | if (stall) begin | 
|  | state_d                  = OtbnStateStall; | 
|  | //insn_fetch_req_addr_o = insn_addr_i; | 
|  | insn_fetch_req_valid_raw = 1'b0; | 
|  | insn_fetch_resp_clear_o  = 1'b0; | 
|  | end else begin | 
|  | insn_fetch_req_valid_raw = 1'b1; | 
|  |  | 
|  | if (loop_jump) begin | 
|  | insn_fetch_req_addr_o = loop_jump_addr; | 
|  | end else begin | 
|  | insn_fetch_req_addr_o = next_insn_addr; | 
|  | end | 
|  |  | 
|  | state_d = OtbnStateRun; | 
|  | end | 
|  | end | 
|  | OtbnStateLocked: begin | 
|  | insn_fetch_req_valid_raw = 1'b0; | 
|  | state_d                  = OtbnStateLocked; | 
|  | end | 
|  | default: begin | 
|  | // We should never get here. If we do (e.g. via a malicious glitch), error out immediately. | 
|  | // SEC_CM: CONTROLLER.FSM.LOCAL_ESC | 
|  | state_d = OtbnStateLocked; | 
|  | state_error = 1'b1; | 
|  | end | 
|  | endcase | 
|  |  | 
|  | // On any error immediately halt, either going to OtbnStateLocked or OtbnStateHalt depending on | 
|  | // whether it was a fatal error. | 
|  | if (err) begin | 
|  | prefetch_en_o           = 1'b0; | 
|  | insn_fetch_resp_clear_o = 1'b1; | 
|  |  | 
|  | if (fatal_err) begin | 
|  | // SEC_CM: CONTROLLER.FSM.GLOBAL_ESC | 
|  | state_d = OtbnStateLocked; | 
|  | end else begin | 
|  | state_d = OtbnStateHalt; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Regardless of what happens above enforce staying in OtnbStateLocked. | 
|  | if (state_q == OtbnStateLocked) begin | 
|  | state_d = OtbnStateLocked; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign state_error_d = state_error | state_error_q; | 
|  |  | 
|  | prim_flop #( | 
|  | .Width(1), | 
|  | .ResetValue('0) | 
|  | ) u_state_error_flop ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  |  | 
|  | .d_i(state_error_d), | 
|  | .q_o(state_error_q) | 
|  | ); | 
|  |  | 
|  | `ASSERT(InsnAlwaysValidInStall, state_q == OtbnStateStall |-> insn_valid_i) | 
|  |  | 
|  | // Anything that moves us or keeps us in the stall state should cause `stall` to be asserted | 
|  | `ASSERT(StallIfNextStateStall, insn_valid_i & (state_d == OtbnStateStall) |-> stall) | 
|  |  | 
|  | // The raw signal is needed by the instruction fetch stage for generating instruction address | 
|  | // errors (where instruction fetch and prefetch disagree on address). `err` will factor this in so | 
|  | // using the qualified signal results in a combinational loop. | 
|  | assign insn_fetch_req_valid_raw_o = insn_fetch_req_valid_raw; | 
|  | assign insn_fetch_req_valid_o     = err ? 1'b0 : insn_fetch_req_valid_raw; | 
|  |  | 
|  | // Determine if there are any errors related to the Imem fetch address. | 
|  | always_comb begin | 
|  | imem_addr_err = 1'b0; | 
|  |  | 
|  | if (insn_fetch_req_valid_raw) begin | 
|  | if (|insn_fetch_req_addr_o[1:0]) begin | 
|  | // Imem address is unaligned | 
|  | imem_addr_err = 1'b1; | 
|  | end else if (branch_taken) begin | 
|  | imem_addr_err = branch_target_overflow; | 
|  | end else begin | 
|  | imem_addr_err = next_insn_addr_wide[ImemAddrWidth] & insn_valid_i; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Signal error if MuBi input signals take on invalid values as this means something bad is | 
|  | // happening. Register the error signal to break circular paths (instruction fetch errors factor | 
|  | // into fatal_escalate_en_i, RND errors factor into recov_escalate_en_i). | 
|  | assign mubi_err_d = |{mubi4_test_invalid(fatal_escalate_en_i), | 
|  | mubi4_test_invalid(recov_escalate_en_i), | 
|  | mubi4_test_invalid(rma_req_i), | 
|  | mubi_err_q}; | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | mubi_err_q <= 1'b0; | 
|  | end else begin | 
|  | mubi_err_q <= mubi_err_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Instruction is illegal based on the static properties of the instruction bits (illegal encoding | 
|  | // or illegal WSR/CSR referenced). | 
|  | assign illegal_insn_static = insn_illegal_i | ispr_err; | 
|  |  | 
|  | assign fatal_software_err       = software_err & software_errs_fatal_i; | 
|  | assign bad_internal_state_err   = |{state_error_d, loop_hw_err, rf_base_call_stack_hw_err_i, | 
|  | rf_bignum_spurious_we_err, spurious_secure_wipe_ack_q, | 
|  | mubi_err_q}; | 
|  | assign reg_intg_violation_err   = rf_bignum_intg_err | ispr_rdata_intg_err; | 
|  | assign key_invalid_err          = ispr_rd_bignum_insn & insn_valid_i & key_invalid; | 
|  | assign illegal_insn_err         = illegal_insn_static | rf_indirect_err; | 
|  | assign call_stack_sw_err        = rf_base_call_stack_sw_err_i; | 
|  |  | 
|  | // Flag a bad data address error if the data memory address is invalid and it does not come from | 
|  | // an empty call stack.  The second case cannot be decided as bad data address because the address | 
|  | // on top of the empty call stack may or may not be valid.  (Also, in most RTL simulators an empty | 
|  | // call stack that has never been pushed contains an unknown value, so this error bit would become | 
|  | // unknown.)  Thus, a data memory address coming from an empty call stack raises a call stack | 
|  | // error but never a bad data address error. | 
|  | assign bad_data_addr_err = dmem_addr_err & | 
|  | ~(call_stack_sw_err & | 
|  | (ld_insn_with_addr_from_call_stack | | 
|  | st_insn_with_addr_from_call_stack)); | 
|  |  | 
|  | // Identify load instructions that take the memory address from the call stack. | 
|  | assign ld_insn_with_addr_from_call_stack = insn_valid_i               & | 
|  | insn_dec_shared_i.ld_insn  & | 
|  | insn_dec_base_i.rf_ren_a   & | 
|  | (insn_dec_base_i.a == 5'd1); | 
|  |  | 
|  | // Identify store instructions that take the memory address from the call stack. | 
|  | assign st_insn_with_addr_from_call_stack = insn_valid_i               & | 
|  | insn_dec_shared_i.st_insn  & | 
|  | insn_dec_base_i.rf_ren_a   & | 
|  | (insn_dec_base_i.a == 5'd1); | 
|  |  | 
|  | // All software errors that aren't bad_insn_addr. Factored into bad_insn_addr so it is only raised | 
|  | // if other software errors haven't ocurred. As bad_insn_addr relates to the next instruction | 
|  | // begin fetched it cannot occur if the current instruction has seen an error and failed to | 
|  | // execute. | 
|  | assign non_insn_addr_software_err = |{key_invalid_err, | 
|  | loop_sw_err, | 
|  | illegal_insn_err, | 
|  | call_stack_sw_err, | 
|  | bad_data_addr_err}; | 
|  |  | 
|  | assign bad_insn_addr_err = imem_addr_err & ~non_insn_addr_software_err; | 
|  |  | 
|  | assign err_bits_d = '{ | 
|  | fatal_software:     fatal_software_err, | 
|  | bad_internal_state: bad_internal_state_err, | 
|  | reg_intg_violation: reg_intg_violation_err, | 
|  | key_invalid:        key_invalid_err, | 
|  | loop:               loop_sw_err, | 
|  | illegal_insn:       illegal_insn_err, | 
|  | call_stack:         call_stack_sw_err, | 
|  | bad_data_addr:      bad_data_addr_err, | 
|  | bad_insn_addr:      bad_insn_addr_err | 
|  | }; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | err_bits_q <= '0; | 
|  | end else begin | 
|  | if (err_bit_clear_i && !locking_o) begin | 
|  | err_bits_q <= '0; | 
|  | end else begin | 
|  | err_bits_q <= err_bits_q | err_bits_d; | 
|  | end | 
|  | end | 
|  | end | 
|  | assign err_bits_o = err_bits_q; | 
|  |  | 
|  | assign software_err = non_insn_addr_software_err | bad_insn_addr_err; | 
|  |  | 
|  | assign recoverable_err = mubi4_test_true_loose(recov_escalate_en_i); | 
|  |  | 
|  | assign internal_fatal_err = |{fatal_software_err, | 
|  | bad_internal_state_err, | 
|  | reg_intg_violation_err}; | 
|  |  | 
|  | // In case of an RMA request, just lock up the controller. This triggers the rotation of the | 
|  | // scrambling keys. The start/stop controller takes care of initiating the internal secure wipe | 
|  | // and eventually acknowledging the RMA request. | 
|  | assign fatal_err = |{internal_fatal_err, | 
|  | mubi4_test_true_loose(fatal_escalate_en_i), | 
|  | mubi4_test_true_loose(rma_req_i)}; | 
|  |  | 
|  | assign recoverable_err_o = recoverable_err | (software_err & ~software_errs_fatal_i); | 
|  | assign mems_sec_wipe_o   = (state_d == OtbnStateLocked) & (state_q != OtbnStateLocked); | 
|  |  | 
|  | assign internal_err = software_err | internal_fatal_err; | 
|  | assign err          = software_err | recoverable_err | fatal_err; | 
|  |  | 
|  | assign prefetch_ignore_errs_o = internal_err; | 
|  |  | 
|  | // Instructions must not execute if there is an error | 
|  | assign insn_executing = insn_valid_i & ~err; | 
|  |  | 
|  | `ASSERT(ErrBitSetOnErr, | 
|  | err & (mubi4_test_false_strict(fatal_escalate_en_i) & | 
|  | mubi4_test_false_strict(recov_escalate_en_i) & | 
|  | mubi4_test_false_strict(rma_req_i)) |=> | 
|  | err_bits_o) | 
|  | `ASSERT(ErrSetOnFatalErr, fatal_err |-> err) | 
|  | `ASSERT(SoftwareErrIfNonInsnAddrSoftwareErr, non_insn_addr_software_err |-> software_err) | 
|  |  | 
|  | `ASSERT(ControllerStateValid, | 
|  | state_q inside {OtbnStateHalt, OtbnStateRun, OtbnStateStall, OtbnStateLocked}) | 
|  | // Branch only takes effect in OtbnStateRun so must not go into stall state for branch | 
|  | // instructions. | 
|  | `ASSERT(NoStallOnBranch, | 
|  | insn_valid_i & insn_dec_shared_i.branch_insn |-> state_q != OtbnStateStall) | 
|  |  | 
|  | // SEC_CM: CONTROLLER.FSM.SPARSE | 
|  | `PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, otbn_state_e, OtbnStateHalt) | 
|  |  | 
|  | // SEC_CM: CTRL_FLOW.COUNT | 
|  | assign insn_cnt_clear = state_reset_i | (state_q == OtbnStateLocked) | insn_cnt_clear_i; | 
|  |  | 
|  | always_comb begin | 
|  | if (insn_cnt_clear) begin | 
|  | insn_cnt_d = 32'd0; | 
|  | end else if (insn_executing & ~stall & (insn_cnt_q != 32'hffffffff)) begin | 
|  | insn_cnt_d = insn_cnt_q + 32'd1; | 
|  | end else begin | 
|  | insn_cnt_d = insn_cnt_q; | 
|  | end | 
|  | end | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | insn_cnt_q <= 32'd0; | 
|  | end else begin | 
|  | insn_cnt_q <= insn_cnt_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign insn_cnt_o = insn_cnt_q; | 
|  |  | 
|  | assign loop_reset = state_reset_i | sec_wipe_zero_i; | 
|  |  | 
|  | otbn_loop_controller #( | 
|  | .ImemAddrWidth(ImemAddrWidth) | 
|  | ) u_otbn_loop_controller ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  |  | 
|  | .state_reset_i(loop_reset), | 
|  |  | 
|  | .insn_valid_i, | 
|  | .insn_addr_i, | 
|  | .next_insn_addr_i(next_insn_addr), | 
|  |  | 
|  | .loop_start_req_i       (loop_start_req), | 
|  | .loop_start_commit_i    (loop_start_commit), | 
|  | .loop_bodysize_i        (loop_bodysize), | 
|  | .loop_iterations_i      (loop_iterations), | 
|  | .loop_end_addr_predec_i (ctrl_flow_target_predec_i), | 
|  |  | 
|  | .loop_jump_o     (loop_jump), | 
|  | .loop_jump_addr_o(loop_jump_addr), | 
|  |  | 
|  | .sw_err_o     (loop_sw_err), | 
|  | .hw_err_o     (loop_hw_err), | 
|  | .predec_err_o (loop_predec_err), | 
|  |  | 
|  | .jump_or_branch_i(jump_or_branch), | 
|  | .otbn_stall_i    (stall), | 
|  |  | 
|  | .prefetch_loop_active_o, | 
|  | .prefetch_loop_iterations_o, | 
|  | .prefetch_loop_end_addr_o, | 
|  | .prefetch_loop_jump_addr_o | 
|  | ); | 
|  |  | 
|  | // loop_start_req indicates the instruction wishes to start a loop, loop_start_commit confirms it | 
|  | // should occur. | 
|  | assign loop_start_req    = insn_valid_i & insn_dec_shared_i.loop_insn; | 
|  | assign loop_start_commit = insn_executing; | 
|  | assign loop_bodysize     = insn_dec_base_i.loop_bodysize; | 
|  | assign loop_iterations   = insn_dec_base_i.loop_immediate ? insn_dec_base_i.i : | 
|  | rf_base_rd_data_a_no_intg; | 
|  |  | 
|  | // Compute increments which can be optionally applied to indirect register accesses and memory | 
|  | // addresses in BN.LID/BN.SID/BN.MOVR instructions. | 
|  | assign rf_base_rd_data_a_inc           = rf_base_rd_data_a_no_intg[4:0] + 1'b1; | 
|  | assign rf_base_rd_data_b_inc           = rf_base_rd_data_b_no_intg[4:0] + 1'b1; | 
|  | // We can avoid a full 32-bit adder here because the offset is 32-bit aligned, so we know the | 
|  | // load/store address will only be valid if rf_base_rd_data_a_no_intg[4:0] is zero. | 
|  | assign rf_base_rd_data_a_wlen_word_inc = rf_base_rd_data_a_no_intg[31:5] + 27'h1; | 
|  |  | 
|  | // Choose increment to write back to base register file, only one increment can be written as | 
|  | // there is only one write port. Note that where an instruction is incrementing the indirect | 
|  | // reference to its destination register (insn_dec_bignum_i.d_inc) that reference is read on the | 
|  | // B read port so the B increment is written back. | 
|  | always_comb begin | 
|  | unique case (1'b1) | 
|  | insn_dec_bignum_i.a_inc: begin | 
|  | increment_out = {26'b0, rf_base_rd_data_a_inc}; | 
|  | end | 
|  | insn_dec_bignum_i.b_inc: begin | 
|  | increment_out = {26'b0, rf_base_rd_data_b_inc}; | 
|  | end | 
|  | insn_dec_bignum_i.d_inc: begin | 
|  | increment_out = {26'b0, rf_base_rd_data_b_inc}; | 
|  | end | 
|  | insn_dec_bignum_i.a_wlen_word_inc: begin | 
|  | increment_out = {rf_base_rd_data_a_wlen_word_inc, 5'b0}; | 
|  | end | 
|  | default: begin | 
|  | // Whenever increment_out is written back to the register file, exactly one of the | 
|  | // increment selector signals is high. To prevent the automatic inference of latches in | 
|  | // case nothing is written back (rf_wdata_sel != RfWdSelIncr) and to save logic, we choose | 
|  | // a valid output as default. | 
|  | increment_out = {26'b0, rf_base_rd_data_a_inc}; | 
|  | end | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // Base RF read/write address, enable and commit control | 
|  | always_comb begin | 
|  | rf_base_rd_addr_a_o = insn_dec_base_i.a; | 
|  | rf_base_rd_addr_b_o = insn_dec_base_i.b; | 
|  | rf_base_wr_addr_o   = insn_dec_base_i.d; | 
|  |  | 
|  | // Only commit read or write if the instruction is executing (in particular a read commit pops | 
|  | // the call stack so must not occur where a valid instruction sees an error and doesn't | 
|  | // execute). | 
|  | rf_base_rd_commit_o = insn_executing; | 
|  | rf_base_wr_commit_o = insn_executing; | 
|  |  | 
|  | rf_base_rd_en_a_raw = 1'b0; | 
|  | rf_base_rd_en_b_raw = 1'b0; | 
|  | rf_base_wr_en_raw   = 1'b0; | 
|  |  | 
|  | if (insn_valid_i) begin | 
|  | if (insn_dec_shared_i.st_insn) begin | 
|  | // For stores, both base reads happen in the first cycle of the store instruction. For base | 
|  | // stores this is the same cycle as the request. For bignum stores this is the cycle before | 
|  | // the request (as the indirect register read to get the store data occurs the following | 
|  | // cycle). | 
|  | rf_base_rd_en_a_raw = insn_dec_base_i.rf_ren_a & | 
|  | (rf_indirect_stall | (insn_dec_shared_i.subset == InsnSubsetBase)); | 
|  | rf_base_rd_en_b_raw = insn_dec_base_i.rf_ren_b & | 
|  | (rf_indirect_stall | (insn_dec_shared_i.subset == InsnSubsetBase)); | 
|  |  | 
|  | // Bignum stores can update the base register file where an increment is used. | 
|  | rf_base_wr_en_raw   = (insn_dec_shared_i.subset == InsnSubsetBignum) & | 
|  | insn_dec_base_i.rf_we                          & | 
|  | rf_indirect_stall; | 
|  | end else if (insn_dec_shared_i.ld_insn) begin | 
|  | // For loads, both base reads happen in the same cycle as the request. The address is | 
|  | // required for the request and the indirect destination register (only used for Bignum | 
|  | // loads) is flopped in ld_insn_bignum_wr_addr_q to correctly deal with the case where it's | 
|  | // updated by an increment. | 
|  | rf_base_rd_en_a_raw = insn_dec_base_i.rf_ren_a & lsu_load_req_raw; | 
|  | rf_base_rd_en_b_raw = insn_dec_base_i.rf_ren_b & lsu_load_req_raw; | 
|  |  | 
|  | if (insn_dec_shared_i.subset == InsnSubsetBignum) begin | 
|  | // Bignum loads can update the base register file where an increment is used. This must | 
|  | // always happen in the same cycle as the request as this is where both registers are | 
|  | // read. | 
|  | rf_base_wr_en_raw = insn_dec_base_i.rf_we & lsu_load_req_raw & rf_indirect_stall; | 
|  | end else begin | 
|  | // For Base loads write the base register file when the instruction is unstalled (meaning | 
|  | // the load data is available). | 
|  | rf_base_wr_en_raw = insn_dec_base_i.rf_we & ~stall; | 
|  | end | 
|  | end else if (insn_dec_bignum_i.rf_wdata_sel == RfWdSelMovSel) begin | 
|  | // For MOVR base register reads occur in the first cycle of the instruction. The indirect | 
|  | // register read for the bignum data occurs in the following cycle. | 
|  | rf_base_rd_en_a_raw = insn_dec_base_i.rf_ren_a & rf_indirect_stall; | 
|  | rf_base_rd_en_b_raw = insn_dec_base_i.rf_ren_b & rf_indirect_stall; | 
|  | rf_base_wr_en_raw   = insn_dec_base_i.rf_we    & rf_indirect_stall; | 
|  | end else begin | 
|  | // For all other instructions the read and write happen when the instruction is unstalled. | 
|  | rf_base_rd_en_a_raw = insn_dec_base_i.rf_ren_a & ~stall; | 
|  | rf_base_rd_en_b_raw = insn_dec_base_i.rf_ren_b & ~stall; | 
|  | rf_base_wr_en_raw   = insn_dec_base_i.rf_we    & ~stall; | 
|  | end | 
|  | end | 
|  |  | 
|  | if (insn_dec_shared_i.subset == InsnSubsetBignum) begin | 
|  | unique case (1'b1) | 
|  | insn_dec_bignum_i.a_inc, | 
|  | insn_dec_bignum_i.a_wlen_word_inc: begin | 
|  | rf_base_wr_addr_o = insn_dec_base_i.a; | 
|  | end | 
|  |  | 
|  | insn_dec_bignum_i.b_inc, | 
|  | insn_dec_bignum_i.d_inc: begin | 
|  | rf_base_wr_addr_o = insn_dec_base_i.b; | 
|  | end | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | rf_base_rd_en_a_o = rf_base_rd_en_a_raw & ~illegal_insn_static; | 
|  | rf_base_rd_en_b_o = rf_base_rd_en_b_raw & ~illegal_insn_static; | 
|  | rf_base_wr_en_o   = rf_base_wr_en_raw   & ~illegal_insn_static; | 
|  | end | 
|  |  | 
|  | // Base ALU Operand A MUX | 
|  | always_comb begin | 
|  | unique case (insn_dec_base_i.op_a_sel) | 
|  | OpASelRegister: alu_base_operation_o.operand_a = rf_base_rd_data_a_no_intg; | 
|  | OpASelZero:     alu_base_operation_o.operand_a = '0; | 
|  | OpASelCurrPc:   alu_base_operation_o.operand_a = {{(32 - ImemAddrWidth){1'b0}}, insn_addr_i}; | 
|  | default:        alu_base_operation_o.operand_a = rf_base_rd_data_a_no_intg; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // Base ALU Operand B MUX | 
|  | always_comb begin | 
|  | unique case (insn_dec_base_i.op_b_sel) | 
|  | OpBSelRegister:  alu_base_operation_o.operand_b = rf_base_rd_data_b_no_intg; | 
|  | OpBSelImmediate: alu_base_operation_o.operand_b = insn_dec_base_i.i; | 
|  | default:         alu_base_operation_o.operand_b = rf_base_rd_data_b_no_intg; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign alu_base_operation_o.op = insn_dec_base_i.alu_op; | 
|  |  | 
|  | assign alu_base_comparison_o.operand_a = rf_base_rd_data_a_no_intg; | 
|  | assign alu_base_comparison_o.operand_b = rf_base_rd_data_b_no_intg; | 
|  | assign alu_base_comparison_o.op = insn_dec_base_i.comparison_op; | 
|  |  | 
|  | assign rf_base_rd_data_a_no_intg = rf_base_rd_data_a_intg_i[31:0]; | 
|  | assign rf_base_rd_data_b_no_intg = rf_base_rd_data_b_intg_i[31:0]; | 
|  |  | 
|  | // TODO: For now integrity bits from RF base are ignored in the controller, remove this when end | 
|  | // to end integrity features that use them are implemented | 
|  | logic unused_rf_base_rd_a_intg_bits; | 
|  | logic unused_rf_base_rd_b_intg_bits; | 
|  |  | 
|  | assign unused_rf_base_rd_a_intg_bits = |rf_base_rd_data_a_intg_i[38:32]; | 
|  | assign unused_rf_base_rd_b_intg_bits = |rf_base_rd_data_b_intg_i[38:32]; | 
|  |  | 
|  | // Base register file write MUX. Depending on the data source, integrity bits do or don't have to | 
|  | // be appended: | 
|  | // - Data sources that require appending integrity bits go into `rf_base_wr_data_no_intg_o` and | 
|  | //   `rf_base_wr_data_intg_sel_o` is low. | 
|  | // - Data sources that already come with integrity bits go into `rf_base_wr_data_intg_o` and | 
|  | //   `rf_base_wr_data_intg_sel_o` is high. | 
|  | always_comb begin | 
|  | // Default values | 
|  | rf_base_wr_data_no_intg_o  = alu_base_operation_result_i; | 
|  | rf_base_wr_data_intg_o     = '0; | 
|  | rf_base_wr_data_intg_sel_o = 1'b0; | 
|  |  | 
|  | unique case (insn_dec_base_i.rf_wdata_sel) | 
|  | RfWdSelEx: begin | 
|  | rf_base_wr_data_no_intg_o  = alu_base_operation_result_i; | 
|  | end | 
|  | RfWdSelNextPc: begin | 
|  | rf_base_wr_data_no_intg_o  = {{(32-(ImemAddrWidth+1)){1'b0}}, next_insn_addr_wide}; | 
|  | end | 
|  | RfWdSelIspr: begin | 
|  | rf_base_wr_data_no_intg_o  = csr_rdata; | 
|  | end | 
|  | RfWdSelIncr: begin | 
|  | rf_base_wr_data_no_intg_o  = increment_out; | 
|  | end | 
|  | RfWdSelLsu: begin | 
|  | rf_base_wr_data_intg_sel_o = 1'b1; | 
|  | rf_base_wr_data_intg_o     = lsu_base_rdata_i; | 
|  | end | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | for (genvar i = 0; i < BaseWordsPerWLEN; ++i) begin : g_rf_bignum_rd_data | 
|  | assign rf_bignum_rd_data_a_no_intg[i*32+:32] = rf_bignum_rd_data_a_intg_i[i*39+:32]; | 
|  | assign rf_bignum_rd_data_b_no_intg[i*32+:32] = rf_bignum_rd_data_b_intg_i[i*39+:32]; | 
|  | end | 
|  |  | 
|  | // Bignum RF control signals from the controller aren't actually used, instead the predecoded | 
|  | // one-hot versions are. The predecoded versions get checked against the signals produced here. | 
|  | // Buffer them to ensure they don't get optimised away (with a functionaly correct OTBN they will | 
|  | // always be identical). | 
|  | assign rf_bignum_rd_addr_a_unbuf = insn_dec_bignum_i.rf_a_indirect ? insn_bignum_rd_addr_a_q : | 
|  | insn_dec_bignum_i.a; | 
|  |  | 
|  | prim_buf #( | 
|  | .Width(WdrAw) | 
|  | ) u_rf_bignum_rd_addr_a_buf ( | 
|  | .in_i (rf_bignum_rd_addr_a_unbuf), | 
|  | .out_o(rf_bignum_rd_addr_a_o) | 
|  | ); | 
|  |  | 
|  | assign rf_bignum_rd_en_a_unbuf = insn_dec_bignum_i.rf_ren_a & insn_valid_i & ~stall; | 
|  |  | 
|  | prim_buf #( | 
|  | .Width(1) | 
|  | ) u_rf_bignum_rd_en_a_buf ( | 
|  | .in_i (rf_bignum_rd_en_a_unbuf), | 
|  | .out_o(rf_bignum_rd_en_a_o) | 
|  | ); | 
|  |  | 
|  | assign rf_bignum_rd_addr_b_unbuf = insn_dec_bignum_i.rf_b_indirect ? insn_bignum_rd_addr_b_q : | 
|  | insn_dec_bignum_i.b; | 
|  |  | 
|  | prim_buf #( | 
|  | .Width(WdrAw) | 
|  | ) u_rf_bignum_rd_addr_b_buf ( | 
|  | .in_i (rf_bignum_rd_addr_b_unbuf), | 
|  | .out_o(rf_bignum_rd_addr_b_o) | 
|  | ); | 
|  |  | 
|  | assign rf_bignum_rd_en_b_unbuf = insn_dec_bignum_i.rf_ren_b & insn_valid_i & ~stall; | 
|  |  | 
|  | prim_buf #( | 
|  | .Width(1) | 
|  | ) u_rf_bignum_rd_en_b_buf ( | 
|  | .in_i (rf_bignum_rd_en_b_unbuf), | 
|  | .out_o(rf_bignum_rd_en_b_o) | 
|  | ); | 
|  |  | 
|  | assign alu_bignum_operation_o.operand_a = rf_bignum_rd_data_a_no_intg; | 
|  |  | 
|  | // Base ALU Operand B MUX | 
|  | always_comb begin | 
|  | unique case (insn_dec_bignum_i.alu_op_b_sel) | 
|  | OpBSelRegister:  alu_bignum_operation_o.operand_b = rf_bignum_rd_data_b_no_intg; | 
|  | OpBSelImmediate: alu_bignum_operation_o.operand_b = insn_dec_bignum_i.i; | 
|  | default:         alu_bignum_operation_o.operand_b = rf_bignum_rd_data_b_no_intg; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign alu_bignum_operation_o.op          = insn_dec_bignum_i.alu_op; | 
|  | assign alu_bignum_operation_o.shift_right = insn_dec_bignum_i.alu_shift_right; | 
|  | assign alu_bignum_operation_o.shift_amt   = insn_dec_bignum_i.alu_shift_amt; | 
|  | assign alu_bignum_operation_o.flag_group  = insn_dec_bignum_i.alu_flag_group; | 
|  | assign alu_bignum_operation_o.sel_flag    = insn_dec_bignum_i.alu_sel_flag; | 
|  | assign alu_bignum_operation_o.alu_flag_en = insn_dec_bignum_i.alu_flag_en & insn_valid_i; | 
|  | assign alu_bignum_operation_o.mac_flag_en = insn_dec_bignum_i.mac_flag_en & insn_valid_i; | 
|  |  | 
|  | assign alu_bignum_operation_valid_o  = insn_valid_i; | 
|  | assign alu_bignum_operation_commit_o = insn_executing; | 
|  |  | 
|  | assign mac_bignum_operation_o.operand_a         = rf_bignum_rd_data_a_no_intg; | 
|  | assign mac_bignum_operation_o.operand_b         = rf_bignum_rd_data_b_no_intg; | 
|  | assign mac_bignum_operation_o.operand_a_qw_sel  = insn_dec_bignum_i.mac_op_a_qw_sel; | 
|  | assign mac_bignum_operation_o.operand_b_qw_sel  = insn_dec_bignum_i.mac_op_b_qw_sel; | 
|  | assign mac_bignum_operation_o.wr_hw_sel_upper   = insn_dec_bignum_i.mac_wr_hw_sel_upper; | 
|  | assign mac_bignum_operation_o.pre_acc_shift_imm = insn_dec_bignum_i.mac_pre_acc_shift; | 
|  | assign mac_bignum_operation_o.zero_acc          = insn_dec_bignum_i.mac_zero_acc; | 
|  | assign mac_bignum_operation_o.shift_acc         = insn_dec_bignum_i.mac_shift_out; | 
|  |  | 
|  | assign mac_bignum_en_o     = insn_valid_i & insn_dec_bignum_i.mac_en; | 
|  | assign mac_bignum_commit_o = insn_executing; | 
|  |  | 
|  | // Move / Conditional Select. Only select B register data when a selection instruction is being | 
|  | // executed and the selection flag isn't set. | 
|  |  | 
|  | `ASSERT(SelFlagValid, insn_valid_i & insn_dec_bignum_i.sel_insn |-> | 
|  | insn_dec_bignum_i.alu_sel_flag inside {FlagC, FlagL, FlagM, FlagZ}) | 
|  |  | 
|  | assign selection_result = | 
|  | ~insn_dec_bignum_i.sel_insn | alu_bignum_selection_flag_i ? rf_bignum_rd_data_a_intg_i : | 
|  | rf_bignum_rd_data_b_intg_i; | 
|  |  | 
|  | // Bignum Register file write control | 
|  |  | 
|  | always_comb begin | 
|  | // By default write nothing | 
|  | rf_bignum_wr_en_unbuf = 2'b00; | 
|  |  | 
|  | // Only write if valid instruction wants a bignum rf write and it isn't stalled. If instruction | 
|  | // doesn't execute (e.g. due to an error) the write won't commit. | 
|  | if (insn_valid_i && insn_dec_bignum_i.rf_we && !rf_indirect_stall) begin | 
|  | if (insn_dec_bignum_i.mac_en && insn_dec_bignum_i.mac_shift_out) begin | 
|  | // Special handling for BN.MULQACC.SO, only enable upper or lower half depending on | 
|  | // mac_wr_hw_sel_upper. | 
|  | rf_bignum_wr_en_unbuf = insn_dec_bignum_i.mac_wr_hw_sel_upper ? 2'b10 : 2'b01; | 
|  | end else begin | 
|  | // For everything else write both halves immediately. | 
|  | rf_bignum_wr_en_unbuf = 2'b11; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Bignum RF control signals from the controller aren't actually used, instead the predecoded | 
|  | // one-hot versions are. The predecoded versions get checked against the signals produced here. | 
|  | // Buffer them to ensure they don't get optimised away (with a functionaly correct OTBN they will | 
|  | // always be identical). | 
|  | prim_buf #( | 
|  | .Width(2) | 
|  | ) u_bignum_wr_en_buf ( | 
|  | .in_i (rf_bignum_wr_en_unbuf), | 
|  | .out_o(rf_bignum_wr_en_o) | 
|  | ); | 
|  |  | 
|  |  | 
|  | assign rf_bignum_wr_commit_o = |rf_bignum_wr_en_o & insn_executing & !stall; | 
|  |  | 
|  | assign rf_bignum_indirect_en_o    = insn_executing & rf_indirect_stall; | 
|  | assign rf_bignum_rd_a_indirect_en = insn_executing & insn_dec_bignum_i.rf_a_indirect; | 
|  | assign rf_bignum_rd_b_indirect_en = insn_executing & insn_dec_bignum_i.rf_b_indirect; | 
|  | assign rf_bignum_wr_indirect_en   = insn_executing & insn_dec_bignum_i.rf_d_indirect; | 
|  |  | 
|  | prim_onehot_enc #( | 
|  | .OneHotWidth(NWdr) | 
|  | ) rf_bignum_rd_a_idirect_onehot__enc ( | 
|  | .in_i  (rf_base_rd_data_a_no_intg[4:0]), | 
|  | .en_i  (rf_bignum_rd_a_indirect_en), | 
|  | .out_o (rf_bignum_rd_a_indirect_onehot_o) | 
|  | ); | 
|  |  | 
|  | prim_onehot_enc #( | 
|  | .OneHotWidth(NWdr) | 
|  | ) rf_bignum_rd_b_indirect_onehot_enc ( | 
|  | .in_i  (rf_base_rd_data_b_no_intg[4:0]), | 
|  | .en_i  (rf_bignum_rd_b_indirect_en), | 
|  | .out_o (rf_bignum_rd_b_indirect_onehot_o) | 
|  | ); | 
|  |  | 
|  | prim_onehot_enc #( | 
|  | .OneHotWidth(NWdr) | 
|  | ) rf_bignum_wr_indirect_onehot_enc ( | 
|  | .in_i  (rf_base_rd_data_b_no_intg[4:0]), | 
|  | .en_i  (rf_bignum_wr_indirect_en), | 
|  | .out_o (rf_bignum_wr_indirect_onehot_o) | 
|  | ); | 
|  |  | 
|  | // For BN.LID sample the indirect destination register index in first cycle as an increment might | 
|  | // change it for the second cycle where the load data is written to the bignum register file. | 
|  | always_ff @(posedge clk_i) begin | 
|  | if (insn_dec_bignum_i.rf_d_indirect) begin | 
|  | insn_bignum_wr_addr_q <= rf_base_rd_data_b_no_intg[4:0]; | 
|  | end | 
|  |  | 
|  | if (insn_dec_bignum_i.rf_a_indirect) begin | 
|  | insn_bignum_rd_addr_a_q <= rf_base_rd_data_a_no_intg[4:0]; | 
|  | end | 
|  |  | 
|  | if (insn_dec_bignum_i.rf_b_indirect) begin | 
|  | insn_bignum_rd_addr_b_q <= rf_base_rd_data_b_no_intg[4:0]; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Bignum RF control signals from the controller aren't actually used, instead the predecoded | 
|  | // one-hot versions are. The predecoded versions get checked against the signals produced here. | 
|  | // Buffer them to ensure they don't get optimised away (with a functionaly correct OTBN they will | 
|  | // always be identical). | 
|  | assign rf_bignum_wr_addr_unbuf = insn_dec_bignum_i.rf_d_indirect ? insn_bignum_wr_addr_q : | 
|  | insn_dec_bignum_i.d; | 
|  |  | 
|  | prim_buf #( | 
|  | .Width(WdrAw) | 
|  | ) u_rf_bignum_wr_addr_buf ( | 
|  | .in_i (rf_bignum_wr_addr_unbuf), | 
|  | .out_o(rf_bignum_wr_addr_o) | 
|  | ); | 
|  |  | 
|  | // For the shift-out variant of BN.MULQACC the bottom half of the MAC result is written to one | 
|  | // half of a desintation register specified by the instruction (mac_wr_hw_sel_upper). The bottom | 
|  | // half of the MAC result must be placed in the appropriate half of the write data (the RF only | 
|  | // accepts write data for the top half in the top half of the write data input). Otherwise | 
|  | // (shift-out to bottom half and all other BN.MULQACC instructions) simply pass the MAC result | 
|  | // through unchanged as write data. | 
|  | assign mac_bignum_rf_wr_data[WLEN-1:WLEN/2] = | 
|  | insn_dec_bignum_i.mac_wr_hw_sel_upper && | 
|  | insn_dec_bignum_i.mac_shift_out          ? mac_bignum_operation_result_i[WLEN/2-1:0] : | 
|  | mac_bignum_operation_result_i[WLEN-1:WLEN/2]; | 
|  |  | 
|  | assign mac_bignum_rf_wr_data[WLEN/2-1:0] = mac_bignum_operation_result_i[WLEN/2-1:0]; | 
|  |  | 
|  | // Bignum register file write MUX. Depending on the data source, integrity bits do or don't have | 
|  | // to be appended; see comments on the "Base register file write MUX" for details. | 
|  | always_comb begin | 
|  | // Default values | 
|  | rf_bignum_wr_data_intg_sel_o = 1'b0; | 
|  | rf_bignum_wr_data_intg_o     = '0; | 
|  | rf_bignum_wr_data_no_intg_o  = alu_bignum_operation_result_i; | 
|  |  | 
|  | unique case (insn_dec_bignum_i.rf_wdata_sel) | 
|  | RfWdSelEx: begin | 
|  | rf_bignum_wr_data_no_intg_o  = alu_bignum_operation_result_i; | 
|  | end | 
|  | RfWdSelMac: begin | 
|  | rf_bignum_wr_data_no_intg_o  = mac_bignum_rf_wr_data; | 
|  | end | 
|  | RfWdSelIspr: begin | 
|  | rf_bignum_wr_data_intg_sel_o = 1'b1; | 
|  | rf_bignum_wr_data_intg_o     = ispr_rdata_intg_i; | 
|  | end | 
|  | RfWdSelMovSel: begin | 
|  | rf_bignum_wr_data_intg_sel_o = 1'b1; | 
|  | rf_bignum_wr_data_intg_o     = selection_result; | 
|  | end | 
|  | RfWdSelLsu: begin | 
|  | rf_bignum_wr_data_intg_sel_o = 1'b1; | 
|  | //SEC_CM: BUS.INTEGRITY | 
|  | rf_bignum_wr_data_intg_o     = lsu_bignum_rdata_i; | 
|  | end | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign rf_a_indirect_err = insn_dec_bignum_i.rf_a_indirect    & | 
|  | (|rf_base_rd_data_a_no_intg[31:5]) & | 
|  | ~rf_base_call_stack_sw_err_i       & | 
|  | rf_base_rd_en_a_o; | 
|  |  | 
|  | assign rf_b_indirect_err = insn_dec_bignum_i.rf_b_indirect    & | 
|  | (|rf_base_rd_data_b_no_intg[31:5]) & | 
|  | ~rf_base_call_stack_sw_err_i       & | 
|  | rf_base_rd_en_b_o; | 
|  |  | 
|  | assign rf_d_indirect_err = insn_dec_bignum_i.rf_d_indirect    & | 
|  | (|rf_base_rd_data_b_no_intg[31:5]) & | 
|  | ~rf_base_call_stack_sw_err_i       & | 
|  | rf_base_rd_en_b_o; | 
|  |  | 
|  | assign rf_indirect_err = | 
|  | insn_valid_i & (rf_a_indirect_err | rf_b_indirect_err | rf_d_indirect_err); | 
|  |  | 
|  |  | 
|  | // If the source registers are indirectly indexed and there is a stack error, the source | 
|  | // register indices were illegal due to a stack pop error. In this case, ignore bignum RF read | 
|  | // integrity errors. | 
|  | assign ignore_rf_bignum_intg_errs = (insn_dec_bignum_i.rf_a_indirect | | 
|  | insn_dec_bignum_i.rf_b_indirect) & | 
|  | rf_base_call_stack_sw_err_i; | 
|  |  | 
|  | assign rf_bignum_intg_err = rf_bignum_intg_err_i & ~ignore_rf_bignum_intg_errs; | 
|  |  | 
|  | // If the destination register is indirectly indexed and there is a stack error, the destination | 
|  | // register index was illegal due to a stack pop error. In this case, ignore bignum RF | 
|  | // write-enable errors. | 
|  | assign ignore_rf_bignum_spurious_we_errs = insn_dec_bignum_i.rf_d_indirect & | 
|  | rf_base_call_stack_sw_err_i; | 
|  |  | 
|  | assign rf_bignum_spurious_we_err = rf_bignum_spurious_we_err_i & | 
|  | ~ignore_rf_bignum_spurious_we_errs; | 
|  |  | 
|  | // CSR/WSR/ISPR handling | 
|  | // ISPRs (Internal Special Purpose Registers) are the internal registers. CSRs and WSRs are the | 
|  | // ISA visible versions of those registers in the base and bignum ISAs respectively. | 
|  |  | 
|  | assign csr_addr     = csr_e'(insn_dec_base_i.i[11:0]); | 
|  | assign csr_sub_addr = insn_dec_base_i.i[$clog2(BaseWordsPerWLEN)-1:0]; | 
|  |  | 
|  | always_comb begin | 
|  | ispr_addr_base      = IsprMod; | 
|  | ispr_word_addr_base = '0; | 
|  | csr_illegal_addr    = 1'b0; | 
|  |  | 
|  | unique case (csr_addr) | 
|  | CsrFlags, CsrFg0, CsrFg1: begin | 
|  | ispr_addr_base      = IsprFlags; | 
|  | ispr_word_addr_base = '0; | 
|  | end | 
|  | CsrMod0, CsrMod1, CsrMod2, CsrMod3, CsrMod4, CsrMod5, CsrMod6, CsrMod7: begin | 
|  | ispr_addr_base      = IsprMod; | 
|  | ispr_word_addr_base = csr_sub_addr; | 
|  | end | 
|  | CsrRndPrefetch: begin | 
|  | // Reading from RND_PREFETCH results in 0, there is no ISPR to read so no address is set. | 
|  | // The csr_rdata mux logic takes care of producing the 0. | 
|  | end | 
|  | CsrRnd: begin | 
|  | ispr_addr_base      = IsprRnd; | 
|  | ispr_word_addr_base = '0; | 
|  | end | 
|  | CsrUrnd: begin | 
|  | ispr_addr_base      = IsprUrnd; | 
|  | ispr_word_addr_base = '0; | 
|  | end | 
|  | default: csr_illegal_addr = 1'b1; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_ispr_word_sel_base | 
|  | assign ispr_word_sel_base[i_word] = ispr_word_addr_base == i_word; | 
|  | end | 
|  |  | 
|  | // Decode wide ISPR read data. | 
|  | logic [WLEN-1:0]                ispr_rdata; | 
|  | logic [2*BaseWordsPerWLEN-1:0]  ispr_rdata_intg_err_wide; | 
|  | logic [BaseWordsPerWLEN-1:0]    ispr_rdata_intg_err_narrow; | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_ispr_rdata_dec | 
|  | prim_secded_inv_39_32_dec i_secded_dec ( | 
|  | .data_i     (ispr_rdata_intg_i[i_word*39+:39]), | 
|  | .data_o     (/* unused because we abort on any integrity error */), | 
|  | .syndrome_o (/* unused */), | 
|  | .err_o      (ispr_rdata_intg_err_wide[i_word*2+:2]) | 
|  | ); | 
|  | assign ispr_rdata[i_word*32+:32] = ispr_rdata_intg_i[i_word*39+:32]; | 
|  | assign ispr_rdata_intg_err_narrow[i_word] = |(ispr_rdata_intg_err_wide[i_word*2+:2]); | 
|  | end | 
|  |  | 
|  | // Propagate integrity error only if wide ISPR is used. | 
|  |  | 
|  | // Handle ISPR integrity error detection. We've got a bitmask of ISPR words that failed their | 
|  | // integrity check (ispr_rdata_intg_err_narrow), but a nonzero entry may not be a problem if we | 
|  | // don't actually use the data. | 
|  | // | 
|  | // The situations when the data is actually used are: | 
|  | // | 
|  | //   (1) This is a bignum instruction that writes back to the bignum register file by reading an | 
|  | //       ISPR. In this case, we actually pass the data through with integrity bits, but it | 
|  | //       shouldn't hurt to add fault detection at this point. | 
|  | // | 
|  | //   (2) This instruction consumes the data by selecting a word from an ISPR and then writing it | 
|  | //       back. This happens for things like CSRRS instructions, where the data flows to the base | 
|  | //       register file through rf_base_wr_data_no_intg_o and back to the ISPR through | 
|  | //       ispr_base_wdata_o. The word used is given by the onehot ispr_word_sel_base mask. | 
|  | // | 
|  | // In both cases, there's a special case for the RND_PREFETCH register, which doesn't actually | 
|  | // have any backing data. It reads as zero with invalid integrity bits which we want to ignore. | 
|  |  | 
|  | // Are we reading all the ISPR data? (case (1) above) | 
|  | logic all_ispr_words_used; | 
|  | assign all_ispr_words_used = (insn_dec_bignum_i.rf_wdata_sel == RfWdSelIspr); | 
|  |  | 
|  | // Are we reading just one word of the ISPR data? (case (2) above). | 
|  | logic one_ispr_word_used; | 
|  | assign one_ispr_word_used = ispr_rd_insn & (insn_dec_shared_i.subset == InsnSubsetBase); | 
|  |  | 
|  | // A bit-mask giving which ISPR words are being read | 
|  | logic [BaseWordsPerWLEN-1:0] ispr_read_mask; | 
|  | assign ispr_read_mask = all_ispr_words_used ? '1 : | 
|  | one_ispr_word_used  ? ispr_word_sel_base : '0; | 
|  |  | 
|  | // Use ispr_read_mask to qualify the error bit-mask that came out of the integrity decoder. | 
|  | logic [BaseWordsPerWLEN-1:0] ispr_rdata_used_intg_err; | 
|  | assign ispr_rdata_used_intg_err = ispr_read_mask & ispr_rdata_intg_err_narrow; | 
|  |  | 
|  | // We only architecturally read the ISPR when there's a non-stalled instruction. This is also the | 
|  | // place where we factor in the special RND_PREFETCH behaviour. We also need to squash any | 
|  | // integrity errors if we're reading a sideload key which isn't currently valid (this will | 
|  | // generate a key_invalid error, but we shouldn't have any behaviour that depends on what happens | 
|  | // to be on the pins) | 
|  | logic non_prefetch_insn_running; | 
|  | assign non_prefetch_insn_running = (insn_valid_i & ~stall & | 
|  | (csr_addr != CsrRndPrefetch) & ~key_invalid); | 
|  |  | 
|  | assign ispr_rdata_intg_err = non_prefetch_insn_running & |(ispr_rdata_used_intg_err); | 
|  |  | 
|  | `ASSERT_KNOWN(IsprRdataIntgErrKnown_A, ispr_rdata_intg_err) | 
|  |  | 
|  | for (genvar i_bit = 0; i_bit < 32; i_bit++) begin : g_csr_rdata_mux | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_csr_rdata_mux_inner | 
|  | assign csr_rdata_mux[i_bit][i_word] = | 
|  | ispr_rdata[i_word*32 + i_bit] & ispr_word_sel_base[i_word]; | 
|  | end | 
|  |  | 
|  | assign csr_rdata_raw[i_bit] = |csr_rdata_mux[i_bit]; | 
|  | end | 
|  |  | 
|  | // Specialised read data handling for CSR reads where raw read data needs modification. | 
|  | always_comb begin | 
|  | csr_rdata = csr_rdata_raw; | 
|  |  | 
|  | unique case (csr_addr) | 
|  | // For FG0/FG1 select out appropriate bits from FLAGS ISPR and pad the rest with zeros. | 
|  | CsrFg0:         csr_rdata = {28'b0, csr_rdata_raw[3:0]}; | 
|  | CsrFg1:         csr_rdata = {28'b0, csr_rdata_raw[7:4]}; | 
|  | CsrRndPrefetch: csr_rdata = '0; | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign csr_wdata_raw = insn_dec_shared_i.ispr_rs_insn ? csr_rdata | rf_base_rd_data_a_no_intg : | 
|  | rf_base_rd_data_a_no_intg; | 
|  |  | 
|  | // Specialised write data handling for CSR writes where raw write data needs modification. | 
|  | always_comb begin | 
|  | csr_wdata = csr_wdata_raw; | 
|  |  | 
|  | unique case (csr_addr) | 
|  | // For FG0/FG1 only modify relevant part of FLAGS ISPR. | 
|  | CsrFg0: csr_wdata = {24'b0, csr_rdata_raw[7:4], csr_wdata_raw[3:0]}; | 
|  | CsrFg1: csr_wdata = {24'b0, csr_wdata_raw[3:0], csr_rdata_raw[3:0]}; | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // ISPR RS (read and set) must not be combined with ISPR RD or WR (read or write). ISPR RD and | 
|  | // WR (read and write) is allowed. | 
|  | `ASSERT(NoIsprRorWAndRs, insn_valid_i |-> ~(insn_dec_shared_i.ispr_rs_insn   & | 
|  | (insn_dec_shared_i.ispr_rd_insn | | 
|  | insn_dec_shared_i.ispr_wr_insn))) | 
|  |  | 
|  |  | 
|  | assign wsr_addr = wsr_e'(insn_dec_bignum_i.i[WsrNumWidth-1:0]); | 
|  |  | 
|  | always_comb begin | 
|  | ispr_addr_bignum = IsprMod; | 
|  | wsr_illegal_addr = 1'b0; | 
|  | key_invalid      = 1'b0; | 
|  |  | 
|  | unique case (wsr_addr) | 
|  | WsrMod:  ispr_addr_bignum = IsprMod; | 
|  | WsrRnd:  ispr_addr_bignum = IsprRnd; | 
|  | WsrUrnd: ispr_addr_bignum = IsprUrnd; | 
|  | WsrAcc:  ispr_addr_bignum = IsprAcc; | 
|  | WsrKeyS0L: begin | 
|  | ispr_addr_bignum = IsprKeyS0L; | 
|  | key_invalid = ~sideload_key_shares_valid_i[0]; | 
|  | end | 
|  | WsrKeyS0H: begin | 
|  | ispr_addr_bignum = IsprKeyS0H; | 
|  | key_invalid = ~sideload_key_shares_valid_i[0]; | 
|  | end | 
|  | WsrKeyS1L: begin | 
|  | ispr_addr_bignum = IsprKeyS1L; | 
|  | key_invalid = ~sideload_key_shares_valid_i[1]; | 
|  | end | 
|  | WsrKeyS1H: begin | 
|  | ispr_addr_bignum = IsprKeyS1H; | 
|  | key_invalid = ~sideload_key_shares_valid_i[1]; | 
|  | end | 
|  | default: wsr_illegal_addr = 1'b1; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign wsr_wdata = insn_dec_shared_i.ispr_rs_insn ? ispr_rdata | rf_bignum_rd_data_a_no_intg : | 
|  | rf_bignum_rd_data_a_no_intg; | 
|  |  | 
|  | assign ispr_illegal_addr = insn_dec_shared_i.subset == InsnSubsetBase ? csr_illegal_addr : | 
|  | wsr_illegal_addr; | 
|  |  | 
|  | assign ispr_err = ispr_illegal_addr & insn_valid_i & (insn_dec_shared_i.ispr_rd_insn | | 
|  | insn_dec_shared_i.ispr_wr_insn | | 
|  | insn_dec_shared_i.ispr_rs_insn); | 
|  |  | 
|  | assign ispr_wr_insn = insn_dec_shared_i.ispr_wr_insn | insn_dec_shared_i.ispr_rs_insn; | 
|  | assign ispr_rd_insn = insn_dec_shared_i.ispr_rd_insn | insn_dec_shared_i.ispr_rs_insn; | 
|  |  | 
|  | // Write to RND_PREFETCH must not produce ISR write | 
|  | assign ispr_wr_base_insn = | 
|  | ispr_wr_insn & (insn_dec_shared_i.subset == InsnSubsetBase) & (csr_addr != CsrRndPrefetch); | 
|  |  | 
|  | assign ispr_wr_bignum_insn = ispr_wr_insn & (insn_dec_shared_i.subset == InsnSubsetBignum); | 
|  | assign ispr_rd_bignum_insn = ispr_rd_insn & (insn_dec_shared_i.subset == InsnSubsetBignum); | 
|  |  | 
|  | assign ispr_addr_o         = insn_dec_shared_i.subset == InsnSubsetBase ? ispr_addr_base : | 
|  | ispr_addr_bignum; | 
|  | assign ispr_base_wdata_o   = csr_wdata; | 
|  | assign ispr_base_wr_en_o   = {BaseWordsPerWLEN{ispr_wr_base_insn & insn_valid_i}} & | 
|  | ispr_word_sel_base; | 
|  |  | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_ispr_bignum_wdata_enc | 
|  | prim_secded_inv_39_32_enc i_secded_enc ( | 
|  | .data_i(wsr_wdata[i_word*32+:32]), | 
|  | .data_o(ispr_bignum_wdata_intg_o[i_word*39+:39]) | 
|  | ); | 
|  | end | 
|  | assign ispr_bignum_wr_en_o = ispr_wr_bignum_insn & insn_valid_i; | 
|  |  | 
|  | assign ispr_wr_commit_o = ispr_wr_insn & insn_executing; | 
|  | assign ispr_rd_en_o     = ispr_rd_insn & insn_valid_i & | 
|  | ~((insn_dec_shared_i.subset == InsnSubsetBase) & (csr_addr == CsrRndPrefetch)); | 
|  |  | 
|  | // For BN.SID the LSU address is computed in the first cycle by the base ALU. The store request | 
|  | // itself occurs in the second cycle when the store data is available (from the indirect register | 
|  | // read). The calculated address is saved in a flop here so it's available for use in the second | 
|  | // cycle. | 
|  | assign lsu_addr_saved_d = alu_base_operation_result_i[DmemAddrWidth-1:0]; | 
|  | always_ff @(posedge clk_i) begin | 
|  | lsu_addr_saved_q <= lsu_addr_saved_d; | 
|  | end | 
|  |  | 
|  | //assign expected_lsu_addr_en_predec = insn_valid & insn_dec_shared_i.ld_insn | 
|  |  | 
|  | // lsu_load_req_raw/lsu_store_req_raw indicate an instruction wishes to perform a store or a load. | 
|  | // lsu_load_req_o/lsu_store_req_o factor in whether an instruction is actually executing (it may | 
|  | // be suppressed due an error) and command the load or store to happen when asserted. | 
|  | assign lsu_load_req_raw = insn_valid_i & insn_dec_shared_i.ld_insn & (state_q == OtbnStateRun); | 
|  | assign lsu_load_req_o   = insn_executing & lsu_load_req_raw; | 
|  |  | 
|  | assign lsu_store_req_raw = insn_valid_i & insn_dec_shared_i.st_insn & ~rf_indirect_stall; | 
|  | assign lsu_store_req_o   = insn_executing & lsu_store_req_raw; | 
|  |  | 
|  | assign lsu_req_subset_o = insn_dec_shared_i.subset; | 
|  |  | 
|  | // To simplify blanking logic all two cycle memory operations (BN.LID, BN.SID, LW) present the | 
|  | // calculated address in their first cycle and the saved address in the second cycle. This results | 
|  | // in lsu_addr_o remaining stable for the entire instruction. Only SW is a single cycle | 
|  | // instruction so it only presents the calculated address. The stability property is checked by an | 
|  | // assertion. | 
|  | assign lsu_addr_saved_sel = | 
|  | insn_valid_i & ((insn_dec_shared_i.subset == InsnSubsetBignum) || | 
|  | insn_dec_shared_i.ld_insn                         ? ~stall : 1'b0); | 
|  |  | 
|  | assign lsu_addr = lsu_addr_saved_sel ? lsu_addr_saved_q                                : | 
|  | alu_base_operation_result_i[DmemAddrWidth-1:0]; | 
|  |  | 
|  | // SEC_CM: CTRL.REDUN | 
|  | assign expected_lsu_addr_en = | 
|  | insn_valid_i & (insn_dec_shared_i.ld_insn | insn_dec_shared_i.st_insn); | 
|  |  | 
|  | assign lsu_predec_error = expected_lsu_addr_en != lsu_addr_en_predec_i; | 
|  |  | 
|  | assign expected_call_stack_push = | 
|  | insn_valid_i & insn_dec_base_i.rf_we & rf_base_wr_addr_o == 5'd1; | 
|  |  | 
|  | assign expected_call_stack_pop = insn_valid_i & | 
|  | ((insn_dec_base_i.rf_ren_a & rf_base_rd_addr_a_o == 5'd1) | | 
|  | (insn_dec_base_i.rf_ren_b & rf_base_rd_addr_b_o == 5'd1)); | 
|  |  | 
|  | // Check branch target against the precalculated target from pre-decode. Pre-decode cannot | 
|  | // calculate the jump target of a JALR as it requires a register read so this is excluded from the | 
|  | // check (by looking at the ALU op a selection). | 
|  | assign branch_target_predec_error = | 
|  | insn_dec_shared_i.branch_insn                                            & | 
|  | insn_dec_shared_i.jump_insn & insn_dec_base_i.op_a_sel != OpASelRegister & | 
|  | (ctrl_flow_target_predec_i != branch_target); | 
|  |  | 
|  | assign ctrl_predec_error = | 
|  | |{ctrl_flow_predec_i.jump_insn       != (insn_dec_shared_i.jump_insn   & insn_valid_i), | 
|  | ctrl_flow_predec_i.loop_insn       != (insn_dec_shared_i.loop_insn   & insn_valid_i), | 
|  | ctrl_flow_predec_i.branch_insn     != (insn_dec_shared_i.branch_insn & insn_valid_i), | 
|  | ctrl_flow_predec_i.call_stack_push != expected_call_stack_push, | 
|  | ctrl_flow_predec_i.call_stack_pop  != expected_call_stack_pop, | 
|  | branch_target_predec_error, | 
|  | loop_predec_err}; | 
|  |  | 
|  | assign predec_error_o = lsu_predec_error | ctrl_predec_error; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(DmemAddrWidth)) u_lsu_addr_blanker ( | 
|  | .in_i (lsu_addr), | 
|  | .en_i (lsu_addr_en_predec_i), | 
|  | .out_o(lsu_addr_blanked) | 
|  | ); | 
|  |  | 
|  | // Check stability property described above (see the lsu_addr_saved_sel signal) holds. | 
|  | `ASSERT(LsuAddrBlankedStable_A, insn_valid_i & stall & ~err |=> $stable(lsu_addr_blanked)) | 
|  |  | 
|  | assign lsu_addr_o = lsu_addr_blanked; | 
|  |  | 
|  | assign lsu_base_wdata_o   = rf_base_rd_data_b_intg_i; | 
|  | assign lsu_bignum_wdata_o = rf_bignum_rd_data_b_intg_i; | 
|  |  | 
|  | assign dmem_addr_unaligned_bignum = | 
|  | (lsu_req_subset_o == InsnSubsetBignum) & (|lsu_addr_o[$clog2(WLEN/8)-1:0]); | 
|  | assign dmem_addr_unaligned_base   = | 
|  | (lsu_req_subset_o == InsnSubsetBase)   & (|lsu_addr_o[1:0]); | 
|  | assign dmem_addr_overflow         = |alu_base_operation_result_i[31:DmemAddrWidth]; | 
|  |  | 
|  | // A dmem address is checked the cycle it is available. For bignum stores this is the first cycle | 
|  | // where the base register file read occurs, with the store request occurring the following cycle. | 
|  | // For all other loads and stores the dmem address is available the same cycle as the request. | 
|  | assign dmem_addr_err_check = | 
|  | (lsu_req_subset_o == InsnSubsetBignum) & | 
|  | insn_dec_shared_i.st_insn               ? rf_indirect_stall : | 
|  | lsu_load_req_raw | lsu_store_req_raw; | 
|  |  | 
|  | assign dmem_addr_err = | 
|  | insn_valid_i & dmem_addr_err_check & (dmem_addr_overflow         | | 
|  | dmem_addr_unaligned_bignum | | 
|  | dmem_addr_unaligned_base); | 
|  |  | 
|  | assign rnd_req_raw = insn_valid_i & ispr_rd_insn & (ispr_addr_o == IsprRnd); | 
|  | // Don't factor rnd_rep/fips_err_i into rnd_req_o. This would lead to a combo loop. | 
|  | assign rnd_req_o = rnd_req_raw & insn_valid_i & ~(software_err | fatal_err); | 
|  |  | 
|  | assign rnd_prefetch_req_o = insn_executing & ispr_wr_insn & | 
|  | (insn_dec_shared_i.subset == InsnSubsetBase) & (csr_addr == CsrRndPrefetch); | 
|  | endmodule |