| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| { | |
| name: "sram_ctrl", | |
| human_name: "SRAM Controller", | |
| one_line_desc: "Interfacing on-chip SRAM blocks with system bus, supports lightweight scrambling, integrity and secure wipe", | |
| one_paragraph_desc: ''' | |
| SRAM Controller instantiates on-chip SRAM and makes it accessible through the TileLink on-chip interconnect. | |
| SRAM Controller includes a lightweight scrambling mechanism based on the PRINCE cipher to reduce the attack surface on the confidentiality and integrity of data stored in the SRAM. | |
| For end-to-end data integrity protection, SRAM Controller stores the integrity bits alongside data words in memory and raises an alert if it detects an integrity fault. | |
| SRAM Controller contains an LFSR-based initialization mechanism that overwrites the entire SRAM with pseudorandom data. | |
| ''' | |
| design_spec: "../doc", | |
| dv_doc: "../doc/dv", | |
| hw_checklist: "../doc/checklist", | |
| sw_checklist: "/sw/device/lib/dif/dif_sram_ctrl", | |
| version: "1.0", | |
| life_stage: "L1", | |
| design_stage: "D3", | |
| verification_stage: "V2S", | |
| dif_stage: "S2", | |
| clocking: [ | |
| {clock: "clk_i", reset: "rst_ni", primary: true}, | |
| {clock: "clk_otp_i", reset: "rst_otp_ni"} | |
| ] | |
| bus_interfaces: [ | |
| { protocol: "tlul", direction: "device", name: "regs" } | |
| { protocol: "tlul", direction: "device", name: "ram" }, | |
| ], | |
| /////////////////////////// | |
| // Interrupts and Alerts // | |
| /////////////////////////// | |
| alert_list: [ | |
| { name: "fatal_error", | |
| desc: ''' | |
| This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected, | |
| or if the initialization mechanism has reached an invalid state. | |
| ''' | |
| } | |
| ], | |
| //////////////// | |
| // Parameters // | |
| //////////////// | |
| param_list: [ | |
| { name: "RndCnstSramKey", | |
| desc: "Compile-time random reset value for SRAM scrambling key.", | |
| type: "otp_ctrl_pkg::sram_key_t" | |
| randcount: "128", | |
| randtype: "data", // randomize randcount databits | |
| } | |
| { name: "RndCnstSramNonce", | |
| desc: "Compile-time random reset value for SRAM scrambling nonce.", | |
| type: "otp_ctrl_pkg::sram_nonce_t" | |
| randcount: "128", | |
| randtype: "data", // randomize randcount databits | |
| }, | |
| { name: "RndCnstLfsrSeed", | |
| desc: "Compile-time random bits for initial LFSR seed", | |
| type: "sram_ctrl_pkg::lfsr_seed_t" | |
| randcount: "32", | |
| randtype: "data", // randomize randcount databits | |
| } | |
| { name: "RndCnstLfsrPerm", | |
| desc: "Compile-time random permutation for LFSR output", | |
| type: "sram_ctrl_pkg::lfsr_perm_t" | |
| randcount: "32", | |
| randtype: "perm", // random permutation for randcount elements | |
| } | |
| // This parameter is overridden by topgen to set the actual RAM size. | |
| { name: "MemSizeRam", | |
| desc: "Memory size of the RAM (in bytes).", | |
| type: "int", | |
| default: "0x1000" | |
| }, | |
| { name: "InstrExec", | |
| desc: "Support execution from SRAM", | |
| type: "bit", | |
| local: "false", | |
| expose: "true", | |
| default: "1" | |
| }, | |
| ] | |
| ///////////////////////////// | |
| // Intermodule Connections // | |
| ///////////////////////////// | |
| inter_signal_list: [ | |
| // Key request to OTP | |
| { struct: "sram_otp_key" | |
| type: "req_rsp" | |
| name: "sram_otp_key" | |
| act: "req" | |
| package: "otp_ctrl_pkg" | |
| }, | |
| // SRAM attribute input | |
| { struct: "ram_1p_cfg" | |
| type: "uni" | |
| name: "cfg" | |
| act: "rcv" | |
| default: "'0" | |
| package: "prim_ram_1p_pkg" | |
| }, | |
| // Broadcast from LC | |
| { struct: "lc_tx" | |
| type: "uni" | |
| name: "lc_escalate_en" | |
| act: "rcv" | |
| default: "lc_ctrl_pkg::Off" | |
| package: "lc_ctrl_pkg" | |
| }, | |
| { struct: "lc_tx" | |
| type: "uni" | |
| name: "lc_hw_debug_en" | |
| act: "rcv" | |
| default: "lc_ctrl_pkg::Off" | |
| package: "lc_ctrl_pkg" | |
| }, | |
| { struct: "mubi8", | |
| type: "uni", | |
| name: "otp_en_sram_ifetch", | |
| act: "rcv", | |
| package: "prim_mubi_pkg", | |
| default: "prim_mubi_pkg::MuBi8False" | |
| }, | |
| ] | |
| ///////////////////// | |
| // Countermeasures // | |
| ///////////////////// | |
| countermeasures: [ | |
| { name: "BUS.INTEGRITY", | |
| desc: "End-to-end bus integrity scheme." | |
| } | |
| { name: "CTRL.CONFIG.REGWEN", | |
| desc: "The SRAM control register is protected by a REGWEN." | |
| } | |
| { name: "EXEC.CONFIG.REGWEN", | |
| desc: "The SRAM execution enable register is protected by a REGWEN." | |
| } | |
| { name: "EXEC.CONFIG.MUBI", | |
| desc: "The SRAM execution enable register is multibit encoded." | |
| } | |
| { name: "EXEC.INTERSIG.MUBI", | |
| desc: "The SRAM execution enable signal coming from OTP is multibit encoded." | |
| } | |
| { name: "LC_ESCALATE_EN.INTERSIG.MUBI", | |
| desc: "The life cycle escalation enable signal is multibit encoded." | |
| } | |
| { name: "LC_HW_DEBUG_EN.INTERSIG.MUBI", | |
| desc: "The life cycle hardware debug enable signal is multibit encoded." | |
| } | |
| { name: "MEM.INTEGRITY", | |
| desc: "End-to-end data/memory integrity scheme." | |
| } | |
| { name: "MEM.SCRAMBLE", | |
| desc: "Data is scrambled with a keyed reduced-round PRINCE cipher in CTR mode." | |
| } | |
| { name: "ADDR.SCRAMBLE", | |
| desc: "Address is scrambled with a keyed lightweight permutation/diffusion function." | |
| } | |
| { name: "INSTR.BUS.LC_GATED", | |
| desc: "Prevent code execution from SRAM in non-test lifecycle states." | |
| } | |
| { name: "RAM_TL_LC_GATE.FSM.SPARSE", | |
| desc: "The control FSM inside the TL-UL gating primitive is sparsely encoded." | |
| } | |
| { name: "KEY.GLOBAL_ESC", | |
| desc: "Scrambling key and nonce are reset to a fixed value upon escalation, and bus transactions going to the memory will be blocked." | |
| } | |
| { name: "KEY.LOCAL_ESC", | |
| desc: "Scrambling key and nonce are reset to a fixed value upon local escalation due to bus integrity or counter errors, and bus transactions going to the memory will be blocked." | |
| } | |
| { name: "INIT.CTR.REDUN", | |
| desc: "The initialization counter is duplicated." | |
| } | |
| { name: "SCRAMBLE.KEY.SIDELOAD", | |
| desc: "The scrambling key is sideloaded from OTP and thus unreadable by SW." | |
| } | |
| { name: "TLUL_FIFO.CTR.REDUN", | |
| desc: "The TL-UL response FIFO pointers are implemented with duplicate counters." | |
| } | |
| ] | |
| regwidth: "32", | |
| registers: { | |
| regs: [ | |
| //////////////////////// | |
| // Ctrl / Status CSRs // | |
| //////////////////////// | |
| { name: "STATUS", | |
| desc: "SRAM status register.", | |
| swaccess: "ro", | |
| hwaccess: "hrw", | |
| hwqe: "false", | |
| fields: [ | |
| { bits: "0" | |
| name: "BUS_INTEG_ERROR" | |
| desc: ''' | |
| This bit is set to 1 if a fatal bus integrity fault is detected. | |
| This error triggers a fatal_error alert. | |
| This condition is terminal. | |
| ''', | |
| resval: 0, | |
| } | |
| { bits: "1" | |
| name: "INIT_ERROR" | |
| desc: ''' | |
| This bit is set to 1 if a the initialization counter has reached an invalid state. | |
| This error triggers a fatal_error alert. | |
| This condition is terminal. | |
| ''', | |
| resval: 0, | |
| } | |
| { bits: "2" | |
| name: "ESCALATED" | |
| desc: ''' | |
| Set to 1 if the sram controller has received an escalate request. | |
| If this is set to 1, the scrambling keys have been reset to the default values | |
| and all subsequent memory requests will be blocked. | |
| This condition is terminal. | |
| ''', | |
| resval: 0, | |
| } | |
| { bits: "3" | |
| hwaccess: "hwo", | |
| name: "SCR_KEY_VALID" | |
| desc: ''' | |
| Set to 1 if a new scrambling key has been successfully obtained from OTP. | |
| Note that if this is set to 0, the SRAM contents are still scrambled, but a | |
| default all-zero key and nonce are used to do so. | |
| ''', | |
| resval: 0, | |
| } | |
| { bits: "4" | |
| name: "SCR_KEY_SEED_VALID" | |
| desc: ''' | |
| Set to 1 if the scrambling key has been derived from a valid key seed in OTP. | |
| If !!STATUS.SCR_KEY_VALID is set to 1, !!STATUS.SCR_KEY_SEED_VALID should be 1 | |
| except for cases where the scrambling key seeds have not yet been provisioned to | |
| OTP. In such a case, the scrambling key is still ephemeral (i.e., it is derived | |
| using entropy from CSRNG), but a default all-zero value is used as the key seed. | |
| ''', | |
| resval: 0, | |
| } | |
| { bits: "5" | |
| name: "INIT_DONE" | |
| desc: ''' | |
| Set to 1 if the hardware initialization triggered via !!CTRL.INIT has completed. | |
| ''', | |
| resval: 0, | |
| } | |
| ] | |
| } | |
| { name: "EXEC_REGWEN", | |
| desc: "Lock register for execution enable register.", | |
| swaccess: "rw0c", | |
| hwaccess: "none", | |
| fields: [ | |
| { bits: "0" | |
| desc: ''' | |
| When cleared to zero, !!EXEC can not be written anymore. | |
| ''', | |
| resval: 1 | |
| } | |
| ] | |
| } | |
| { name: "EXEC", | |
| desc: "Sram execution enable.", | |
| swaccess: "rw", | |
| hwaccess: "hro", | |
| regwen: "EXEC_REGWEN" | |
| fields: [ | |
| { bits: "3:0", | |
| name: "EN", | |
| mubi: true, | |
| desc: ''' | |
| Write kMultiBitBool4True to this field to enable execution from SRAM. | |
| Note that this register only takes effect if the EN_SRAM_IFETCH switch | |
| in the OTP HW_CFG partition is set to kMultiBitBool8True. Otherwise execution | |
| from SRAM cannot be enabled via this register. | |
| ''', | |
| resval: false | |
| }, | |
| ] | |
| }, | |
| { name: "CTRL_REGWEN", | |
| desc: "Lock register for control register.", | |
| swaccess: "rw0c", | |
| hwaccess: "none", | |
| fields: [ | |
| { bits: "0" | |
| desc: ''' | |
| When cleared to zero, !!CTRL can not be written anymore. | |
| ''', | |
| resval: 1 | |
| } | |
| ] | |
| } | |
| { name: "CTRL", | |
| desc: "SRAM ctrl register.", | |
| swaccess: "wo", | |
| hwaccess: "hro", | |
| hwqe: "true", | |
| regwen: "CTRL_REGWEN" | |
| tags: [// avoid writing to CTRL, as this will cause STATUS to be modified | |
| "excl:CsrNonInitTests:CsrExclWrite"] | |
| fields: [ | |
| { bits: "0", | |
| name: "RENEW_SCR_KEY", | |
| desc: ''' | |
| Write 1 to request a new scrambling key from OTP. After writing to this register, SRAM transactions will | |
| be blocked until !!STATUS.SCR_KEY_VALID has been set to 1. If !!STATUS.SCR_KEY_VALID was already 1 | |
| before triggering a key renewal, hardware will automatically clear that status bit such that software | |
| can poll its status. Note that requesting a new scrambling key takes ~200 OTP cycles, which translates | |
| 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. | |
| ''' | |
| }, | |
| { bits: "1", | |
| name: "INIT", | |
| desc: ''' | |
| Write 1 to request memory init. | |
| The init mechanism uses an LFSR that is seeded with a part of the nonce supplied when requesting a scrambling key. | |
| Once seeded, the memory is initialized with pseudo-random data pulled from the LFSR. | |
| Note that !!CTRL.RENEW_SCR_KEY takes priority when writing 1 to both !!CTRL.RENEW_SCR_KEY and !!CTRL.INIT with the same write transaction. | |
| This means that the key request will complete first, followed by SRAM initialization. | |
| ''' | |
| }, | |
| ] | |
| }, | |
| ], | |
| ram: [ | |
| // no CSRs defined here. | |
| ] | |
| } | |
| } |