[otbn] Changes to indirect register usage and increments * If a value greater than 31 is used for an indirect register reference an `illegal_insn` error is raised. * Increment no longer wraps Fixes #4808 Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv index c0465c6..8296ccd 100644 --- a/hw/ip/otbn/rtl/otbn_controller.sv +++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -151,8 +151,8 @@ // Computed increments for indirect register index and memory address in BN.LID/BN.SID/BN.MOVR // instructions. - logic [4:0] rf_base_rd_data_a_inc; - logic [4:0] rf_base_rd_data_b_inc; + 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; // Output of mux taking the above increments as inputs and choosing one to write back to base @@ -176,6 +176,8 @@ logic imem_addr_err, loop_err, ispr_err; logic dmem_addr_err, dmem_addr_unaligned_base, dmem_addr_unaligned_bignum, dmem_addr_overflow; + logic rf_a_indirect_err, rf_b_indirect_err, rf_d_indirect_err, rf_indirect_err; + // 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. // TODO: Possibility of error response on store? Probably still don't need to stall in that case @@ -285,7 +287,7 @@ assign err_bits_o.fatal_reg = 1'b0; assign err_bits_o.fatal_imem = insn_fetch_err_i; assign err_bits_o.fatal_dmem = lsu_rdata_err_i; - assign err_bits_o.illegal_insn = insn_illegal_i | ispr_err; + assign err_bits_o.illegal_insn = insn_illegal_i | ispr_err | rf_indirect_err; assign err_bits_o.bad_data_addr = dmem_addr_err; assign err_bits_o.loop = loop_err; assign err_bits_o.call_stack = rf_base_call_stack_err_i; @@ -358,13 +360,13 @@ always_comb begin unique case (1'b1) insn_dec_bignum_i.a_inc: begin - increment_out = {27'b0, rf_base_rd_data_a_inc}; + increment_out = {26'b0, rf_base_rd_data_a_inc}; end insn_dec_bignum_i.b_inc: begin - increment_out = {27'b0, rf_base_rd_data_b_inc}; + increment_out = {26'b0, rf_base_rd_data_b_inc}; end insn_dec_bignum_i.d_inc: begin - increment_out = {27'b0, rf_base_rd_data_b_inc}; + 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}; @@ -374,7 +376,7 @@ // 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 = {27'b0, rf_base_rd_data_a_inc}; + increment_out = {26'b0, rf_base_rd_data_a_inc}; end endcase end @@ -433,7 +435,7 @@ // only available in the stall state. assign rf_base_wr_en_o = insn_valid_i & insn_dec_base_i.rf_we & ~(insn_dec_shared_i.ld_insn & (state_q != OtbnStateStall)); - assign rf_base_wr_commit_o = insn_executing; + assign rf_base_wr_commit_o = insn_executing & ~err; always_comb begin unique case (insn_dec_base_i.rf_wdata_sel) @@ -489,8 +491,8 @@ // By default write nothing rf_bignum_wr_en_o = 2'b00; - // Only write if executing instruction wants a bignum rf write - if (insn_executing && insn_dec_bignum_i.rf_we) begin + // Only write if executing instruction wants a bignum rf write and there is no error + if (insn_executing && insn_dec_bignum_i.rf_we && !err) 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. @@ -513,6 +515,7 @@ assign rf_bignum_wr_addr_o = insn_dec_bignum_i.rf_d_indirect ? rf_base_rd_data_b_i[4:0] : insn_dec_bignum_i.d; + // 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 @@ -536,6 +539,13 @@ endcase end + assign rf_a_indirect_err = insn_dec_bignum_i.rf_a_indirect & (|rf_base_rd_data_a_i[31:5]); + assign rf_b_indirect_err = insn_dec_bignum_i.rf_b_indirect & (|rf_base_rd_data_b_i[31:5]); + assign rf_d_indirect_err = insn_dec_bignum_i.rf_d_indirect & (|rf_base_rd_data_b_i[31:5]); + + assign rf_indirect_err = + insn_valid_i & (rf_a_indirect_err | rf_b_indirect_err | rf_d_indirect_err); + // 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.