| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| { name: "otp_ctrl", |
| clock_primary: "clk_i", |
| bus_device: "tlul", |
| bus_host: "none", |
| |
| /////////////////////////// |
| // Interrupts and Alerts // |
| /////////////////////////// |
| |
| interrupt_list: [ |
| { name: "otp_operation_done", |
| desc: "A direct access command or digest calculation operation has completed." |
| } |
| { name: "otp_error", |
| desc: "An error has occurred in the OTP controller. Check the ERR_CODE register to get more information." |
| } |
| ], |
| |
| alert_list: [ |
| { name: "otp_integrity_mismatch", |
| desc: "This alert triggers if hardware detects a parity bit or digest error in the buffered partitions.", |
| } |
| { name: "otp_consistency_mismatch", |
| desc: "This alert triggers if the digest over the buffered registers does not match with the digest stored in OTP.", |
| } |
| ], |
| |
| //////////////// |
| // Parameters // |
| //////////////// |
| param_list: [ |
| { name: "OtpByteAddressWidth", |
| desc: "Width of the OTP Byte address.", |
| type: "int", |
| default: "11", |
| local: "true" |
| }, |
| { name: "NumCreatorSwCfgPartitionWords", |
| desc: "Number of 32bit words in the software config partitions.", |
| type: "int", |
| default: "192", |
| local: "true" |
| }, |
| { name: "NumOwnerSwCfgPartitionWords", |
| desc: "Number of 32bit words in the software config partitions.", |
| type: "int", |
| default: "192", |
| local: "true" |
| }, |
| { name: "NumHwCfgWords", |
| desc: "Number of 32bit words in the hardware config partition.", |
| type: "int", |
| default: "50", |
| local: "true" |
| }, |
| { name: "NumLcPartitionWords", |
| desc: "Number of 32bit words in the life cycle partition.", |
| type: "int", |
| default: "22", |
| local: "true" |
| }, |
| { name: "NumSecret0PartitionWords", |
| desc: "Number of 32bit words in the secret0 partition.", |
| type: "int", |
| default: "10", |
| local: "true" |
| }, |
| { name: "NumSecret1PartitionWords", |
| desc: "Number of 32bit words in the secret1 partition.", |
| type: "int", |
| default: "14", |
| local: "true" |
| }, |
| { name: "NumSecret2PartitionWords", |
| desc: "Number of 32bit words in the secret2 partition.", |
| type: "int", |
| default: "32", |
| local: "true" |
| }, |
| { name: "NumDebugWindowWords", |
| desc: "Number of 32bit words in the debug window.", |
| type: "int", |
| default: "500", |
| local: "true" |
| }, |
| ] |
| |
| ///////////////////////////// |
| // Intermodule Connections // |
| ///////////////////////////// |
| // TODO: these need to be refined during implementation and integration |
| inter_signal_list: [ |
| // CSRNG interface |
| { struct: "otp_csrng" |
| type: "req_rsp" |
| name: "otp_csrng" |
| act: "req" |
| package: "otp_ctrl_pkg" |
| } |
| // Power manager init command |
| { struct: "pwr_otp" |
| type: "req_rsp" |
| name: "pwr_otp_init" |
| act: "rsp" |
| package: "pwrmgr_pkg" |
| } |
| // Status output to power manager |
| { struct: "otp_pwr_state" |
| type: "uni" |
| name: "otp_pwr_state" |
| act: "req" |
| package: "otp_ctrl_pkg" |
| } |
| // LC transition command |
| { struct: "lc_otp_program" |
| type: "req_rsp" |
| name: "lc_otp_program" |
| act: "rsp" |
| package: "otp_ctrl_pkg" |
| } |
| // Broadcast to LC |
| { struct: "otp_lc_data" |
| type: "uni" |
| name: "otp_lc_data" |
| act: "req" |
| package: "otp_ctrl_pkg" |
| } |
| // Broadcast from LC |
| { struct: "lc_tx" |
| type: "uni" |
| name: "lc_provision_en" |
| act: "rcv" |
| package: "lifecycle_pkg" // TODO: move to LC package? |
| } |
| { struct: "lc_tx" |
| type: "uni" |
| name: "lc_test_en" |
| act: "rcv" |
| package: "lifecycle_pkg" // TODO: move to LC package? |
| } |
| // Broadcast to Key Manager |
| { struct: "keymgr_key" |
| type: "uni" |
| name: "otp_keymgr_key" |
| act: "req" |
| package: "otp_ctrl_pkg" // TODO: move this to keymgr package? |
| } |
| // Broadcast to Flash Controller |
| { struct: "flash_key" |
| type: "uni" |
| name: "otp_flash_key" |
| act: "req" |
| package: "otp_ctrl_pkg" |
| } |
| // Key request from Main RAM Scrambler |
| { struct: "ram_main_key" |
| type: "req_rsp" |
| name: "otp_ram_main_key" |
| act: "rsp" |
| package: "otp_ctrl_pkg" |
| } |
| // Key request from Retention RAM Scrambler |
| { struct: "ram_ret_aon_key" |
| type: "req_rsp" |
| name: "otp_ram_ret_aon_key" |
| act: "rsp" |
| package: "otp_ctrl_pkg" |
| } |
| // Key request from OTBN RAM Scrambler |
| { struct: "otbn_ram_key" |
| type: "req_rsp" |
| name: "otp_otbn_ram_key" |
| act: "rsp" |
| package: "otp_ctrl_pkg" |
| } |
| ] // inter_signal_list |
| |
| regwidth: "32", |
| registers: [ |
| |
| //////////////////////// |
| // Ctrl / Status CSRs // |
| //////////////////////// |
| // TODO: this may have to be refined during implementation. |
| { name: "STATUS", |
| desc: "OTP status register.", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| fields: [ |
| { bits: "2:0" |
| enum: [ |
| { value: "0", |
| name: "IDLE", |
| desc: ''' |
| OTP is in IDLE state and ready to accept commands. |
| ''' |
| }, |
| { value: "1", |
| name: "INIT", |
| desc: ''' |
| OTP is initializing. |
| ''' |
| }, |
| { value: "2", |
| name: "ERROR", |
| desc: ''' |
| An error condition has occurred. |
| Please check the ERR_CODE register for specifics. |
| ''' |
| }, |
| { value: "3", |
| name: "READ_PENDING", |
| desc: ''' |
| A read operation is pending. |
| ''' |
| }, |
| { value: "4", |
| name: "WRITE_PENDING", |
| desc: ''' |
| A write operation is pending. |
| ''' |
| }, |
| ] |
| } |
| ] |
| } |
| // TODO: need to define error codes for errors during background operations (or add alerts for them) |
| { name: "ERR_CODE", |
| desc: '''This register holds information on error conditions and should be |
| checked when !!STATUS indicates that an error has occurred, or when |
| an !!INTR_STATE.otp_error has been triggered. |
| ''' |
| swaccess: "ro", |
| hwaccess: "hwo", |
| fields: [ |
| { |
| bits: "2:0" |
| enum: [ |
| { value: "0", |
| name: "NONE", |
| desc: ''' |
| No error condition has occurred. |
| ''' |
| }, |
| { value: "1", |
| name: "MISSING_INIT", |
| desc: ''' |
| Read or write operation attempted before initialization. |
| The OTP controller automatically recovers from this error when initializing. |
| ''' |
| }, |
| { value: "2", |
| name: "INVALID_CMD", |
| desc: ''' |
| Invalid command has been written to !!DIRECT_ACCESS_CMD or the digest calculation registers. |
| The OTP controller automatically recovers from this error when issuing a new command. |
| ''' |
| }, |
| { value: "3", |
| name: "READ_CORR", |
| desc: ''' |
| A correctable error has occured during a read operation. |
| The OTP controller automatically recovers from this error when issuing a new command. |
| ''' |
| }, |
| { value: "4", |
| name: "READ_UNCORR", |
| desc: ''' |
| An uncorrectable error has occurred during a read operation. |
| The OTP controller automatically recovers from this error when issuing a new command. |
| ''' |
| }, |
| { value: "5", |
| name: "READ_ERR", |
| desc: ''' |
| An error has occurred during a read operation. |
| The OTP controller may not be able to automatically recover from this error and has to be reset. |
| ''' |
| }, |
| { value: "6", |
| name: "WRITE_ERR", |
| desc: ''' |
| An error has occurred during a programming operation. |
| The OTP controller may not be able to automatically recover from this error and has to be reset. |
| ''' |
| }, |
| { value: "7", |
| name: "ESCALATED", |
| desc: ''' |
| OTP has been rendered unusable due to an escalation. |
| This is a terminal state. |
| ''' |
| }, |
| ] |
| } |
| ] |
| } |
| { name: "DIRECT_ACCESS_REGWEN", |
| desc: ''' |
| Register write enable for all direct access interface registers. |
| ''', |
| swaccess: "rw", |
| hwaccess: "hrw", |
| fields: [ |
| { |
| bits: "0", |
| desc: ''' When true, the direct access interface registers become writable. |
| Otherwise, all direct access interface registers are write protected. |
| Note that the DAI hardware will temporarily set this to 0 when a transaction is in progress. |
| ''' |
| resval: 0, |
| }, |
| ] |
| }, |
| { name: "DIRECT_ACCESS_CMD", |
| desc: "Command register for direct accesses.", |
| swaccess: "r0w1c", |
| hwaccess: "hro", |
| hwqe: "true", |
| regwen: "DIRECT_ACCESS_REGWEN", |
| fields: [ |
| { bits: "0", |
| name: "READ", |
| desc: ''' |
| Initiates a readout sequence that reads the location specified |
| by !!DIRECT_ACCESS_ADDRESS. The command places the data read into |
| !!DIRECT_ACCESS_RDATA_0 and !!DIRECT_ACCESS_RDATA_1 (for 64bit partitions). |
| ''' |
| } |
| { bits: "1", |
| name: "WRITE", |
| desc: ''' |
| Initiates a programming sequence that writes the data in !!DIRECT_ACCESS_WDATA_0 |
| and !!DIRECT_ACCESS_WDATA_1 (for 64bit partitions) to the location specified by |
| !!DIRECT_ACCESS_ADDRESS. |
| ''' |
| } |
| { bits: "2", |
| name: "DIGEST", |
| desc: ''' |
| Initiates the digest calculation and locking sequence for the partition specified by |
| !!DIRECT_ACCESS_ADDRESS. |
| ''' |
| } |
| ] |
| } |
| { name: "DIRECT_ACCESS_ADDRESS", |
| desc: "Address register for direct accesses.", |
| swaccess: "rw", |
| hwaccess: "hro", |
| regwen: "DIRECT_ACCESS_REGWEN", |
| fields: [ |
| { bits: "OtpByteAddressWidth-1:0", |
| desc: ''' |
| This is the address for the OTP word to be read or written through |
| the direct access interface. Note that the address is aligned to the access size |
| internally, hence bits 1:0 are ignored for 32bit accesses, and bits 2:0 are ignored |
| for 64bit accesses. |
| |
| For the digest calculation command, set this register to the appropriate partition index |
| (0x2: !!HW_CFG_DIGEST, 0x4: !!SECRET0_DIGEST, 0x5: !!SECRET1_DIGEST and 0x6: !!SECRET2_DIGEST). |
| ''' |
| } |
| ] |
| } |
| { multireg: { |
| name: "DIRECT_ACCESS_WDATA", |
| desc: '''Write data for direct accesses. |
| Hardware automatically determines the access granule (32bit or 64bit) based on which |
| partition is being written to. |
| ''', |
| count: "2", // 2 x 32bit = 64bit |
| swaccess: "rw", |
| hwaccess: "hro", |
| regwen: "DIRECT_ACCESS_REGWEN", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "DIRECT_ACCESS_RDATA", |
| desc: '''Read data for direct accesses. |
| Hardware automatically determines the access granule (32bit or 64bit) based on which |
| partition is read from. |
| ''', |
| count: "2", // 2 x 32bit = 64bit |
| swaccess: "ro", |
| hwaccess: "hwo", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { name: "CHECK_PERIOD_REGEN", |
| desc: ''' |
| Register write enable for !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB. |
| ''', |
| swaccess: "rw1c", |
| hwaccess: "hro", |
| fields: [ |
| { bits: "0", |
| desc: ''' |
| When true, !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB registers cannot be written anymore. |
| ''' |
| resval: 1, |
| }, |
| ] |
| }, |
| { name: "INTEGRITY_CHECK_PERIOD_MSB", |
| desc: ''' |
| This value specifies the maximum period that can be generated pseudo-randomly. |
| Only applies to the HW_CFG and SECRET partitions if they are locked. |
| ''' |
| swaccess: "rw", |
| hwaccess: "hro", |
| regwen: "CHECK_PERIOD_REGEN", |
| fields: [ |
| { bits: "5:0", |
| desc: ''' |
| The pseudo-random period is generated using a 40bit LFSR internally, and this value defines |
| the bit mask to be applied to the LFSR output in order to limit its range. A value of N will generate |
| an internal mask of 2^N-1. So for N=16 this would allow the maximum pseudo-random period to be 0xFFFF cycles. |
| The default value has been set to 25, which corresponds to a maximum period of a bit more than 1.3s at 25MHz. |
| ''' |
| resval: "25" |
| } |
| ] |
| } |
| { name: "CONSISTENCY_CHECK_PERIOD_MSB", |
| desc: ''' |
| This value specifies the maximum period that can be generated pseudo-randomly. |
| This applies to the LIFE_CYCLE partition and the HW_CFG and SECRET partitions (but only if they are locked). |
| ''' |
| swaccess: "rw", |
| hwaccess: "hro", |
| regwen: "CHECK_PERIOD_REGEN", |
| fields: [ |
| { bits: "5:0", |
| desc: ''' |
| The pseudo-random period is generated using a 40bit LFSR internally, and this value defines |
| the bit mask to be applied to the LFSR output in order to limit its range. A value of N will generate |
| an internal mask of 2^N-1. So for N=16 this would allow the maximum pseudo-random period to be 0xFFFF cycles. |
| The default value has been set to 34, which corresponds to a maximum period of a bit more than 687s at 25MHz. |
| ''' |
| resval: "34" |
| } |
| ] |
| } |
| |
| //////////////////////////////////// |
| // Dynamic Locks of SW Parititons // |
| //////////////////////////////////// |
| { name: "CREATOR_SW_CFG_READ_LOCK", |
| desc: ''' |
| Runtime read lock for the creator software partition. |
| ''', |
| swaccess: "rw1c", |
| hwaccess: "hro", |
| fields: [ |
| { bits: "0", |
| desc: ''' |
| When true, read access to the !!CREATOR_SW_CFG partition is locked. |
| ''' |
| resval: 1, |
| }, |
| ] |
| }, |
| { name: "OWNER_SW_CFG_READ_LOCK", |
| desc: ''' |
| Runtime read lock for the owner software partition. |
| ''', |
| swaccess: "rw1c", |
| hwaccess: "hro", |
| fields: [ |
| { bits: "0", |
| desc: ''' |
| When true, read access to the !!OWNER_SW_CFG partition is locked. |
| ''' |
| resval: 1, |
| }, |
| ] |
| }, |
| |
| /////////////////////// |
| // Integrity Digests // |
| /////////////////////// |
| { multireg: { |
| name: "CREATOR_SW_CFG_DIGEST", |
| desc: ''' |
| Integrity digest for the creator software config partition. |
| The integrity digest is 0 by default. Software must write this |
| digest value via the direct access interface in order to lock the partition. |
| After a reset, write access to the !!OWNER_SW_CFG partition is locked and |
| the digest becomes visible in this CSR. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "OWNER_SW_CFG_DIGEST", |
| desc: ''' |
| Integrity digest for the owner software config partition. |
| The integrity digest is 0 by default. Software must write this |
| digest value via the direct access interface in order to lock the partition. |
| After a reset, write access to the !!OWNER_SW_CFG partition is locked and |
| the digest becomes visible in this CSR. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "HW_CFG_DIGEST", |
| desc: ''' |
| Hardware config partition integrity digest. |
| The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD. |
| After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "SECRET0_DIGEST", |
| desc: ''' |
| Integrity digest for the SECRET0 partition. |
| The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD. |
| After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "SECRET1_DIGEST", |
| desc: ''' |
| Integrity digest for the SECRET1 partition. |
| The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD. |
| After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| { multireg: { |
| name: "SECRET2_DIGEST", |
| desc: ''' |
| Integrity digest for the SECRET2 partition. |
| The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD. |
| After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked. |
| ''', |
| count: "2", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| hwext: "true", |
| cname: "WORD", |
| fields: [ |
| { bits: "31:0" |
| } |
| ] |
| } |
| }, |
| |
| /////////////////////////////// |
| // Hardware Config Partition // |
| /////////////////////////////// |
| { skipto: "0x100" } |
| |
| // TODO: there is likely more collateral that needs to be stored in the HW_CFG partition. |
| // add them as they become apparent. |
| |
| ////////////////////////// |
| // Life Cycle Partition // |
| ////////////////////////// |
| { skipto: "0x200" } |
| |
| { multireg: { |
| name: "LC_STATE", |
| desc: ''' |
| Life cycle state, comprised of 12 16bit words. |
| TODO: add link to LC controller spec. |
| Other values than specified below are invalid. |
| ''', |
| count: "12", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| cname: "HALFWORD", |
| fields: [ |
| { bits: "15:0" |
| enum: [ |
| { value: "0", |
| name: "BLANK", |
| desc: ''' |
| 0x0000 |
| ''' |
| }, |
| { value: "62970", |
| name: "SET", |
| desc: ''' |
| 0xF5FA |
| ''' |
| }, |
| ] |
| } |
| ] |
| } |
| }, |
| { name: "TRANSITION_CNT", |
| desc: "Counter for total amount of state transition attempts.", |
| swaccess: "ro", |
| hwaccess: "hwo", |
| fields: [ |
| { bits: "31:0", |
| desc: ''' |
| This counter will be incremented upon each state transition attempt, |
| or when the transition command coming from the life cycle controller is invalid. |
| ''' |
| } |
| ] |
| } |
| |
| /////////////////////////////// |
| // Software Config Partition // |
| /////////////////////////////// |
| { skipto: "0x300" } |
| |
| { window: { |
| name: "CREATOR_SW_CFG" |
| items: "NumCreatorSwCfgPartitionWords" |
| swaccess: "ro", |
| desc: ''' |
| Any read to this window directly maps to the corresponding offset in the creator software |
| config partition, and triggers an OTP readout of the Bytes requested. Note that the transaction |
| will block until OTP readout has completed. |
| ''' |
| } |
| } |
| |
| { skipto: "0x700" } |
| |
| { window: { |
| name: "OWNER_SW_CFG" |
| items: "NumOwnerSwCfgPartitionWords" |
| swaccess: "ro", |
| desc: ''' |
| Any read to this window directly maps to the corresponding offset in the owner software |
| config partition, and triggers an OTP readout of the Bytes requested. Note that the transaction |
| will block until OTP readout has completed. |
| ''' |
| } |
| } |
| |
| ////////////////////// |
| // Test Access Port // |
| ////////////////////// |
| { skipto: "0xB00" } |
| |
| // TODO: may have to update description, once it is known how RAW unlock is handled. |
| { window: { |
| name: "TEST_ACCESS" |
| items: "NumDebugWindowWords" |
| swaccess: "rw", |
| desc: ''' |
| This maps to the register file of the proprietary OTP macro. Note that this is only |
| accessible during the TEST life cycle state. |
| ''' |
| } |
| } |
| ], |
| } |