|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | { name: "otbn" | 
|  | clocking: [ | 
|  | {clock: "clk_i", reset: "rst_ni", idle: "idle_o", primary: true}, | 
|  | {clock: "clk_edn_i", reset: "rst_edn_ni", idle: "idle_o"}, | 
|  | {clock: "clk_otp_i", reset: "rst_otp_ni", idle: "idle_otp_o"} | 
|  | ] | 
|  | bus_interfaces: [ | 
|  | { protocol: "tlul", direction: "device" } | 
|  | ], | 
|  | param_list: [ | 
|  | { name:    "Stub", | 
|  | type:    "bit", | 
|  | default: "0", | 
|  | desc:    "Stub out the core of Otbn logic" | 
|  | local:   "false", | 
|  | expose:  "true" | 
|  | }, | 
|  | { name:    "RegFile", | 
|  | type:    "otbn_pkg::regfile_e", | 
|  | default: "otbn_pkg::RegFileFF", | 
|  | desc:    "Selection of the register file implementation. See otbn_pkg.sv." | 
|  | local:   "false", | 
|  | expose:  "true" | 
|  | }, | 
|  | { name:    "RndCnstUrndPrngSeed", | 
|  | type:    "otbn_pkg::urnd_prng_seed_t", | 
|  | desc:    ''' | 
|  | Default seed of the PRNG used for URND. | 
|  | ''' | 
|  | randcount: "256", | 
|  | randtype:  "data" | 
|  | }, | 
|  | { name: "RndCnstOtbnKey", | 
|  | type: "otp_ctrl_pkg::otbn_key_t", | 
|  | desc: ''' | 
|  | Compile-time random reset value for IMem/DMem scrambling key. | 
|  | ''' | 
|  | randcount: "128", | 
|  | randtype: "data" | 
|  | }, | 
|  | { name: "RndCnstOtbnNonce", | 
|  | type: "otp_ctrl_pkg::otbn_nonce_t", | 
|  | desc: ''' | 
|  | Compile-time random reset value for IMem/DMem scrambling nonce. | 
|  | ''' | 
|  | randcount: "64", | 
|  | randtype: "data" | 
|  | }, | 
|  | ] | 
|  | interrupt_list: [ | 
|  | { name: "done" | 
|  | desc: "OTBN has completed the operation." | 
|  | } | 
|  | ] | 
|  | alert_list: [ | 
|  | { name: "fatal" | 
|  | desc: "A fatal error. Fatal alerts are non-recoverable and will be asserted until a hard reset." | 
|  | } | 
|  | { name: "recov" | 
|  | desc: "A recoverable error. Just sent once (as the processor stops)." | 
|  | } | 
|  | ] | 
|  |  | 
|  | inter_signal_list: [ | 
|  | // Key request to OTP | 
|  | { struct:  "otbn_otp_key" | 
|  | type:    "req_rsp" | 
|  | name:    "otbn_otp_key" | 
|  | act:     "req" | 
|  | default: "'0" | 
|  | package: "otp_ctrl_pkg" | 
|  | }, | 
|  | // EDN interface for RND | 
|  | { struct:  "edn" | 
|  | type:    "req_rsp" | 
|  | name:    "edn_rnd" | 
|  | act:     "req" | 
|  | package: "edn_pkg" | 
|  | }, | 
|  |  | 
|  | // EDN interface for URND | 
|  | { struct:  "edn" | 
|  | type:    "req_rsp" | 
|  | name:    "edn_urnd" | 
|  | act:     "req" | 
|  | package: "edn_pkg" | 
|  | }, | 
|  |  | 
|  | // OTBN is not performing any operation and can be clock/power-gated. | 
|  | { name:    "idle", | 
|  | type:    "uni", | 
|  | struct:  "mubi4", | 
|  | width:   "1", | 
|  | act:     "req", | 
|  | package: "prim_mubi_pkg" | 
|  | }, | 
|  |  | 
|  | // ram configuration | 
|  | { struct:  "ram_1p_cfg", | 
|  | package: "prim_ram_1p_pkg", | 
|  | type:    "uni", | 
|  | name:    "ram_cfg", | 
|  | act:     "rcv" | 
|  | }, | 
|  |  | 
|  | // Lifecycle escalation | 
|  | { struct:  "lc_tx" | 
|  | type:    "uni" | 
|  | name:    "lc_escalate_en" | 
|  | act:     "rcv" | 
|  | default: "lc_ctrl_pkg::Off" | 
|  | package: "lc_ctrl_pkg" | 
|  | }, | 
|  |  | 
|  | // Key sideload | 
|  | { struct:  "otbn_key_req" | 
|  | type:    "uni" | 
|  | name:    "keymgr_key" | 
|  | act:     "rcv" | 
|  | package: "keymgr_pkg" | 
|  | }, | 
|  | ], | 
|  | countermeasures: [ | 
|  | { name: "BUS.INTEGRITY", | 
|  | desc: "End-to-end bus integrity scheme." | 
|  | } | 
|  | { name: "SCRAMBLE.KEY.SIDELOAD", | 
|  | desc: "The scrambling key is sideloaded from OTP and thus unreadable by SW." | 
|  | } | 
|  | ] | 
|  |  | 
|  | regwidth: "32" | 
|  | registers: [ | 
|  | // The magic values for EXECUTE, SEC_WIPE_DMEM and SEC_WIPE_IMEM in the CMD | 
|  | // register below were generated with the sparse-fsm-encode.py script: | 
|  | // | 
|  | //   util/design/sparse-fsm-encode.py -d 4 -m 3 -n 8 --avoid-zero -s 1 | 
|  | // | 
|  | // and have a hamming distance of at least 4 from one another and the zero | 
|  | // word. | 
|  | { name: "CMD" | 
|  | desc: ''' | 
|  | Command Register | 
|  |  | 
|  | A command initiates an OTBN operation. While performing the operation, | 
|  | OTBN is busy; the !!STATUS register reflects that. | 
|  |  | 
|  | All operations signal their completion by raising the done | 
|  | interrupt; alternatively, software may poll the !!STATUS register. | 
|  |  | 
|  | Writes are ignored if OTBN is not idle. | 
|  | Unrecognized commands are ignored. | 
|  | ''', | 
|  | swaccess: "wo", | 
|  | hwaccess: "hro", | 
|  | hwext: "true", | 
|  | hwqe:  "true", | 
|  | fields: [ | 
|  | { bits: "7:0" | 
|  | name: "cmd" | 
|  | resval: 0, | 
|  | desc: ''' | 
|  | The operation to perform. | 
|  |  | 
|  | <table> | 
|  | <thead> | 
|  | <tr> | 
|  | <th>Value</th> | 
|  | <th>Name</th> | 
|  | <th>Description</th> | 
|  | </tr> | 
|  | </thead> | 
|  | <tbody> | 
|  | <tr> | 
|  | <td>0xd8</td> | 
|  | <td>EXECUTE</td> | 
|  | <td> | 
|  | Starts the execution of the program stored in the | 
|  | instruction memory, starting at address zero. | 
|  | </td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0xc3</td> | 
|  | <td>SEC_WIPE_DMEM</td> | 
|  | <td>Securely removes all contents from the data memory.</td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0x1e</td> | 
|  | <td>SEC_WIPE_IMEM</td> | 
|  | <td> | 
|  | Securely removes all contents from the instruction memory. | 
|  | </td> | 
|  | </tr> | 
|  | </tbody> | 
|  | </table> | 
|  | ''' | 
|  | tags: [ | 
|  | // Don't write this field in the automated CSR tests: it would | 
|  | // start an operation! | 
|  | "excl:CsrAllTests:CsrExclWrite" | 
|  | ] | 
|  | } | 
|  | ], | 
|  | } | 
|  | { name: "CTRL", | 
|  | desc: "Control Register", | 
|  | hwext: "true", | 
|  | swaccess: "rw", | 
|  | hwaccess: "hrw", | 
|  | hwqe:  "true", | 
|  | fields: [ | 
|  | { bits: "0", | 
|  | name: "software_errs_fatal", | 
|  | resval: 0, | 
|  | desc: ''' | 
|  | Controls the reaction to software errors. | 
|  |  | 
|  | When set software errors produce fatal errors, rather than | 
|  | recoverable errors. | 
|  |  | 
|  | Writes are ignored if OTBN is not idle. | 
|  | ''' | 
|  | } | 
|  | ], | 
|  | } | 
|  | { name: "STATUS", | 
|  | desc: "Status Register", | 
|  | swaccess: "ro", | 
|  | hwaccess: "hrw", | 
|  | fields: [ | 
|  | { bits: "7:0", | 
|  | name: "status", | 
|  | resval: 0, | 
|  | // Note: Keep the list of status codes in sync with status_e in | 
|  | // otbn_pkg.sv. | 
|  | desc: ''' | 
|  | Indicates the current operational state OTBN is in. | 
|  |  | 
|  | All BUSY values represent an operation started by a write to the | 
|  | !!CMD register. | 
|  |  | 
|  | <table> | 
|  | <thead> | 
|  | <tr> | 
|  | <th>Value</th> | 
|  | <th>Name</th> | 
|  | <th>Description</th> | 
|  | </tr> | 
|  | </thead> | 
|  | <tbody> | 
|  | <tr> | 
|  | <td>0x00</td> | 
|  | <td>IDLE</td> | 
|  | <td>OTBN is idle: it is not performing any action.</td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0x01</td> | 
|  | <td>BUSY_EXECUTE</td> | 
|  | <td>OTBN is busy executing software.</td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0x02</td> | 
|  | <td>BUSY_SEC_WIPE_DMEM</td> | 
|  | <td>OTBN is busy securely wiping the data memory.</td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0x03</td> | 
|  | <td>BUSY_SEC_WIPE_IMEM</td> | 
|  | <td>OTBN is busy securely wiping the instruction memory.</td> | 
|  | </tr> | 
|  | <tr> | 
|  | <td>0xFF</td> | 
|  | <td>LOCKED</td> | 
|  | <td> | 
|  | OTBN is locked as reaction to a fatal error, and must be | 
|  | reset to unlock it again. See also the the section | 
|  | "Reaction to Fatal Errors". | 
|  | </td> | 
|  | </tr> | 
|  | </tbody> | 
|  | </table> | 
|  | ''' | 
|  | tags: [ | 
|  | // Don't write this field in the automated CSR tests. Despite this being RO for software | 
|  | // (so you might think we couldn't write it), the csr_hw_reset test does write this by | 
|  | // by default (using a backdoor access). The idea of that test is to write bogus values | 
|  | // to registers, reset the block and then check that everything has its reset value. | 
|  | // Doing this causes various assertions to fire and it isn't really necessary: we | 
|  | // already have strong checks between this register and the output of the model. | 
|  | "excl:CsrAllTests:CsrExclWrite" | 
|  | ] | 
|  | } | 
|  | ] | 
|  | } | 
|  | { name: "ERR_BITS", | 
|  | desc: ''' | 
|  | Operation Result Register | 
|  |  | 
|  | Describes the errors detected during an operation. | 
|  |  | 
|  | Refer to the "List of Errors" section for a detailed description of the | 
|  | errors. | 
|  |  | 
|  | The host CPU can clear this register when OTBN is not running, | 
|  | by writing any value. Write attempts while OTBN is running are ignored. | 
|  | ''', | 
|  | swaccess: "rw", | 
|  | hwaccess: "hrw", | 
|  | hwext: "true", | 
|  | hwqe:  "true", | 
|  | tags: [ | 
|  | // Don't use this register in the automated CSR tests. Its behaviour is | 
|  | // "write any value to clear", which we don't model in those tests. | 
|  | "excl:CsrAllTests:CsrExclWrite" | 
|  | ], | 
|  | fields: [ | 
|  | // Software errors | 
|  | { bits: "0", | 
|  | name: "bad_data_addr" | 
|  | resval: 0, | 
|  | desc: "A `BAD_DATA_ADDR` error was observed." | 
|  | } | 
|  | { bits: "1", | 
|  | name: "bad_insn_addr" | 
|  | resval: 0, | 
|  | desc: "A `BAD_INSN_ADDR` error was observed." | 
|  | } | 
|  | { bits: "2", | 
|  | resval: 0, | 
|  | name: "call_stack" | 
|  | desc: "A `CALL_STACK` error was observed." | 
|  | } | 
|  | { bits: "3", | 
|  | resval: 0, | 
|  | name: "illegal_insn" | 
|  | desc: "An `ILLEGAL_INSN` error was observed." | 
|  | } | 
|  | { bits: "4", | 
|  | name: "loop" | 
|  | resval: 0, | 
|  | desc: "A `LOOP` error was observed." | 
|  | } | 
|  | { bits: "5", | 
|  | name: "key_invalid" | 
|  | resval: 0, | 
|  | desc: "A `KEY_INVALID` error was observed." | 
|  | } | 
|  |  | 
|  | // Fatal errors. Keep in sync with list in FATAL_ALERT_CAUSE. | 
|  | { bits: "16", | 
|  | name: "imem_intg_violation" | 
|  | resval: 0, | 
|  | desc: "A `IMEM_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "17", | 
|  | name: "dmem_intg_violation" | 
|  | resval: 0, | 
|  | desc: "A `DMEM_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "18", | 
|  | name: "reg_intg_violation" | 
|  | resval: 0, | 
|  | desc: "A `REG_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "19", | 
|  | name: "bus_intg_violation" | 
|  | resval: 0, | 
|  | desc: "A `BUS_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "20", | 
|  | name: "bad_internal_state", | 
|  | resval: 0, | 
|  | desc: "A `BAD_INTERNAL_STATE` error was observed." | 
|  | } | 
|  | { bits: "21", | 
|  | name: "illegal_bus_access" | 
|  | resval: 0, | 
|  | desc: "An `ILLEGAL_BUS_ACCESS` error was observed." | 
|  | } | 
|  | { bits: "22", | 
|  | name: "lifecycle_escalation" | 
|  | resval: 0, | 
|  | desc: "A `LIFECYCLE_ESCALATION` error was observed." | 
|  | } | 
|  | { bits: "23", | 
|  | name: "fatal_software" | 
|  | resval: 0, | 
|  | desc: "A `FATAL_SOFTWARE` error was observed." | 
|  | } | 
|  | ] | 
|  | } | 
|  | { name: "FATAL_ALERT_CAUSE", | 
|  | desc: ''' | 
|  | Fatal Alert Cause Register | 
|  |  | 
|  | Describes any errors that led to a fatal alert. | 
|  | A fatal error puts OTBN in locked state; the value of this register | 
|  | does not change until OTBN is reset. | 
|  |  | 
|  | Refer to the "List of Errors" section for a detailed description of the | 
|  | errors. | 
|  | ''' | 
|  | swaccess: "ro", | 
|  | hwaccess: "hwo", | 
|  | fields: [ | 
|  | // Keep the list in sync with the fatal errors in ERR_BITS. | 
|  | { bits: "0", | 
|  | name: "imem_intg_violation", | 
|  | resval: 0, | 
|  | desc: "A `IMEM_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "1", | 
|  | name: "dmem_intg_violation", | 
|  | resval: 0, | 
|  | desc: "A `DMEM_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "2", | 
|  | name: "reg_intg_violation", | 
|  | resval: 0, | 
|  | desc: "A `REG_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "3", | 
|  | name: "bus_intg_violation", | 
|  | resval: 0, | 
|  | desc: "A `BUS_INTG_VIOLATION` error was observed." | 
|  | } | 
|  | { bits: "4", | 
|  | name: "bad_internal_state", | 
|  | resval: 0, | 
|  | desc: "A `BAD_INTERNAL_STATE` error was observed." | 
|  | } | 
|  | { bits: "5", | 
|  | name: "illegal_bus_access" | 
|  | resval: 0, | 
|  | desc: "A `ILLEGAL_BUS_ACCESS` error was observed." | 
|  | } | 
|  | { bits: "6", | 
|  | name: "lifecycle_escalation" | 
|  | resval: 0, | 
|  | desc: "A `LIFECYCLE_ESCALATION` error was observed." | 
|  | } | 
|  | { bits: "7", | 
|  | name: "fatal_software" | 
|  | resval: 0, | 
|  | desc: "A `FATAL_SOFTWARE` error was observed." | 
|  | } | 
|  | ] | 
|  | } | 
|  | { name: "INSN_CNT", | 
|  | desc: ''' | 
|  | Instruction Count Register | 
|  |  | 
|  | Returns the number of instructions executed in the current or last | 
|  | operation. The counter saturates at 2^32-1 and is reset to 0 at the | 
|  | start of a new operation. | 
|  |  | 
|  | Only the EXECUTE operation counts instructions; for all other operations | 
|  | this register remains at 0. Instructions triggering an error do not | 
|  | count towards the total. | 
|  |  | 
|  | Always reads as 0 if OTBN is locked. | 
|  |  | 
|  | The host CPU can clear this register when OTBN is not running, | 
|  | by writing any value. Write attempts while OTBN is running are ignored. | 
|  | ''', | 
|  | swaccess: "rw", | 
|  | hwaccess: "hrw", | 
|  | hwext: "true", | 
|  | hwqe:  "true", | 
|  | fields: [ | 
|  | { bits: "31:0", | 
|  | name: "insn_cnt", | 
|  | resval: 0, | 
|  | desc: ''' | 
|  | The number of executed instructions. | 
|  | ''' | 
|  | tags: [ | 
|  | // Don't write this field in the automated CSR tests. Like | 
|  | // ERR_BITS, its behaviour is "write any value to clear", which we | 
|  | // don't model in those tests. | 
|  | "excl:CsrAllTests:CsrExclWrite" | 
|  | ] | 
|  | } | 
|  | ] | 
|  | } | 
|  | { name: "LOAD_CHECKSUM", | 
|  | desc: ''' | 
|  | A 32-bit CRC checksum of data written to memory | 
|  |  | 
|  | See the "Memory Load Integrity" section of the manual for full details. | 
|  | ''' | 
|  | hwext: "true", | 
|  | hwqe:  "true", | 
|  | swaccess: "rw", | 
|  | hwaccess: "hrw", | 
|  | fields: [ | 
|  | { bits: "31:0", | 
|  | name: "checksum", | 
|  | resval: 0, | 
|  | desc: "Checksum accumulator" | 
|  | } | 
|  | ] | 
|  | tags: [ | 
|  | // Don't read this field in any of the automated CSR tests (they | 
|  | // will predict the value wrongly because they don't know that it | 
|  | // updates on memory writes). | 
|  | "excl:CsrAllTests:CsrExclAll" | 
|  | ] | 
|  | } | 
|  |  | 
|  | // Give IMEM and DMEM 16 KiB address space, each, to allow for easy expansion | 
|  | // of the actual IMEM and DMEM sizes without changing the address map. | 
|  | { skipto: "0x4000" } | 
|  |  | 
|  | // Imem size (given as `items` below) must be a power of two. | 
|  | { window: { | 
|  | name: "IMEM", | 
|  | items: "1024", // 4 kB | 
|  | swaccess: "rw", | 
|  | data-intg-passthru: "true", | 
|  | byte-write: "false", | 
|  | desc: ''' | 
|  | Instruction Memory Access | 
|  |  | 
|  | The instruction memory may only be accessed through this window | 
|  | while OTBN is idle. | 
|  |  | 
|  | If OTBN is busy or locked, read accesses return 0 and write accesses | 
|  | are ignored. | 
|  | If OTBN is busy, any access additionally triggers an | 
|  | ILLEGAL_BUS_ACCESS fatal error. | 
|  | ''' | 
|  | } | 
|  | } | 
|  |  | 
|  | { skipto: "0x8000" } | 
|  |  | 
|  | // Dmem size (given as `items` below) must be a power of two. | 
|  | { window: { | 
|  | name: "DMEM", | 
|  | items: "512", // 2 kB visible over the bus | 
|  | swaccess: "rw", | 
|  | data-intg-passthru: "true", | 
|  | byte-write: "false", | 
|  | desc: ''' | 
|  | Data Memory Access | 
|  |  | 
|  | The data memory may only be accessed through this window while OTBN | 
|  | is idle. | 
|  |  | 
|  | If OTBN is busy or locked, read accesses return 0 and write accesses | 
|  | are ignored. | 
|  | If OTBN is busy, any access additionally triggers an | 
|  | ILLEGAL_BUS_ACCESS fatal error. | 
|  |  | 
|  | Note that DMEM is actually 4kiB in size, but only the first 2kiB of | 
|  | the memory is visible through this register interface. | 
|  | ''' | 
|  | } | 
|  | } | 
|  | ] | 
|  | } |