|  | // 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 alu block for the bignum instruction subset | 
|  | * | 
|  | * This ALU supports all of the 'plain' arithmetic and logic bignum instructions, BN.MULQACC is | 
|  | * implemented in a separate block. | 
|  | * | 
|  | * One barrel shifter and two adders (X and Y) are implemented along with the logic operators | 
|  | * (AND,OR,XOR,NOT). | 
|  | * | 
|  | * The adders have 256-bit operands with a carry_in and optional invert on the second operand. This | 
|  | * can be used to implement subtraction (a - b == a + ~b + 1). BN.SUBB/BN.ADDC are implemented by | 
|  | * feeding in the carry flag as carry in rather than a fixed 0 or 1. | 
|  | * | 
|  | * The shifter takes a 512-bit input (to implement BN.RSHI, concatenate and right shift) and shifts | 
|  | * right by up to 256-bits. The lower (256-bit) half of the input and output can be reversed to | 
|  | * allow left shift implementation.  There is no concatenate and left shift instruction so reversing | 
|  | * isn't required over the full width. | 
|  | * | 
|  | * The dataflow between the adders and shifter is in the diagram below. This arrangement allows the | 
|  | * implementation of the pseudo-mod (BN.ADDM/BN.SUBM) instructions in a single cycle whilst | 
|  | * minimising the critical path. The pseudo-mod instructions do not have a shifted input so X can | 
|  | * compute the initial add/sub and Y computes the pseudo-mod result. For all other add/sub | 
|  | * operations Y computes the operation with one of the inputs supplied by the shifter and the other | 
|  | * from operand_a. | 
|  | * | 
|  | * Both adder X and the shifter get supplied with operand_a and operand_b from the operation_i | 
|  | * input. In addition the shifter gets a shift amount (shift_amt) and can use 0 instead of | 
|  | * operand_a. The shifter concatenates operand_a (or 0) and operand_b together before shifting with | 
|  | * operand_a in the upper (256-bit) half {operand_a/0, operand_b}. This allows the shifter to pass | 
|  | * through operand_b simply by not performing a shift. | 
|  | * | 
|  | * Blanking is employed on the ALU data paths. This holds unused data paths to 0 to reduce side | 
|  | * channel leakage. The lower-case 'b' on the digram below indicates points in the data path that | 
|  | * get blanked. Note that Adder X is never used in isolation, it is always combined with Adder Y so | 
|  | * there is no need for blanking between Adder X and Adder Y. | 
|  | * | 
|  | *      A       B       A   B | 
|  | *      |       |       |   | | 
|  | *      b       b       b   b   shift_amt | 
|  | *      |       |       |   |   | | 
|  | *    +-----------+   +-----------+ | 
|  | *    |  Adder X  |   |  Shifter  | | 
|  | *    +-----------+   +-----------+ | 
|  | *          |               | | 
|  | *          |----+     +----| | 
|  | *          |    |     |    | | 
|  | *      X result |     | Shifter result | 
|  | *               |     | | 
|  | *             A |     | | 
|  | *             | |     |     +-----------+ | 
|  | *             b |     b +---|  MOD WSR  | | 
|  | *             | |     | |   +-----------+ | 
|  | *           \-----/ \-----/ | 
|  | *            \---/   \---/ | 
|  | *              |       | | 
|  | *              |       | | 
|  | *            +-----------+ | 
|  | *            |  Adder Y  | | 
|  | *            +-----------+ | 
|  | *                  | | 
|  | *              Y result | 
|  | */ | 
|  |  | 
|  |  | 
|  | module otbn_alu_bignum | 
|  | import otbn_pkg::*; | 
|  | ( | 
|  | input logic clk_i, | 
|  | input logic rst_ni, | 
|  |  | 
|  | input  alu_bignum_operation_t operation_i, | 
|  | input  logic                  operation_valid_i, | 
|  | input  logic                  operation_commit_i, | 
|  | output logic [WLEN-1:0]       operation_result_o, | 
|  | output logic                  selection_flag_o, | 
|  |  | 
|  | input  alu_predec_bignum_t  alu_predec_bignum_i, | 
|  | input  ispr_predec_bignum_t ispr_predec_bignum_i, | 
|  |  | 
|  | input  ispr_e                       ispr_addr_i, | 
|  | input  logic [31:0]                 ispr_base_wdata_i, | 
|  | input  logic [BaseWordsPerWLEN-1:0] ispr_base_wr_en_i, | 
|  | input  logic [ExtWLEN-1:0]          ispr_bignum_wdata_intg_i, | 
|  | input  logic                        ispr_bignum_wr_en_i, | 
|  | input  logic                        ispr_wr_commit_i, | 
|  | input  logic                        ispr_init_i, | 
|  | output logic [ExtWLEN-1:0]          ispr_rdata_intg_o, | 
|  | input  logic                        ispr_rd_en_i, | 
|  |  | 
|  | input  logic [ExtWLEN-1:0]          ispr_acc_intg_i, | 
|  | output logic [ExtWLEN-1:0]          ispr_acc_wr_data_intg_o, | 
|  | output logic                        ispr_acc_wr_en_o, | 
|  |  | 
|  | output logic                        reg_intg_violation_err_o, | 
|  |  | 
|  | input logic                         sec_wipe_mod_urnd_i, | 
|  | input logic                         sec_wipe_zero_i, | 
|  |  | 
|  | input  flags_t                      mac_operation_flags_i, | 
|  | input  flags_t                      mac_operation_flags_en_i, | 
|  |  | 
|  | input  logic [WLEN-1:0]             rnd_data_i, | 
|  | input  logic [WLEN-1:0]             urnd_data_i, | 
|  |  | 
|  | input  logic [1:0][SideloadKeyWidth-1:0] sideload_key_shares_i, | 
|  |  | 
|  | output logic alu_predec_error_o, | 
|  | output logic ispr_predec_error_o | 
|  | ); | 
|  | /////////// | 
|  | // ISPRs // | 
|  | /////////// | 
|  |  | 
|  | flags_t                              flags_q [NFlagGroups]; | 
|  | flags_t                              flags_d [NFlagGroups]; | 
|  | logic   [NFlagGroups*FlagsWidth-1:0] flags_flattened; | 
|  | logic   [NFlagGroups-1:0]            flags_en; | 
|  | logic   [NFlagGroups-1:0]            is_operation_flag_group; | 
|  | flags_t                              selected_flags; | 
|  | flags_t                              adder_update_flags; | 
|  | logic                                adder_update_flags_en, adder_update_flags_en_raw; | 
|  | flags_t                              logic_update_flags; | 
|  | logic                                logic_update_flags_en, logic_update_flags_en_raw; | 
|  | flags_t                              mac_update_flags; | 
|  | logic                                mac_update_flags_en; | 
|  | logic                                ispr_update_flags_en; | 
|  |  | 
|  | logic [NIspr-1:0] expected_ispr_rd_en_onehot; | 
|  | logic [NIspr-1:0] expected_ispr_wr_en_onehot; | 
|  | logic             ispr_wr_en; | 
|  |  | 
|  | assign adder_update_flags_en = operation_i.alu_flag_en   & | 
|  | operation_commit_i        & | 
|  | adder_update_flags_en_raw; | 
|  |  | 
|  | assign logic_update_flags_en = operation_i.alu_flag_en   & | 
|  | operation_commit_i        & | 
|  | logic_update_flags_en_raw; | 
|  |  | 
|  | assign mac_update_flags_en   = operation_i.mac_flag_en & operation_commit_i; | 
|  |  | 
|  | assign ispr_update_flags_en  = ispr_base_wr_en_i[0]       & | 
|  | ispr_wr_commit_i           & | 
|  | (ispr_addr_i == IsprFlags); | 
|  |  | 
|  | `ASSERT(UpdateFlagsOnehot, $onehot0({ispr_init_i, adder_update_flags_en, logic_update_flags_en, | 
|  | mac_update_flags_en, ispr_update_flags_en})) | 
|  |  | 
|  | assign selected_flags = flags_q[operation_i.flag_group]; | 
|  |  | 
|  | assign mac_update_flags = (selected_flags        & ~mac_operation_flags_en_i) | | 
|  | (mac_operation_flags_i &  mac_operation_flags_en_i); | 
|  |  | 
|  | for (genvar i_fg = 0; i_fg < NFlagGroups; i_fg++) begin : g_flag_groups | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | flags_q[i_fg] <= '{Z : 1'b0, L : 1'b0, M : 1'b0, C : 1'b0}; | 
|  | end else if (flags_en[i_fg]) begin | 
|  | flags_q[i_fg] <= flags_d[i_fg]; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign is_operation_flag_group[i_fg] = operation_i.flag_group == i_fg; | 
|  |  | 
|  | assign flags_flattened[i_fg*FlagsWidth+:FlagsWidth] = flags_q[i_fg]; | 
|  |  | 
|  | // Flag updates can come from the Y adder result, the logical operation result or from an ISPR | 
|  | // write. | 
|  | always_comb begin | 
|  | flags_d[i_fg] = adder_update_flags; | 
|  |  | 
|  | unique case (1'b1) | 
|  | ispr_init_i:           flags_d[i_fg] = '0; | 
|  | adder_update_flags_en: flags_d[i_fg] = adder_update_flags; | 
|  | logic_update_flags_en: flags_d[i_fg] = logic_update_flags; | 
|  | mac_update_flags_en:   flags_d[i_fg] = mac_update_flags; | 
|  | ispr_update_flags_en:  flags_d[i_fg] = ispr_base_wdata_i[i_fg*FlagsWidth+:FlagsWidth]; | 
|  | sec_wipe_zero_i:       flags_d[i_fg] = '0; | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | assign flags_en[i_fg] = ispr_init_i | ispr_update_flags_en | | 
|  | (adder_update_flags_en & is_operation_flag_group[i_fg]) | | 
|  | (logic_update_flags_en & is_operation_flag_group[i_fg]) | | 
|  | (mac_update_flags_en   & is_operation_flag_group[i_fg]) | | 
|  | sec_wipe_zero_i; | 
|  | end | 
|  |  | 
|  |  | 
|  | logic [ExtWLEN-1:0]          mod_intg_q; | 
|  | logic [ExtWLEN-1:0]          mod_intg_d; | 
|  | logic [BaseWordsPerWLEN-1:0] mod_ispr_wr_en; | 
|  | logic [BaseWordsPerWLEN-1:0] mod_wr_en; | 
|  |  | 
|  | logic [ExtWLEN-1:0] ispr_mod_bignum_wdata_intg_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(ExtWLEN)) u_ispr_mod_bignum_wdata_blanker ( | 
|  | .in_i (ispr_bignum_wdata_intg_i), | 
|  | .en_i (ispr_predec_bignum_i.ispr_wr_en[IsprMod]), | 
|  | .out_o(ispr_mod_bignum_wdata_intg_blanked) | 
|  | ); | 
|  | // If the blanker is enabled, the output will not carry the correct ECC bits.  This is not | 
|  | // a problem because a blanked value should never be written to the register.  If the blanked | 
|  | // value is written to the register nonetheless, an integrity error arises. | 
|  |  | 
|  | logic [WLEN-1:0]                mod_no_intg_d; | 
|  | logic [WLEN-1:0]                mod_no_intg_q; | 
|  | logic [ExtWLEN-1:0]             mod_intg_calc; | 
|  | logic [2*BaseWordsPerWLEN-1:0]  mod_intg_err; | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_mod_words | 
|  | prim_secded_inv_39_32_enc i_secded_enc ( | 
|  | .data_i (mod_no_intg_d[i_word*32+:32]), | 
|  | .data_o (mod_intg_calc[i_word*39+:39]) | 
|  | ); | 
|  | prim_secded_inv_39_32_dec i_secded_dec ( | 
|  | .data_i     (mod_intg_q[i_word*39+:39]), | 
|  | .data_o     (/* unused because we abort on any integrity error */), | 
|  | .syndrome_o (/* unused */), | 
|  | .err_o      (mod_intg_err[i_word*2+:2]) | 
|  | ); | 
|  | assign mod_no_intg_q[i_word*32+:32] = mod_intg_q[i_word*39+:32]; | 
|  |  | 
|  | always_ff @(posedge clk_i) begin | 
|  | if (mod_wr_en[i_word]) begin | 
|  | mod_intg_q[i_word*39+:39] <= mod_intg_d[i_word*39+:39]; | 
|  | end | 
|  | end | 
|  |  | 
|  | always_comb begin | 
|  | mod_no_intg_d[i_word*32+:32] = '0; | 
|  | unique case (1'b1) | 
|  | // Non-encoded inputs have to be encoded before writing to the register. | 
|  | sec_wipe_mod_urnd_i: begin | 
|  | // In a secure wipe, `urnd_data_i` is written to the register before the zero word.  The | 
|  | // ECC bits should not matter between the two writes, but nonetheless we encode | 
|  | // `urnd_data_i` so there is no spurious integrity error. | 
|  | mod_no_intg_d[i_word*32+:32] = urnd_data_i[i_word*32+:32]; | 
|  | mod_intg_d[i_word*39+:39]  = mod_intg_calc[i_word*39+:39]; | 
|  | end | 
|  | // Pre-encoded inputs can directly be written to the register. | 
|  | default: mod_intg_d[i_word*39+:39] = ispr_mod_bignum_wdata_intg_blanked[i_word*39+:39]; | 
|  | endcase | 
|  |  | 
|  | unique case (1'b1) | 
|  | ispr_init_i: mod_intg_d[i_word*39+:39] = EccZeroWord; | 
|  | ispr_base_wr_en_i[i_word]: begin | 
|  | mod_no_intg_d[i_word*32+:32] = ispr_base_wdata_i; | 
|  | mod_intg_d[i_word*39+:39] = mod_intg_calc[i_word*39+:39]; | 
|  | end | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | `ASSERT(ModWrSelOneHot, $onehot0({ispr_init_i, ispr_base_wr_en_i[i_word]})) | 
|  |  | 
|  | assign mod_ispr_wr_en[i_word] = (ispr_addr_i == IsprMod)                          & | 
|  | (ispr_base_wr_en_i[i_word] | ispr_bignum_wr_en_i) & | 
|  | ispr_wr_commit_i; | 
|  |  | 
|  | assign mod_wr_en[i_word] = ispr_init_i            | | 
|  | mod_ispr_wr_en[i_word] | | 
|  | sec_wipe_mod_urnd_i; | 
|  | end | 
|  |  | 
|  | assign ispr_acc_wr_en_o   = | 
|  | ((ispr_addr_i == IsprAcc) & ispr_bignum_wr_en_i & ispr_wr_commit_i) | ispr_init_i; | 
|  |  | 
|  |  | 
|  | logic [ExtWLEN-1:0] ispr_acc_bignum_wdata_intg_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(ExtWLEN)) u_ispr_acc_bignum_wdata_intg_blanker ( | 
|  | .in_i (ispr_bignum_wdata_intg_i), | 
|  | .en_i (ispr_predec_bignum_i.ispr_wr_en[IsprAcc]), | 
|  | .out_o(ispr_acc_bignum_wdata_intg_blanked) | 
|  | ); | 
|  | // If the blanker is enabled, the output will not carry the correct ECC bits.  This is not | 
|  | // a problem because a blanked value should never be used.  If the blanked value is used | 
|  | // nonetheless, an integrity error arises. | 
|  |  | 
|  | assign ispr_acc_wr_data_intg_o = ispr_init_i ? EccWideZeroWord | 
|  | : ispr_acc_bignum_wdata_intg_blanked; | 
|  |  | 
|  | // ISPR read data is muxed out in two stages: | 
|  | // 1. Select amongst the ISPRs that have no integrity bits. The output has integrity calculated | 
|  | //    for it. | 
|  | // 2. Select between the ISPRs that have integrity bits and the result of the first stage. | 
|  |  | 
|  | // Number of ISPRs that have integrity protection | 
|  | localparam int NIntgIspr = 2; | 
|  | // IDs fpr ISPRs with integrity | 
|  | localparam int IsprModIntg = 0; | 
|  | localparam int IsprAccIntg = 1; | 
|  | // ID representing all ISPRs with no integrity | 
|  | localparam int IsprNoIntg = 2; | 
|  |  | 
|  | logic [NIntgIspr:0] ispr_rdata_intg_mux_sel; | 
|  | logic [ExtWLEN-1:0] ispr_rdata_intg_mux_in    [NIntgIspr+1]; | 
|  | logic [WLEN-1:0]    ispr_rdata_no_intg_mux_in [NIspr]; | 
|  |  | 
|  | // First stage | 
|  | // MOD and ACC supply their own integrity so these values are unused | 
|  | assign ispr_rdata_no_intg_mux_in[IsprMod] = 0; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprAcc] = 0; | 
|  |  | 
|  | assign ispr_rdata_no_intg_mux_in[IsprRnd]    = rnd_data_i; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprUrnd]   = urnd_data_i; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprFlags]  = {{(WLEN - (NFlagGroups * FlagsWidth)){1'b0}}, | 
|  | flags_flattened}; | 
|  | // SEC_CM: KEY.SIDELOAD | 
|  | assign ispr_rdata_no_intg_mux_in[IsprKeyS0L] = sideload_key_shares_i[0][255:0]; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprKeyS0H] = {{(WLEN - (SideloadKeyWidth - 256)){1'b0}}, | 
|  | sideload_key_shares_i[0][SideloadKeyWidth-1:256]}; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprKeyS1L] = sideload_key_shares_i[1][255:0]; | 
|  | assign ispr_rdata_no_intg_mux_in[IsprKeyS1H] = {{(WLEN - (SideloadKeyWidth - 256)){1'b0}}, | 
|  | sideload_key_shares_i[1][SideloadKeyWidth-1:256]}; | 
|  |  | 
|  | logic [WLEN-1:0]    ispr_rdata_no_intg; | 
|  | logic [ExtWLEN-1:0] ispr_rdata_intg_calc; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_onehot_mux #( | 
|  | .Width  (WLEN), | 
|  | .Inputs (NIspr) | 
|  | ) u_ispr_rdata_no_intg_mux ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .in_i  (ispr_rdata_no_intg_mux_in), | 
|  | .sel_i (ispr_predec_bignum_i.ispr_rd_en), | 
|  | .out_o (ispr_rdata_no_intg) | 
|  | ); | 
|  |  | 
|  | for (genvar i_word = 0; i_word < BaseWordsPerWLEN; i_word++) begin : g_rdata_enc | 
|  | prim_secded_inv_39_32_enc i_secded_enc ( | 
|  | .data_i(ispr_rdata_no_intg[i_word * 32 +: 32]), | 
|  | .data_o(ispr_rdata_intg_calc[i_word * 39 +: 39]) | 
|  | ); | 
|  | end | 
|  |  | 
|  | // Second stage | 
|  | assign ispr_rdata_intg_mux_in[IsprModIntg] = mod_intg_q; | 
|  | assign ispr_rdata_intg_mux_in[IsprAccIntg] = ispr_acc_intg_i; | 
|  | assign ispr_rdata_intg_mux_in[IsprNoIntg]  = ispr_rdata_intg_calc; | 
|  |  | 
|  | assign ispr_rdata_intg_mux_sel[IsprModIntg] = ispr_predec_bignum_i.ispr_rd_en[IsprMod]; | 
|  | assign ispr_rdata_intg_mux_sel[IsprAccIntg] = ispr_predec_bignum_i.ispr_rd_en[IsprAcc]; | 
|  |  | 
|  | assign ispr_rdata_intg_mux_sel[IsprNoIntg]  = | 
|  | |{ispr_predec_bignum_i.ispr_rd_en[IsprKeyS1H:IsprKeyS0L], | 
|  | ispr_predec_bignum_i.ispr_rd_en[IsprUrnd], | 
|  | ispr_predec_bignum_i.ispr_rd_en[IsprFlags], | 
|  | ispr_predec_bignum_i.ispr_rd_en[IsprRnd]}; | 
|  |  | 
|  | // If we're reading from an ISPR we must be using the ispr_rdata_intg_mux | 
|  | `ASSERT(IsprRDataIntgMuxSelIfIsprRd_A, | 
|  | |ispr_predec_bignum_i.ispr_rd_en |-> |ispr_rdata_intg_mux_sel) | 
|  |  | 
|  | // If we're reading from MOD or ACC we must not take the read data from the calculated integrity | 
|  | // path | 
|  | `ASSERT(IsprModMustTakeIntg_A, | 
|  | ispr_predec_bignum_i.ispr_rd_en[IsprMod] |-> !ispr_rdata_intg_mux_sel[IsprNoIntg]) | 
|  |  | 
|  | `ASSERT(IsprAccMustTakeIntg_A, | 
|  | ispr_predec_bignum_i.ispr_rd_en[IsprAcc] |-> !ispr_rdata_intg_mux_sel[IsprNoIntg]) | 
|  |  | 
|  |  | 
|  | prim_onehot_mux #( | 
|  | .Width  (ExtWLEN), | 
|  | .Inputs (NIntgIspr+1) | 
|  | ) u_ispr_rdata_intg_mux ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .in_i  (ispr_rdata_intg_mux_in), | 
|  | .sel_i (ispr_rdata_intg_mux_sel), | 
|  | .out_o (ispr_rdata_intg_o) | 
|  | ); | 
|  |  | 
|  | prim_onehot_enc #( | 
|  | .OneHotWidth (NIspr) | 
|  | ) u_expected_ispr_rd_en_enc ( | 
|  | .in_i(ispr_addr_i), | 
|  | .en_i (ispr_rd_en_i), | 
|  | .out_o (expected_ispr_rd_en_onehot) | 
|  | ); | 
|  |  | 
|  | assign ispr_wr_en = |{ispr_bignum_wr_en_i, ispr_base_wr_en_i}; | 
|  |  | 
|  | prim_onehot_enc #( | 
|  | .OneHotWidth (NIspr) | 
|  | ) u_expected_ispr_wr_en_enc ( | 
|  | .in_i(ispr_addr_i), | 
|  | .en_i (ispr_wr_en), | 
|  | .out_o (expected_ispr_wr_en_onehot) | 
|  | ); | 
|  |  | 
|  | // SEC_CM: CTRL.REDUN | 
|  | assign ispr_predec_error_o = | 
|  | |{expected_ispr_rd_en_onehot != ispr_predec_bignum_i.ispr_rd_en, | 
|  | expected_ispr_wr_en_onehot != ispr_predec_bignum_i.ispr_wr_en}; | 
|  |  | 
|  | ///////////// | 
|  | // Shifter // | 
|  | ///////////// | 
|  |  | 
|  | logic [WLEN-1:0]   shifter_in_upper, shifter_in_lower, shifter_in_lower_reverse; | 
|  | logic [WLEN*2-1:0] shifter_in; | 
|  | logic [WLEN*2-1:0] shifter_out; | 
|  | logic [WLEN-1:0]   shifter_out_lower_reverse, shifter_res, unused_shifter_out_upper; | 
|  | logic [WLEN-1:0]   shifter_operand_a_blanked; | 
|  | logic [WLEN-1:0]   shifter_operand_b_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_shifter_operand_a_blanker ( | 
|  | .in_i (operation_i.operand_a), | 
|  | .en_i (alu_predec_bignum_i.shifter_a_en), | 
|  | .out_o(shifter_operand_a_blanked) | 
|  | ); | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_shifter_operand_b_blanker ( | 
|  | .in_i (operation_i.operand_b), | 
|  | .en_i (alu_predec_bignum_i.shifter_b_en), | 
|  | .out_o(shifter_operand_b_blanked) | 
|  | ); | 
|  |  | 
|  | // Operand A is only used for BN.RSHI, otherwise the upper input is 0. For all instructions other | 
|  | // than BN.RHSI alu_predec_bignum_i.shifter_a_en will be 0, resulting in 0 for the upper input. | 
|  | assign shifter_in_upper = shifter_operand_a_blanked; | 
|  | assign shifter_in_lower = shifter_operand_b_blanked; | 
|  |  | 
|  | for (genvar i = 0; i < WLEN; i++) begin : g_shifter_in_lower_reverse | 
|  | assign shifter_in_lower_reverse[i] = shifter_in_lower[WLEN-i-1]; | 
|  | end | 
|  |  | 
|  | assign shifter_in = {shifter_in_upper, | 
|  | alu_predec_bignum_i.shift_right ? shifter_in_lower : shifter_in_lower_reverse}; | 
|  |  | 
|  | assign shifter_out = shifter_in >> alu_predec_bignum_i.shift_amt; | 
|  |  | 
|  | for (genvar i = 0; i < WLEN; i++) begin : g_shifter_out_lower_reverse | 
|  | assign shifter_out_lower_reverse[i] = shifter_out[WLEN-i-1]; | 
|  | end | 
|  |  | 
|  | assign shifter_res = | 
|  | alu_predec_bignum_i.shift_right ? shifter_out[WLEN-1:0] : shifter_out_lower_reverse; | 
|  |  | 
|  | // Only the lower WLEN bits of the shift result are returned. | 
|  | assign unused_shifter_out_upper = shifter_out[WLEN*2-1:WLEN]; | 
|  |  | 
|  | ////////////////// | 
|  | // Adders X & Y // | 
|  | ////////////////// | 
|  |  | 
|  | logic [WLEN:0]   adder_x_op_a_blanked, adder_x_op_b, adder_x_op_b_blanked; | 
|  | logic            adder_x_carry_in; | 
|  | logic            adder_x_op_b_invert; | 
|  | logic [WLEN+1:0] adder_x_res; | 
|  |  | 
|  | logic [WLEN:0]   adder_y_op_a, adder_y_op_b; | 
|  | logic            adder_y_carry_in; | 
|  | logic            adder_y_op_b_invert; | 
|  | logic [WLEN+1:0] adder_y_res; | 
|  | logic [WLEN-1:0] adder_y_op_a_blanked; | 
|  | logic [WLEN-1:0] adder_y_op_shifter_res_blanked; | 
|  |  | 
|  | logic [WLEN-1:0] shift_mod_mux_out; | 
|  | logic [WLEN-1:0] x_res_operand_a_mux_out; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN+1)) u_adder_x_op_a_blanked ( | 
|  | .in_i ({operation_i.operand_a, 1'b1}), | 
|  | .en_i (alu_predec_bignum_i.adder_x_en), | 
|  | .out_o(adder_x_op_a_blanked) | 
|  | ); | 
|  |  | 
|  | assign adder_x_op_b = {adder_x_op_b_invert ? ~operation_i.operand_b : operation_i.operand_b, | 
|  | adder_x_carry_in}; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN+1)) u_adder_x_op_b_blanked ( | 
|  | .in_i (adder_x_op_b), | 
|  | .en_i (alu_predec_bignum_i.adder_x_en), | 
|  | .out_o(adder_x_op_b_blanked) | 
|  | ); | 
|  |  | 
|  | assign adder_x_res = adder_x_op_a_blanked + adder_x_op_b_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_adder_y_op_a_blanked ( | 
|  | .in_i (operation_i.operand_a), | 
|  | .en_i (alu_predec_bignum_i.adder_y_op_a_en), | 
|  | .out_o(adder_y_op_a_blanked) | 
|  | ); | 
|  |  | 
|  | assign x_res_operand_a_mux_out = | 
|  | alu_predec_bignum_i.x_res_operand_a_sel ? adder_x_res[WLEN:1] : adder_y_op_a_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_adder_y_op_shifter_blanked ( | 
|  | .in_i (shifter_res), | 
|  | .en_i (alu_predec_bignum_i.adder_y_op_shifter_en), | 
|  | .out_o(adder_y_op_shifter_res_blanked) | 
|  | ); | 
|  |  | 
|  | assign shift_mod_mux_out = | 
|  | alu_predec_bignum_i.shift_mod_sel ? adder_y_op_shifter_res_blanked : mod_no_intg_q; | 
|  |  | 
|  | assign adder_y_op_a = {x_res_operand_a_mux_out, 1'b1}; | 
|  | assign adder_y_op_b = {adder_y_op_b_invert ? ~shift_mod_mux_out : shift_mod_mux_out, | 
|  | adder_y_carry_in}; | 
|  |  | 
|  | assign adder_y_res = adder_y_op_a + adder_y_op_b; | 
|  |  | 
|  | assign adder_update_flags.C = (operation_i.op == AluOpBignumAdd || | 
|  | operation_i.op == AluOpBignumAddc) ?  adder_y_res[WLEN+1] : | 
|  | ~adder_y_res[WLEN+1]; | 
|  | assign adder_update_flags.M = adder_y_res[WLEN]; | 
|  | assign adder_update_flags.L = adder_y_res[1]; | 
|  | assign adder_update_flags.Z = ~|adder_y_res[WLEN:1]; | 
|  |  | 
|  | // The LSb of the adder results are unused. | 
|  | logic unused_adder_x_res_lsb, unused_adder_y_res_lsb; | 
|  | assign unused_adder_x_res_lsb = adder_x_res[0]; | 
|  | assign unused_adder_y_res_lsb = adder_y_res[0]; | 
|  |  | 
|  | ////////////////////////////// | 
|  | // Shifter & Adders control // | 
|  | ////////////////////////////// | 
|  | logic expected_adder_x_en; | 
|  | logic expected_x_res_operand_a_sel; | 
|  | logic expected_adder_y_op_a_en; | 
|  | logic expected_adder_y_op_shifter_en; | 
|  | logic expected_shifter_a_en; | 
|  | logic expected_shifter_b_en; | 
|  | logic expected_shift_right; | 
|  | logic expected_shift_mod_sel; | 
|  | logic expected_logic_a_en; | 
|  | logic expected_logic_shifter_en; | 
|  | logic [3:0] expected_logic_res_sel; | 
|  |  | 
|  | always_comb begin | 
|  | adder_x_carry_in          = 1'b0; | 
|  | adder_x_op_b_invert       = 1'b0; | 
|  | adder_y_carry_in          = 1'b0; | 
|  | adder_y_op_b_invert       = 1'b0; | 
|  | adder_update_flags_en_raw = 1'b0; | 
|  | logic_update_flags_en_raw = 1'b0; | 
|  |  | 
|  | expected_adder_x_en             = 1'b0; | 
|  | expected_x_res_operand_a_sel    = 1'b0; | 
|  | expected_adder_y_op_a_en        = 1'b0; | 
|  | expected_adder_y_op_shifter_en  = 1'b0; | 
|  | expected_shifter_a_en           = 1'b0; | 
|  | expected_shifter_b_en           = 1'b0; | 
|  | expected_shift_right            = 1'b0; | 
|  | expected_shift_mod_sel          = 1'b1; | 
|  | expected_logic_a_en             = 1'b0; | 
|  | expected_logic_shifter_en       = 1'b0; | 
|  | expected_logic_res_sel          = '0; | 
|  |  | 
|  | unique case (operation_i.op) | 
|  | AluOpBignumAdd: begin | 
|  | // Shifter computes B [>>|<<] shift_amt | 
|  | // Y computes A + shifter_res | 
|  | // X ignored | 
|  | adder_y_carry_in               = 1'b0; | 
|  | adder_y_op_b_invert            = 1'b0; | 
|  | adder_update_flags_en_raw      = 1'b1; | 
|  | expected_adder_y_op_shifter_en = 1'b1; | 
|  |  | 
|  | expected_adder_y_op_a_en = 1'b1; | 
|  | expected_shifter_b_en    = 1'b1; | 
|  | expected_shift_right     = operation_i.shift_right; | 
|  | end | 
|  | AluOpBignumAddc: begin | 
|  | // Shifter computes B [>>|<<] shift_amt | 
|  | // Y computes A + shifter_res + flags.C | 
|  | // X ignored | 
|  | adder_y_carry_in               = selected_flags.C; | 
|  | adder_y_op_b_invert            = 1'b0; | 
|  | adder_update_flags_en_raw      = 1'b1; | 
|  | expected_adder_y_op_shifter_en = 1'b1; | 
|  |  | 
|  | expected_adder_y_op_a_en = 1'b1; | 
|  | expected_shifter_b_en    = 1'b1; | 
|  | expected_shift_right     = operation_i.shift_right; | 
|  | end | 
|  | AluOpBignumAddm: begin | 
|  | // X computes A + B | 
|  | // Y computes adder_x_res - mod = adder_x_res + ~mod + 1 | 
|  | // Shifter ignored | 
|  | // Output mux chooses result based on top bit of X result (whether mod subtraction in | 
|  | // Y should be applied or not) | 
|  | adder_x_carry_in    = 1'b0; | 
|  | adder_x_op_b_invert = 1'b0; | 
|  | adder_y_carry_in    = 1'b1; | 
|  | adder_y_op_b_invert = 1'b1; | 
|  |  | 
|  | expected_adder_x_en          = 1'b1; | 
|  | expected_x_res_operand_a_sel = 1'b1; | 
|  | expected_shift_mod_sel       = 1'b0; | 
|  | end | 
|  | AluOpBignumSub: begin | 
|  | // Shifter computes B [>>|<<] shift_amt | 
|  | // Y computes A - shifter_res = A + ~shifter_res + 1 | 
|  | // X ignored | 
|  | adder_y_carry_in               = 1'b1; | 
|  | adder_y_op_b_invert            = 1'b1; | 
|  | adder_update_flags_en_raw      = 1'b1; | 
|  | expected_adder_y_op_shifter_en = 1'b1; | 
|  |  | 
|  | expected_adder_y_op_a_en = 1'b1; | 
|  | expected_shifter_b_en    = 1'b1; | 
|  | expected_shift_right     = operation_i.shift_right; | 
|  | end | 
|  | AluOpBignumSubb: begin | 
|  | // Shifter computes B [>>|<<] shift_amt | 
|  | // Y computes A - shifter_res + ~flags.C = A + ~shifter_res + flags.C | 
|  | // X ignored | 
|  | adder_y_carry_in               = ~selected_flags.C; | 
|  | adder_y_op_b_invert            = 1'b1; | 
|  | adder_update_flags_en_raw      = 1'b1; | 
|  | expected_adder_y_op_shifter_en = 1'b1; | 
|  |  | 
|  | expected_adder_y_op_a_en = 1'b1; | 
|  | expected_shifter_b_en    = 1'b1; | 
|  | expected_shift_right     = operation_i.shift_right; | 
|  | end | 
|  | AluOpBignumSubm: begin | 
|  | // X computes A - B = A + ~B + 1 | 
|  | // Y computes adder_x_res + mod | 
|  | // Shifter ignored | 
|  | // Output mux chooses result based on top bit of X result (whether subtraction in Y should | 
|  | // be applied or not) | 
|  | adder_x_carry_in    = 1'b1; | 
|  | adder_x_op_b_invert = 1'b1; | 
|  | adder_y_carry_in    = 1'b0; | 
|  | adder_y_op_b_invert = 1'b0; | 
|  |  | 
|  | expected_adder_x_en          = 1'b1; | 
|  | expected_x_res_operand_a_sel = 1'b1; | 
|  | expected_shift_mod_sel       = 1'b0; | 
|  | end | 
|  | AluOpBignumRshi: begin | 
|  | // Shifter computes {A, B} >> shift_amt | 
|  | // X, Y ignored | 
|  | // Feed blanked shifter output (adder_y_op_shifter_res_blanked) to Y to avoid undesired | 
|  | // leakage in the zero flag computation. | 
|  |  | 
|  | expected_shifter_a_en = 1'b1; | 
|  | expected_shifter_b_en = 1'b1; | 
|  | expected_shift_right  = 1'b1; | 
|  | end | 
|  | AluOpBignumXor, | 
|  | AluOpBignumOr, | 
|  | AluOpBignumAnd, | 
|  | AluOpBignumNot: begin | 
|  | // Shift computes one operand for the logical operation | 
|  | // X & Y ignored | 
|  | // Feed blanked shifter output (adder_y_op_shifter_res_blanked) to Y to avoid undesired | 
|  | // leakage in the zero flag computation. | 
|  | logic_update_flags_en_raw             = 1'b1; | 
|  |  | 
|  | expected_shifter_b_en                 = 1'b1; | 
|  | expected_shift_right                  = operation_i.shift_right; | 
|  | expected_logic_a_en                   = operation_i.op != AluOpBignumNot; | 
|  | expected_logic_shifter_en             = 1'b1; | 
|  | expected_logic_res_sel[AluOpLogicXor] = operation_i.op == AluOpBignumXor; | 
|  | expected_logic_res_sel[AluOpLogicOr]  = operation_i.op == AluOpBignumOr; | 
|  | expected_logic_res_sel[AluOpLogicAnd] = operation_i.op == AluOpBignumAnd; | 
|  | expected_logic_res_sel[AluOpLogicNot] = operation_i.op == AluOpBignumNot; | 
|  | end | 
|  | // No operation, do nothing. | 
|  | AluOpBignumNone: ; | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | logic [$clog2(WLEN)-1:0] expected_shift_amt; | 
|  | assign expected_shift_amt = operation_i.shift_amt; | 
|  |  | 
|  | // SEC_CM: CTRL.REDUN | 
|  | assign alu_predec_error_o = | 
|  | |{expected_adder_x_en != alu_predec_bignum_i.adder_x_en, | 
|  | expected_x_res_operand_a_sel != alu_predec_bignum_i.x_res_operand_a_sel, | 
|  | expected_adder_y_op_a_en != alu_predec_bignum_i.adder_y_op_a_en, | 
|  | expected_adder_y_op_shifter_en != alu_predec_bignum_i.adder_y_op_shifter_en, | 
|  | expected_shifter_a_en != alu_predec_bignum_i.shifter_a_en, | 
|  | expected_shifter_b_en != alu_predec_bignum_i.shifter_b_en, | 
|  | expected_shift_right != alu_predec_bignum_i.shift_right, | 
|  | expected_shift_amt != alu_predec_bignum_i.shift_amt, | 
|  | expected_shift_mod_sel != alu_predec_bignum_i.shift_mod_sel, | 
|  | expected_logic_a_en != alu_predec_bignum_i.logic_a_en, | 
|  | expected_logic_shifter_en != alu_predec_bignum_i.logic_shifter_en, | 
|  | expected_logic_res_sel != alu_predec_bignum_i.logic_res_sel}; | 
|  |  | 
|  | //////////////////////// | 
|  | // Logical operations // | 
|  | //////////////////////// | 
|  |  | 
|  | logic [WLEN-1:0] logical_res_mux_in [4]; | 
|  | logic [WLEN-1:0] logical_res; | 
|  | logic [WLEN-1:0] logical_op_a_blanked; | 
|  | logic [WLEN-1:0] logical_op_shifter_res_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_logical_op_a_blanker ( | 
|  | .in_i (operation_i.operand_a), | 
|  | .en_i (alu_predec_bignum_i.logic_a_en), | 
|  | .out_o(logical_op_a_blanked) | 
|  | ); | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_blanker #(.Width(WLEN)) u_logical_op_shifter_res_blanker ( | 
|  | .in_i (shifter_res), | 
|  | .en_i (alu_predec_bignum_i.logic_shifter_en), | 
|  | .out_o(logical_op_shifter_res_blanked) | 
|  | ); | 
|  |  | 
|  | assign logical_res_mux_in[AluOpLogicXor] = logical_op_a_blanked ^ logical_op_shifter_res_blanked; | 
|  | assign logical_res_mux_in[AluOpLogicOr]  = logical_op_a_blanked | logical_op_shifter_res_blanked; | 
|  | assign logical_res_mux_in[AluOpLogicAnd] = logical_op_a_blanked & logical_op_shifter_res_blanked; | 
|  | assign logical_res_mux_in[AluOpLogicNot] = ~logical_op_shifter_res_blanked; | 
|  |  | 
|  | // SEC_CM: DATA_REG_SW.SCA | 
|  | prim_onehot_mux #( | 
|  | .Width (WLEN), | 
|  | .Inputs(4) | 
|  | ) u_logical_res_mux ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .in_i  (logical_res_mux_in), | 
|  | .sel_i (alu_predec_bignum_i.logic_res_sel), | 
|  | .out_o (logical_res) | 
|  | ); | 
|  |  | 
|  | // Logical operations only update M, L and Z; C must remain at its old value. | 
|  | assign logic_update_flags.C = selected_flags.C; | 
|  | assign logic_update_flags.M = logical_res[WLEN-1]; | 
|  | assign logic_update_flags.L = logical_res[0]; | 
|  | assign logic_update_flags.Z = ~|logical_res; | 
|  |  | 
|  | ///////////////////////////////// | 
|  | // Conditional Select Flag Mux // | 
|  | ///////////////////////////////// | 
|  |  | 
|  | always_comb begin | 
|  | unique case (operation_i.sel_flag) | 
|  | FlagC:   selection_flag_o = selected_flags.C; | 
|  | FlagM:   selection_flag_o = selected_flags.M; | 
|  | FlagL:   selection_flag_o = selected_flags.L; | 
|  | // FlagZ case | 
|  | default: selection_flag_o = selected_flags.Z; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | //////////////////////// | 
|  | // Output multiplexer // | 
|  | //////////////////////// | 
|  |  | 
|  | logic adder_y_res_used; | 
|  | always_comb begin | 
|  | operation_result_o = adder_y_res[WLEN:1]; | 
|  | adder_y_res_used = 1'b1; | 
|  |  | 
|  | unique case(operation_i.op) | 
|  | AluOpBignumAdd, | 
|  | AluOpBignumAddc, | 
|  | AluOpBignumSub, | 
|  | AluOpBignumSubb: begin | 
|  | operation_result_o = adder_y_res[WLEN:1]; | 
|  | adder_y_res_used = 1'b1; | 
|  | end | 
|  |  | 
|  | // For pseudo-mod operations the result depends upon initial a + b / a - b result that is | 
|  | // computed in X. Operation to add/subtract mod (X + mod / X - mod) is computed in Y. | 
|  | // Subtraction is computed using in the X & Y adders as a - b == a + ~b + 1. Note that for | 
|  | // a - b the top bit of the result will be set if a - b >= 0 and otherwise clear. | 
|  |  | 
|  | // BN.ADDM - X = a + b, Y = X - mod, subtract mod if a + b >= mod | 
|  | // * If X generates carry a + b > mod (as mod is 256-bit) - Select Y result | 
|  | // * If Y generates carry X - mod == (a + b) - mod >= 0 hence a + b >= mod, note this is only | 
|  | //   valid if X does not generate carry - Select Y result | 
|  | // * If neither happen a + b < mod - Select X result | 
|  | AluOpBignumAddm: begin | 
|  | // `adder_y_res` is always used: either as condition in the following `if` statement or, if | 
|  | // the `if` statement short-circuits, in the body of the `if` statement. | 
|  | adder_y_res_used = 1'b1; | 
|  | if (adder_x_res[WLEN+1] || adder_y_res[WLEN+1]) begin | 
|  | operation_result_o = adder_y_res[WLEN:1]; | 
|  | end else begin | 
|  | operation_result_o = adder_x_res[WLEN:1]; | 
|  | end | 
|  | end | 
|  |  | 
|  | // BN.SUBM - X = a - b, Y = X + mod, add mod if a - b < 0 | 
|  | // * If X generates carry a - b >= 0 - Select X result | 
|  | // * Otherwise select Y result | 
|  | AluOpBignumSubm: begin | 
|  | if (adder_x_res[WLEN+1]) begin | 
|  | operation_result_o = adder_x_res[WLEN:1]; | 
|  | adder_y_res_used = 1'b0; | 
|  | end else begin | 
|  | operation_result_o = adder_y_res[WLEN:1]; | 
|  | adder_y_res_used = 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | AluOpBignumRshi: begin | 
|  | operation_result_o = shifter_res[WLEN-1:0]; | 
|  | adder_y_res_used = 1'b0; | 
|  | end | 
|  |  | 
|  | AluOpBignumXor, | 
|  | AluOpBignumOr, | 
|  | AluOpBignumAnd, | 
|  | AluOpBignumNot: begin | 
|  | operation_result_o = logical_res; | 
|  | adder_y_res_used = 1'b0; | 
|  | end | 
|  | default: ; | 
|  | endcase | 
|  | end | 
|  |  | 
|  | // Determine if `mod_intg_q` is used.  The control signals are only valid if `operation_i.op` is | 
|  | // not none. If `shift_mod_sel` is low, `mod_intg_q` flows into `adder_y_op_b` and from there | 
|  | // into `adder_y_res`.  In this case, `mod_intg_q` is used iff  `adder_y_res` flows into | 
|  | // `operation_result_o`. | 
|  | logic mod_used; | 
|  | assign mod_used = operation_valid_i & (operation_i.op != AluOpBignumNone) | 
|  | & !alu_predec_bignum_i.shift_mod_sel & adder_y_res_used; | 
|  | `ASSERT_KNOWN(ModUsed_A, mod_used) | 
|  |  | 
|  | // Raise a register integrity violation error iff `mod_intg_q` is used and (at least partially) | 
|  | // invalid. | 
|  | assign reg_intg_violation_err_o = mod_used & |(mod_intg_err); | 
|  | `ASSERT_KNOWN(RegIntgErrKnown_A, reg_intg_violation_err_o) | 
|  |  | 
|  | endmodule |