diff --git a/hw/ip/sram_ctrl/data/sram_ctrl.hjson b/hw/ip/sram_ctrl/data/sram_ctrl.hjson index 08e279fb2f354a..2e360593da0a12 100644 --- a/hw/ip/sram_ctrl/data/sram_ctrl.hjson +++ b/hw/ip/sram_ctrl/data/sram_ctrl.hjson @@ -381,6 +381,26 @@ }, ] }, + { name: "SCR_KEY_ROTATED", + desc: "Clearable SRAM key request status.", + swaccess: "rw1c", + hwaccess: "hwo", + fields: [ + { bits: "3:0", + name: "SUCCESS", + mubi: true, + desc: ''' + This status register is similar to !!SCR_KEY_VALID with the difference that the status is multibit encoded, + SW clearable and sticky (i.e., HW does not auto-clear the register except during escalation). That way, + SW can use this for a hardened acknowledgement mechanism where it clears the register before requesting a key. + + kMultiBitBool4True indicates that a valid scrambling key has been obtained from OTP. + Write kMultiBitBool4True to clear. + ''', + resval: false + }, + ] + }, ], ram: [ diff --git a/hw/ip/sram_ctrl/doc/registers.md b/hw/ip/sram_ctrl/doc/registers.md index 6f468e43ac4fd6..0b3657fad2233a 100644 --- a/hw/ip/sram_ctrl/doc/registers.md +++ b/hw/ip/sram_ctrl/doc/registers.md @@ -3,14 +3,15 @@ ## Summary of the **`regs`** interface's registers -| Name | Offset | Length | Description | -|:----------------------------------------|:---------|---------:|:---------------------------------------------| -| sram_ctrl.[`ALERT_TEST`](#alert_test) | 0x0 | 4 | Alert Test Register | -| sram_ctrl.[`STATUS`](#status) | 0x4 | 4 | SRAM status register. | -| sram_ctrl.[`EXEC_REGWEN`](#exec_regwen) | 0x8 | 4 | Lock register for execution enable register. | -| sram_ctrl.[`EXEC`](#exec) | 0xc | 4 | Sram execution enable. | -| sram_ctrl.[`CTRL_REGWEN`](#ctrl_regwen) | 0x10 | 4 | Lock register for control register. | -| sram_ctrl.[`CTRL`](#ctrl) | 0x14 | 4 | SRAM ctrl register. | +| Name | Offset | Length | Description | +|:------------------------------------------------|:---------|---------:|:---------------------------------------------| +| sram_ctrl.[`ALERT_TEST`](#alert_test) | 0x0 | 4 | Alert Test Register | +| sram_ctrl.[`STATUS`](#status) | 0x4 | 4 | SRAM status register. | +| sram_ctrl.[`EXEC_REGWEN`](#exec_regwen) | 0x8 | 4 | Lock register for execution enable register. | +| sram_ctrl.[`EXEC`](#exec) | 0xc | 4 | Sram execution enable. | +| sram_ctrl.[`CTRL_REGWEN`](#ctrl_regwen) | 0x10 | 4 | Lock register for control register. | +| sram_ctrl.[`CTRL`](#ctrl) | 0x14 | 4 | SRAM ctrl register. | +| sram_ctrl.[`SCR_KEY_ROTATED`](#scr_key_rotated) | 0x18 | 4 | Clearable SRAM key request status. | ## ALERT_TEST Alert Test Register @@ -174,5 +175,30 @@ can poll its status. Note that requesting a new scrambling key takes ~200 OTP cy to ~800 CPU cycles (OTP runs at 24MHz, CPU runs at 100MHz). Note that writing 1 to this register while a key request is pending has no effect. +## SCR_KEY_ROTATED +Clearable SRAM key request status. +- Offset: `0x18` +- Reset default: `0x9` +- Reset mask: `0xf` + +### Fields + +```wavejson +{"reg": [{"name": "SUCCESS", "bits": 4, "attr": ["rw1c"], "rotate": -90}, {"bits": 28}], "config": {"lanes": 1, "fontsize": 10, "vspace": 90}} +``` + +| Bits | Type | Reset | Name | +|:------:|:------:|:-------:|:-------------------------------------| +| 31:4 | | | Reserved | +| 3:0 | rw1c | 0x9 | [SUCCESS](#scr_key_rotated--success) | + +### SCR_KEY_ROTATED . SUCCESS +This status register is similar to [`SCR_KEY_VALID`](#scr_key_valid) with the difference that the status is multibit encoded, +SW clearable and sticky (i.e., HW does not auto-clear the register except during escalation). That way, +SW can use this for a hardened acknowledgement mechanism where it clears the register before requesting a key. + +kMultiBitBool4True indicates that a valid scrambling key has been obtained from OTP. +Write kMultiBitBool4True to clear. + This interface does not expose any registers. diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv index a6eb75ea0313c0..504a6196fc9eec 100644 --- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv +++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_scoreboard.sv @@ -13,6 +13,7 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor // local variables bit [TL_DW-1:0] exp_status = '0; + mubi4_t exp_renew_scr_key = MuBi4False; bit in_key_req = 0; @@ -356,6 +357,7 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor exp_status[SramCtrlScrKeySeedValid] = 0; exp_status[SramCtrlScrKeyValid] = 0; exp_status[SramCtrlInitDone] = 0; + exp_renew_scr_key = MuBi4False; // escalation resets the key and nonce back to defaults reset_key_nonce(); @@ -439,6 +441,7 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor // if we are in escalated state, key_valid and scr_key_seed_valid will remain low if (status_lc_esc == EscNone) begin + exp_renew_scr_key = MuBi4True; exp_status[SramCtrlScrKeyValid] = 1; exp_status[SramCtrlScrKeySeedValid] = seed_valid; end @@ -523,6 +526,11 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor void'(ral.ctrl.init.predict(.value(0), .kind(UVM_PREDICT_READ))); end end + "renew_scr_key": begin + if (addr_phase_read) begin + void'(ral.renew_scr_key.predict(.value(exp_renew_scr_key), .kind(UVM_PREDICT_READ))); + end + end default: begin `uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name())) end @@ -541,6 +549,9 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor mirrored_value[SramCtrlScrKeySeedValid] != item.d_data[SramCtrlScrKeySeedValid])) begin new_key_received = 0; + end else if (csr.get_name() == "renew_scr_key" && new_key_received && ( + mirrored_value != item.d_data)) begin + new_key_received = 0; end else if (csr.get_name() == "status" && init_after_new_key && mirrored_value[SramCtrlInitDone] != item.d_data[SramCtrlInitDone]) begin @@ -571,6 +582,7 @@ class sram_ctrl_scoreboard #(parameter int AddrWidth = 10) extends cip_base_scor mem_bkdr_scb.reset(); mem_bkdr_scb.update_key(key, nonce); exp_status = '0; + exp_renew_scr_key = MuBi4False; write_item_q.delete(); exp_mem[cfg.sram_ral_name].init(); diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv index 7a1035e5dd0437..7d392e08e6fed0 100644 --- a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv +++ b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv @@ -60,6 +60,11 @@ module sram_ctrl import lc_ctrl_pkg::lc_tx_or_hi; import lc_ctrl_pkg::lc_tx_inv; import lc_ctrl_pkg::lc_to_mubi4; + import prim_mubi_pkg::mubi4_t; + import prim_mubi_pkg::mubi8_t; + import prim_mubi_pkg::MuBi4True; + import prim_mubi_pkg::MuBi4False; + import prim_mubi_pkg::mubi8_test_true_strict; // This is later on pruned to the correct width at the SRAM wrapper interface. parameter int unsigned Depth = MemSizeRam >> 2; @@ -265,6 +270,11 @@ module sram_ctrl assign hw2reg.status.scr_key_valid.d = key_ack & ~key_req & ~local_esc; assign hw2reg.status.scr_key_valid.de = key_req | key_ack | local_esc; + // As opposed to scr_key_valid, SW is responsible for clearing this register. + // It is not automatically cleared by HW, except when escalating. + assign hw2reg.scr_key_rotated.d = (key_ack & ~local_esc) ? MuBi4True : MuBi4False; + assign hw2reg.scr_key_rotated.de = key_ack | local_esc; + // Clear this bit on local escalation. logic key_seed_valid; assign hw2reg.status.scr_key_seed_valid.d = key_seed_valid & ~local_esc; @@ -322,12 +332,6 @@ module sram_ctrl // SRAM Execution // //////////////////// - import prim_mubi_pkg::mubi4_t; - import prim_mubi_pkg::mubi8_t; - import prim_mubi_pkg::MuBi4True; - import prim_mubi_pkg::MuBi4False; - import prim_mubi_pkg::mubi8_test_true_strict; - mubi4_t en_ifetch; if (InstrExec) begin : gen_instr_ctrl lc_tx_t lc_hw_debug_en; diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv index 93e0c043de39ca..97937f0b87e2df 100644 --- a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv +++ b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv @@ -82,6 +82,11 @@ package sram_ctrl_reg_pkg; } init_done; } sram_ctrl_hw2reg_status_reg_t; + typedef struct packed { + logic [3:0] d; + logic de; + } sram_ctrl_hw2reg_scr_key_rotated_reg_t; + // Register -> HW type for regs interface typedef struct packed { sram_ctrl_reg2hw_alert_test_reg_t alert_test; // [14:13] @@ -92,7 +97,8 @@ package sram_ctrl_reg_pkg; // HW -> register type for regs interface typedef struct packed { - sram_ctrl_hw2reg_status_reg_t status; // [11:0] + sram_ctrl_hw2reg_status_reg_t status; // [16:5] + sram_ctrl_hw2reg_scr_key_rotated_reg_t scr_key_rotated; // [4:0] } sram_ctrl_regs_hw2reg_t; // Register offsets for regs interface @@ -102,6 +108,7 @@ package sram_ctrl_reg_pkg; parameter logic [RegsAw-1:0] SRAM_CTRL_EXEC_OFFSET = 5'h c; parameter logic [RegsAw-1:0] SRAM_CTRL_CTRL_REGWEN_OFFSET = 5'h 10; parameter logic [RegsAw-1:0] SRAM_CTRL_CTRL_OFFSET = 5'h 14; + parameter logic [RegsAw-1:0] SRAM_CTRL_SCR_KEY_ROTATED_OFFSET = 5'h 18; // Reset values for hwext registers and their fields for regs interface parameter logic [0:0] SRAM_CTRL_ALERT_TEST_RESVAL = 1'h 0; @@ -114,17 +121,19 @@ package sram_ctrl_reg_pkg; SRAM_CTRL_EXEC_REGWEN, SRAM_CTRL_EXEC, SRAM_CTRL_CTRL_REGWEN, - SRAM_CTRL_CTRL + SRAM_CTRL_CTRL, + SRAM_CTRL_SCR_KEY_ROTATED } sram_ctrl_regs_id_e; // Register width information to check illegal writes for regs interface - parameter logic [3:0] SRAM_CTRL_REGS_PERMIT [6] = '{ + parameter logic [3:0] SRAM_CTRL_REGS_PERMIT [7] = '{ 4'b 0001, // index[0] SRAM_CTRL_ALERT_TEST 4'b 0001, // index[1] SRAM_CTRL_STATUS 4'b 0001, // index[2] SRAM_CTRL_EXEC_REGWEN 4'b 0001, // index[3] SRAM_CTRL_EXEC 4'b 0001, // index[4] SRAM_CTRL_CTRL_REGWEN - 4'b 0001 // index[5] SRAM_CTRL_CTRL + 4'b 0001, // index[5] SRAM_CTRL_CTRL + 4'b 0001 // index[6] SRAM_CTRL_SCR_KEY_ROTATED }; endpackage diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl_regs_reg_top.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl_regs_reg_top.sv index 10a6cf0909a571..1f144c67daa267 100644 --- a/hw/ip/sram_ctrl/rtl/sram_ctrl_regs_reg_top.sv +++ b/hw/ip/sram_ctrl/rtl/sram_ctrl_regs_reg_top.sv @@ -55,9 +55,9 @@ module sram_ctrl_regs_reg_top ( // also check for spurious write enables logic reg_we_err; - logic [5:0] reg_we_check; + logic [6:0] reg_we_check; prim_reg_we_check #( - .OneHotWidth(6) + .OneHotWidth(7) ) u_prim_reg_we_check ( .clk_i(clk_i), .rst_ni(rst_ni), @@ -144,6 +144,9 @@ module sram_ctrl_regs_reg_top ( logic ctrl_we; logic ctrl_renew_scr_key_wd; logic ctrl_init_wd; + logic scr_key_rotated_we; + logic [3:0] scr_key_rotated_qs; + logic [3:0] scr_key_rotated_wd; // Register instances // R[alert_test]: V(True) @@ -489,8 +492,36 @@ module sram_ctrl_regs_reg_top ( assign reg2hw.ctrl.init.qe = ctrl_qe; + // R[scr_key_rotated]: V(False) + prim_subreg #( + .DW (4), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (4'h9), + .Mubi (1'b1) + ) u_scr_key_rotated ( + .clk_i (clk_i), + .rst_ni (rst_ni), - logic [5:0] addr_hit; + // from register interface + .we (scr_key_rotated_we), + .wd (scr_key_rotated_wd), + + // from internal hardware + .de (hw2reg.scr_key_rotated.de), + .d (hw2reg.scr_key_rotated.d), + + // to internal hardware + .qe (), + .q (), + .ds (), + + // to register interface (read) + .qs (scr_key_rotated_qs) + ); + + + + logic [6:0] addr_hit; always_comb begin addr_hit = '0; addr_hit[0] = (reg_addr == SRAM_CTRL_ALERT_TEST_OFFSET); @@ -499,6 +530,7 @@ module sram_ctrl_regs_reg_top ( addr_hit[3] = (reg_addr == SRAM_CTRL_EXEC_OFFSET); addr_hit[4] = (reg_addr == SRAM_CTRL_CTRL_REGWEN_OFFSET); addr_hit[5] = (reg_addr == SRAM_CTRL_CTRL_OFFSET); + addr_hit[6] = (reg_addr == SRAM_CTRL_SCR_KEY_ROTATED_OFFSET); end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; @@ -511,7 +543,8 @@ module sram_ctrl_regs_reg_top ( (addr_hit[2] & (|(SRAM_CTRL_REGS_PERMIT[2] & ~reg_be))) | (addr_hit[3] & (|(SRAM_CTRL_REGS_PERMIT[3] & ~reg_be))) | (addr_hit[4] & (|(SRAM_CTRL_REGS_PERMIT[4] & ~reg_be))) | - (addr_hit[5] & (|(SRAM_CTRL_REGS_PERMIT[5] & ~reg_be))))); + (addr_hit[5] & (|(SRAM_CTRL_REGS_PERMIT[5] & ~reg_be))) | + (addr_hit[6] & (|(SRAM_CTRL_REGS_PERMIT[6] & ~reg_be))))); end // Generate write-enables @@ -532,6 +565,9 @@ module sram_ctrl_regs_reg_top ( assign ctrl_renew_scr_key_wd = reg_wdata[0]; assign ctrl_init_wd = reg_wdata[1]; + assign scr_key_rotated_we = addr_hit[6] & reg_we & !reg_error; + + assign scr_key_rotated_wd = reg_wdata[3:0]; // Assign write-enables to checker logic vector. always_comb begin @@ -542,6 +578,7 @@ module sram_ctrl_regs_reg_top ( reg_we_check[3] = exec_gated_we; reg_we_check[4] = ctrl_regwen_we; reg_we_check[5] = ctrl_gated_we; + reg_we_check[6] = scr_key_rotated_we; end // Read data return @@ -578,6 +615,10 @@ module sram_ctrl_regs_reg_top ( reg_rdata_next[1] = '0; end + addr_hit[6]: begin + reg_rdata_next[3:0] = scr_key_rotated_qs; + end + default: begin reg_rdata_next = '1; end