|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | { | 
|  | name: "otp_ctrl" | 
|  | import_testplans: ["hw/dv/tools/dvsim/testplans/csr_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/mem_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/intr_test_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/alert_test_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/sec_cm_count_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/sec_cm_fsm_testplan.hjson", | 
|  | "hw/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson", | 
|  | "otp_ctrl_sec_cm_testplan.hjson"] | 
|  | testpoints: [ | 
|  | { | 
|  | name: wake_up | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V1 | 
|  | tests: ["otp_ctrl_wake_up"] | 
|  | } | 
|  | { | 
|  | name: smoke | 
|  | desc: ''' | 
|  | 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 | 
|  |  | 
|  | **Checks**: | 
|  | - Assertion checks to ensure vendor specific I/Os: `otp_vendor_test_status_o`, | 
|  | `otp_vendor_test_ctrl_i`, `cio_test_o`, and `cio_test_en_o` are connected currently | 
|  | with `lc_dft_en_i` On and Off. | 
|  | ''' | 
|  | stage: V1 | 
|  | tests: ["otp_ctrl_smoke"] | 
|  | } | 
|  | { | 
|  | name: dai_access_partition_walk | 
|  | desc: ''' | 
|  | 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. | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_partition_walk"] | 
|  | } | 
|  | { | 
|  | name: init_fail | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_init_fail"] | 
|  | } | 
|  | { | 
|  | name: partition_check | 
|  | desc: ''' | 
|  | 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. | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_check_fail", "otp_ctrl_background_chks"] | 
|  | } | 
|  | { | 
|  | name: regwen_during_otp_init | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_regwen"] | 
|  | } | 
|  | { | 
|  | name: partition_lock | 
|  | desc: ''' | 
|  | 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. | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_dai_lock"] | 
|  | } | 
|  | { | 
|  | name: interface_key_check | 
|  | desc: ''' | 
|  | 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. | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_parallel_key_req"] | 
|  | } | 
|  | { | 
|  | name: lc_interactions | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_parallel_lc_req", "otp_ctrl_parallel_lc_esc"] | 
|  | } | 
|  | { name: otp_dai_errors | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_dai_errs"] | 
|  | } | 
|  | { name: otp_macro_errors | 
|  | desc: ''' | 
|  | 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 | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_macro_errs"] | 
|  | } | 
|  | { | 
|  | name: test_access | 
|  | desc: ''' | 
|  | This test checks if the test access to OTP macro is connected correctly. | 
|  |  | 
|  | **Stimulus and Checks**: | 
|  | - Write and check read results from the prim_tl_i/o. | 
|  | - Ensure no error or alert occurs from DUT. | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["otp_ctrl_test_access"] | 
|  | } | 
|  | { | 
|  | name: stress_all | 
|  | desc: ''' | 
|  | - combine above sequences in one test to run sequentially, except csr sequence | 
|  | - randomly add reset between each sequence | 
|  | ''' | 
|  | stage: V2 | 
|  | tests: ["{name}_stress_all"] | 
|  | } | 
|  | { | 
|  | name: sec_cm_additional_check | 
|  | desc: ''' | 
|  | Verify the outcome of injecting faults to security countermeasures. | 
|  |  | 
|  | Stimulus: | 
|  | As mentioned in `prim_count_check`, `prim_fsm_check` and `prim_double_lfsr_check`. | 
|  |  | 
|  | Checks: | 
|  | - Check the value of status register according to where the fault is injected. | 
|  | - Check OTP_CTRL is locked after the fatal fault injection by trying to access OTP_CTRL | 
|  | via dai, kdi, and lci interfaces. | 
|  | ''' | 
|  | stage: V2S | 
|  | tests: ["otp_ctrl_sec_cm"] | 
|  | } | 
|  | { | 
|  | name: otp_ctrl_low_freq_read | 
|  | desc: ''' | 
|  | 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. | 
|  | ''' | 
|  | stage: V3 | 
|  | tests: ["otp_ctrl_low_freq_read"] | 
|  | } | 
|  | ] | 
|  |  | 
|  | covergroups: [ | 
|  | { | 
|  | name: power_on_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: flash_req_cg | 
|  | desc: '''Covers whether secret1 partition is locked during `flash` data or address | 
|  | request.''' | 
|  | } | 
|  | { | 
|  | name: sram_req_cg | 
|  | desc: '''Covers whether secret1 partition is locked during all `srams` key request.''' | 
|  | } | 
|  | { | 
|  | name: otbn_req_cg | 
|  | desc: '''Covers whether secret1 partition is locked during `otbn` key request.''' | 
|  | } | 
|  | { | 
|  | name: lc_prog_cg | 
|  | desc: '''Covers whether the error bit is set during LC program request.''' | 
|  | } | 
|  | { | 
|  | name: keymgr_o_cg | 
|  | desc: '''Covers the following conditions when scoreboard checks `keymgr_o` value: | 
|  | - whether secret2 partition is locked | 
|  | - whether `lc_seed_hw_rd_en_i` is On | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: req_dai_access_after_alert_cg | 
|  | desc: '''Covers if sequence issued various DAI requests after any fatal alert is | 
|  | triggered.''' | 
|  | } | 
|  | { | 
|  | name: issue_checks_after_alert_cg | 
|  | desc: '''Covers if sequence issued various OTP_CTRL's background checks after any fatal alert | 
|  | is triggered.''' | 
|  | } | 
|  | { // TODO: this is from a wrapper class, confirm if this is the correct format | 
|  | name: csr_rd_after_alert_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: dai_err_code_cg | 
|  | desc: '''Covers all applicable error codes in DAI, and cross each error code with all | 
|  | 7 partitions.''' | 
|  | } | 
|  | { | 
|  | name: lci_err_code_cg | 
|  | desc: '''Covers all applicable error codes in LCI.''' | 
|  | } | 
|  | { // TODO: this is an array of covergroups, confirm if this is the correct format | 
|  | name: unbuf_err_code_cg | 
|  | desc: '''This is an array of covergroups to cover all applicable error codes in three | 
|  | unbuffered partitions.''' | 
|  | } | 
|  | { // TODO: this is an array of covergroups, confirm if this is the correct format | 
|  | name: buf_err_code_cg | 
|  | desc: '''This is an array of covergroups to cover all applicable error codes in five | 
|  | buffered partitions.''' | 
|  | } | 
|  | { // TODO: this is an array of covergroups, confirm if this is the correct format | 
|  | name: unbuf_access_lock_cg_wrap_cg | 
|  | desc: '''This is an array of covergroups to cover lock conditions below in three | 
|  | unbuffered partitions: | 
|  | - the partition is write-locked | 
|  | - the partition is read-locked | 
|  | - the current operation type | 
|  | Then cross the three coverpoints.''' | 
|  | } | 
|  | { | 
|  | name: dai_access_secret2_cg | 
|  | desc: '''Covers whether `lc_creator_seed_sw_rw_en` is On during any DAI accesses.''' | 
|  | } | 
|  | { // TODO: this is an array of covergroups, confirm if this is the correct format | 
|  | name: status_csr_cg | 
|  | desc: '''Covers the value of every bit in `status` CSR.''' | 
|  | } | 
|  | // The following covergroups are implemented in `otp_ctrl_cov_if.sv`. | 
|  | { | 
|  | name: lc_esc_en_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: flash_data_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: flash_addr_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: sram_0_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: sram_1_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: otbn_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | { | 
|  | name: lc_prog_req_condition_cg | 
|  | desc: '''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 | 
|  | ''' | 
|  | } | 
|  | ] | 
|  | } |