OTP_CTRL DV document

Goals

  • DV
    • Verify all OTP_CTRL IP features by running dynamic simulations with a SV/UVM based testbench
    • Develop and run all tests based on the testplan below towards closing code and functional coverage on the IP and all of its sub-modules
  • FPV
    • Verify TileLink device protocol compliance with an SVA based testbench

Current status

Design features

For detailed information on OTP_CTRL design features, please see the OTP_CTRL HW IP technical specification.

Testbench architecture

OTP_CTRL testbench has been constructed based on the CIP testbench architecture.

Block diagram

Block diagram

Top level testbench

Top level testbench is located at hw/ip/otp_ctrl/dv/tb.sv. It instantiates the OTP_CTRL DUT module hw/ip/otp_ctrl/rtl/otp_ctrl.sv. In addition, it instantiates the following interfaces, connects them to the DUT and sets their handle into uvm_config_db:

Common DV utility components

The following utilities provide generic helper tasks and functions to perform activities that are common across the project:

Global types & methods

All common types and methods defined at the package level can be found in otp_ctrl_env_pkg. Some of them in use are:

  parameter uint SCRAMBLE_DATA_SIZE = 64;
  parameter uint SCRAMBLE_KEY_SIZE  = 128;
  parameter uint NUM_ROUND          = 31;

  typedef enum bit [2:0] {
    OtpNoError,
    OtpMacroError,
    OtpMacroEccCorrError,
    OtpMacroEccUncorrError,
    OtpMacroWriteBlankError,
    OtpAccessError,
    OtpCheckFailError,
    OtpFsmStateError
  } otp_err_code_e;

TL_agent

OTP_CTRL testbench instantiates (already handled in CIP base env) tl_agent, which provides the ability to drive and independently monitor random traffic via TL host interface into OTP_CTRL device.

Alert_agents

OTP_CTRL testbench instantiates (already handled in CIP base env) two alert_agents: fatal_check_alert and fatal_macro_alert. The alert_agents provide the ability to drive and independently monitor alert handshakes via alert interfaces in OTP_CTRL device.

OTP_CTRL interface

OTP_CTRL design has specific inputs and outputs to communicate with other IPs including LC_CTRL, OTBN, SRAM, FLASH etc. This interface is created to initialize, use simple task to drive, and use assertions to monitor these signals.

Memory backdoor interface

OTP_CTRL testbench binds design’s non-volatile OTP memory with a mem_bkdr_util, which supports read, write, and injection of ECC errors to design’s OTP memory.

UVM RAL model

The OTP_CTRL RAL model is created with the ralgen FuseSoC generator script automatically when the simulation is at the build stage.

It can be created manually by invoking regtool.

Reference models

The OTP_CTRL’s utilizes [PRESENT](/hw/ip/prim/doc/prim_present/ as the cipher to scramble and protect secrets. Thus OTP_CTRL’s scoreboard adopted PRESENT’s C reference model, located under hw/ip/prim/dv/prim_present/ folder, for encryption and decryption purpose.

Stimulus strategy

Test sequences

All test sequences reside in hw/ip/otp_ctrl/dv/env/seq_lib. The otp_ctrl_base_vseq virtual sequence is extended from cip_base_vseq and serves as a starting point. All test sequences are extended from otp_ctrl_base_vseq. It provides commonly used handles, variables, functions and tasks that the test sequences can simple use / call. Some of the most commonly used tasks / functions are as follows:

  • dai_wr: This task triggers an OTP write sequence via the DAI interface.
  • dai_rd: This task triggers an OTP read sequence via the DAI interface.
  • trigger_checks: This task triggers a one-time OTP check and user can choose to trigger consistency check or integrity check.
  • randomize_dai_addr: This task takes a DAI address as input and randomize its last two bits, because the last two bits should be ignored in design.

Functional coverage

To ensure high quality constrained random stimulus, it is necessary to develop a functional coverage model. The following two files declared OTP_CTRL’s covergroups:

  • dv/env/otp_ctrl_env_cov.sv declares functional or CSR related covergroups. The functional coverage is collected manually inside OTP_CTRL’s scoreboard by invoking the sample function.
  • dv/cov/otp_ctrl_cov_if.sv declares interface signal related covergroups. The functional coverage is collected automatically when the sampled signal is active.

Self-checking strategy

Scoreboard

The otp_ctrl_scoreboard is primarily used for end to end checking. It creates the following analysis ports to retrieve the data monitored by corresponding interface agents:

  • tl_a_chan_fifo: tl address channel
  • tl_d_chan_fifo: tl data channel
  • alert_fifos: alert handshakes
  • sram_fifos: sram requests
  • otbn_fifo: otbn request
  • lc_prog_fifo: life cycle programming request
  • lc_token_fifo: life cycle token request
  • flash_addr_fifo: flash address request
  • flash_data_fifo: flash data request
  • edn_fifo: edn response to OTP_CTRL

For all requests to OTP_CTRL as listed above, scoreboard has a corresponding task to process request, check OTP_CTRL’s response value against encryption, and collect coverage.

OTP_CTRL’s scoreboard has an internal array otp_a that tracks OTP memory data. Every successful OTP write operation will update this internal array, and every successful OTP read operation will check the readout value against this internal array. Note that in design, secret partitions will go through a encryption before writing to the actually OTP memory, and will be decrypted upon a read request. For the simplicity of this internal array, we will skip this procedure. However, if scoreboard backdoor read any secret partitions, we will decrypt the data then write the decrypted data to the internal array. For any operation that fails, the scoreboard will predict the status and err_code according to the failure type. If the error can trigger alert, scoreboard will use set_exp_alert task to check if the alert is firing correctly. If a HW digest operation is triggered by sequence, scoreboard will calculate digest value with partition data from its internal array and update the digest value. According to design spec, scoreboard won’t lock the partition and predict the digest value to digest registers until next power cycle.

If a reset or lc_escalation_en is issued during an OTP_CTRL write operation, scoreboard cannot accurately predict how many bits have been programmed into OTP memory. To avoid mismatches, scoreboard utilizes flags dai_wr_ip and dai_digest_ip to track otp write operations, and issue a backdoor read if the write operation is interrupted.

Assertions

  • TLUL assertions: The tb/otp_ctrl_bind.sv binds the tlul_assert assertions to the IP to ensure TileLink interface protocol compliance.
  • Unknown checks on DUT outputs: The RTL has assertions to ensure all outputs are initialized to known values after coming out of reset.
  • OTP_CTRL_IF assertions: This interface has assertions to ensure certain OTP_CTRL’s outputs (such as: otp_hw_cfg_o, keymgr_key_o) are stable after OTP initialization.

Building and running tests

We are using our in-house developed regression tool for building and running our tests and regressions. Please take a look at the link for detailed information on the usage, capabilities, features and known issues. Here’s how to run a smoke test:

$ $REPO_TOP/util/dvsim/dvsim.py $REPO_TOP/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson -i otp_ctrl_smoke

Testplan

Testpoints

Milestone Name Tests Description
V1 wake_up otp_ctrl_wake_up

Wake_up test walks through otp_ctrl's power-on initialization, read, program, and digest functionalities.

  • drive pwrmgr's request pin to trigger OTP initialization after reset, check status after OTP initialization
  • write all-ones to a random address within OTP partition 0, wait until this operation completes
  • read out the random selected write address, check if the readout value is all-ones
  • trigger a digest calculation for a Software partition, check if the OtpError interrupt is set
  • trigger a digest calculation for a non-software partition, expect operation completes without the OtpError interrupt
  • read out secrets through the hardware interfaces
V1 smoke otp_ctrl_smoke

OTP_CTRL smoke test provisions and locks partitions.

  • drive pwrmgr's request pin to trigger OTP initialization after reset, check status after OTP initialization
  • randomly read out keys pertaining to key_manager, flash, sram, otbn
  • randomly issue LC program request
  • write random values to random addresses within each OTP partition
  • read out the random selected write addresses, check if the readout values are expected
  • during read and write operations, check if direct_access_regwen is correctly set by HW
  • perform a system-level reset and check corresponding CSRs are set correctly
  • lock all partitions except life_cycle by triggering digest calculations
  • read back and verify the digest
  • perform a system-level reset to verify the corresponding CSRs exposing the digests have been populated
V1 csr_hw_reset otp_ctrl_csr_hw_reset

Verify the reset values as indicated in the RAL specification.

  • Write all CSRs with a random value.
  • Apply reset to the DUT as well as the RAL model.
  • Read each CSR and compare it against the reset value. it is mandatory to replicate this test for each reset that affects all or a subset of the CSRs.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_rw otp_ctrl_csr_rw

Verify accessibility of CSRs as indicated in the RAL specification.

  • Loop through each CSR to write it with a random value.
  • Read the CSR back and check for correctness while adhering to its access policies.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_bit_bash otp_ctrl_csr_bit_bash

Verify no aliasing within individual bits of a CSR.

  • Walk a 1 through each CSR by flipping 1 bit at a time.
  • Read the CSR back and check for correctness while adhering to its access policies.
  • This verify that writing a specific bit within the CSR did not affect any of the other bits.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_aliasing otp_ctrl_csr_aliasing

Verify no aliasing within the CSR address space.

  • Loop through each CSR to write it with a random value
  • Shuffle and read ALL CSRs back.
  • All CSRs except for the one that was written in this iteration should read back the previous value.
  • The CSR that was written in this iteration is checked for correctness while adhering to its access policies.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_mem_rw_with_rand_resetotp_ctrl_csr_mem_rw_with_rand_reset

Verify random reset during CSR/memory access.

  • Run csr_rw sequence to randomly access CSRs
  • If memory exists, run mem_partial_access in parallel with csr_rw
  • Randomly issue reset and then use hw_reset sequence to check all CSRs are reset to default value
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
V1 mem_walk otp_ctrl_mem_walk

Verify accessibility of all memories in the design.

  • Run the standard UVM mem walk sequence on all memories in the RAL model.
  • It is mandatory to run this test from all available interfaces the memories are accessible from.
V1 mem_partial_access otp_ctrl_mem_partial_access

Verify partial-accessibility of all memories in the design.

  • Do partial reads and writes into the memories and verify the outcome for correctness.
  • Also test outstanding access on memories
V2 dai_access_partition_walk otp_ctrl_partition_walk

Similar to UVM's memory walk test, this test ensures every address in each partition can be accessed successfully via DAI and TLUL interfacs according to its access policy.

V2 init_fail otp_ctrl_init_fail

Based on OTP_CTRL smoke test, this test creates OTP_CTRL's initialization failure:

  • write and read OTP memory via DAI interface
  • randomly issue DAI digest command to lock HW partitions
  • keep writing to OTP memory via DAI interface without asserting reset
  • if digests are not locked, backdoor inject ECC correctable or uncorrectable errors
  • issue reset and power initialization
  • if the injected errors are all correctable errors, disable the lc_bypass_chk_en after LC program request to create an LC partition check failure

If fatal error is triggered, this test will check:

  • OTP initialization failure triggers fatal alert
  • status, intr_state, err_code CSRs reflect correct fatal error

If OTP initialization finished without any fatal error, this test will check:

  • OTP initialization finishes with power init output goes to 1
  • status, intr_state, err_code CSRs reflect ECC correctable error
V2 partition_check otp_ctrl_check_fail
otp_ctrl_background_chks

Randomly program the partition check related CSRs including:

  • check_timeout
  • integrity_check_period
  • consistency_check_period
  • check_trigger

Create a failure scenario by randomly picking one of these three methods:

  • inject ECC errors into the OTP macro via backdoor
  • set the check_timeout CSR with a very small value
  • write to a random OTP partition after digest is issued but before reset is asserted

Checks:

  • the corresponding alerts are triggered
  • the error_code register is set correctly Note that due to limited simulation time, for background checks, this test only write random value that is less than 20 to the check period.
V2 regwen_during_otp_init otp_ctrl_regwen

The direct_access_regwen is a RO register which controls the write-enable of other reigsters. It is not verified by the common CSR tests. HW sets it to 0 when the DAI interface is busy.

Stimulus and checks:

  • randomly read direct_access_regwen and verify that it returns 0 during OTP initialization
  • verify that the writes to the registers controlled by it do not go through during OTP initialization
V2 partition_lock otp_ctrl_dai_lock

This test will cover two methods of locking read and write: digest calculation and CSR write. After locking the partitions, issue read or program sequences and check if the operations are locked correctly, and check if the AccessError is set.

V2 interface_key_check otp_ctrl_parallel_key_req

OTP_CTRL will generate keys to flash, sram, and otbn upon their requests. Based on the DAI access sequence, this test will run key requests sequence in parallel, and check if correct keys are generated.

V2 lc_interactions otp_ctrl_parallel_lc_req
otp_ctrl_parallel_lc_esc

Verify the procotols between OTP_CTRL and LC_CTRL. Based on the DAI access sequence, run the following sequences in parallel:

  • request a LC state transition via the programming interface
  • enable the lc_escalation_en signal

Checks:

  • if the LC program request has AccessError, check the LC program response sets the error bit to 1
  • if lc_escalation_en is enabled, verify that alert is triggered and OTP_CTRL entered terminal state
V2 otp_dai_errors otp_ctrl_dai_errs

Based on the otp_dai_lock test, this test will randomly run the following OTP errors:

  • DAI interface writes non-blank OTP address
  • DAI interface accesses LC partition
  • DAI interface writes HW digests
  • DAI interface writes non-empty memory

Checks:

  • err_code and status CSRs
  • otp_error interrupt
V2 otp_macro_errors otp_ctrl_macro_errs

Randomly run the following OTP errors:

  • MacroError
  • MacroEccCorrError
  • MacroEccUncorrError

Checks:

  • err_code and status CSRs
  • otp_error interrupt
  • if the error is unrecoverable, verify that alert is triggered and OTP_CTRL entered terminal state
V2 test_access otp_ctrl_test_access

This test checks if the test access to OTP macro is connected correctly.

Stimulus and Checks:

  • read out from the test access window and ensure no error occurs
V2 stress_all otp_ctrl_stress_all
  • combine above sequences in one test to run sequentially, except csr sequence
  • randomly add reset between each sequence
V2 intr_test otp_ctrl_intr_test

Verify common intr_test CSRs that allows SW to mock-inject interrupts.

  • Enable a random set of interrupts by writing random value(s) to intr_enable CSR(s).
  • Randomly "turn on" interrupts by writing random value(s) to intr_test CSR(s).
  • Read all intr_state CSR(s) back to verify that it reflects the same value as what was written to the corresponding intr_test CSR.
  • Check the cfg.intr_vif pins to verify that only the interrupts that were enabled and turned on are set.
  • Clear a random set of interrupts by writing a randomly value to intr_state CSR(s).
  • Repeat the above steps a bunch of times.
V2 alert_test otp_ctrl_alert_test

Verify common alert_test CSR that allows SW to mock-inject alert requests.

  • Enable a random set of alert requests by writing random value to alert_test CSR.
  • Check each alert_tx.alert_p pin to verify that only the requested alerts are triggered.
  • During alert_handshakes, write alert_test CSR again to verify that: If alert_test writes to current ongoing alert handshake, the alert_test request will be ignored. If alert_test writes to current idle alert handshake, a new alert_handshake should be triggered.
  • Wait for the alert handshakes to finish and verify alert_tx.alert_p pins all sets back to 0.
  • Repeat the above steps a bunch of times.
V2 tl_d_oob_addr_access otp_ctrl_tl_errors

Access out of bounds address and verify correctness of response / behavior

V2 tl_d_illegal_access otp_ctrl_tl_errors

Drive unsupported requests via TL interface and verify correctness of response / behavior. Below error cases are tested bases on the [TLUL spec]({{< relref "hw/ip/tlul/doc/_index.md#explicit-error-cases" >}})

  • TL-UL protocol error cases
    • invalid opcode
    • some mask bits not set when opcode is PutFullData
    • mask does not match the transfer size, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010
    • mask and address misaligned, e.g. a_address = 0x01, a_mask = 'b0001
    • address and size aren't aligned, e.g. a_address = 0x01, a_size != 0
    • size is greater than 2
  • OpenTitan defined error cases
    • access unmapped address, expect d_error = 1 when devmode_i == 1
    • write a CSR with unaligned address, e.g. a_address[1:0] != 0
    • write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
    • write a memory with a_mask != '1 when it doesn't support partial accesses
    • read a WO (write-only) memory
    • write a RO (read-only) memory
V2 tl_d_outstanding_access otp_ctrl_csr_hw_reset
otp_ctrl_csr_rw
otp_ctrl_csr_aliasing
otp_ctrl_same_csr_outstanding

Drive back-to-back requests without waiting for response to ensure there is one transaction outstanding within the TL device. Also, verify one outstanding when back- to-back accesses are made to the same address.

V2 tl_d_partial_access otp_ctrl_csr_hw_reset
otp_ctrl_csr_rw
otp_ctrl_csr_aliasing
otp_ctrl_same_csr_outstanding

Access CSR with one or more bytes of data. For read, expect to return all word value of the CSR. For write, enabling bytes should cover all CSR valid fields.

V3 otp_ctrl_low_freq_read otp_ctrl_low_freq_read

This test checks if OTP's read operation can operate successfully in a low clock frequency before the clock is calibrated.

Stimulus and Checks:

  • Configure OTP_CTRL's clock to 6MHz low frequency.
  • Backdoor write OTP memory.
  • Use DAI access to read each memory address and compare if the value is correct.
  • If DAI address is in a SW partition, read and check again via TLUL interface.
V3 tl_intg_err otp_ctrl_tl_intg_err

Verify that the data integrity check violation generates an alert.

Randomly inject errors on the control, data, or the ECC bits during CSR accesses. Verify that triggers the correct fatal alert.

V3 stress_all_with_rand_resetotp_ctrl_stress_all_with_rand_reset

This test runs 3 parallel threads - stress_all, tl_errors and random reset. After reset is asserted, the test will read and check all valid CSR registers.

Covergroups

Name Description
buf_err_code_cg

This is an array of covergroups to cover all applicable error codes in five buffered partitions.

csr_rd_after_alert_cg

Covers if the following CSRs are being read and the value is checked in scoreboard after any fatal alert is triggered:

  • unbuffered partitions' digest CSRs
  • HW partition's digest CSRs
  • secrets partitions' digest CSRs
  • direct_access read data CSRs
  • status CSR
  • error_code CSR
dai_access_secret2_cg

Covers whether lc_creator_seed_sw_rw_en is On during any DAI accesses.

dai_err_code_cg

Covers all applicable error codes in DAI, and cross each error code with all 7 partitions.

flash_addr_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
flash_data_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
flash_req_cg

Covers whether secret1 partition is locked during flash data or address request.

issue_checks_after_alert_cg

Covers if sequence issued various OTP_CTRL's background checks after any fatal alert is triggered.

keymgr_o_cg

Covers the following conditions when scoreboard checks keymgr_o value:

  • whether secret2 partition is locked
  • whether lc_seed_hw_rd_en_i is On
lc_esc_en_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether LC program reqeust is in progress
  • whether DAI interface is busy
lc_prog_cg

Covers whether the error bit is set during LC program request.

lc_prog_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
lci_err_code_cg

Covers all applicable error codes in LCI.

otbn_req_cg

Covers whether secret1 partition is locked during otbn key request.

otbn_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
power_on_cg

Covers the following conditions when OTP_CTRL finishes power-on initialization:

  • whether lc_escalation_en is On
  • whether any partition (except life cycle partition) is locked
req_dai_access_after_alert_cg

Covers if sequence issued various DAI requests after any fatal alert is triggered.

sram_0_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
sram_1_req_condition_cg

Covers the following conditions when lc_escalation_en is On:

  • whether any key requests is in progress
  • whether DAI interface is busy
  • whether lc_esc_en is On
sram_req_cg

Covers whether secret1 partition is locked during all srams key request.

status_csr_cg

Covers the value of every bit in status CSR.

tl_errors_cg

Cover the following error cases on TL-UL bus:

  • TL-UL protocol error cases.
  • OpenTitan defined error cases, refer to testpoint tl_d_illegal_access.
tl_intg_err_cg

Cover all kinds of integrity errors (command, data or both) and cover number of error bits on each integrity check.

tl_intg_err_mem_subword_cg

Cover the kinds of integrity errors with byte enabled write on memory.

Some memories store the integrity values. When there is a subword write, design re-calculate the integrity with full word data and update integrity in the memory. This coverage ensures that memory byte write has been issued and the related design logic has been verfied.

unbuf_err_code_cg

This is an array of covergroups to cover all applicable error codes in two unbuffered partitions.