CLKMGR DV document

Goals

  • DV
    • Verify all CLKMGR 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.
    • Verify clock gating assertions.

Current status

Design features

The detailed information on CLKMGR design features is at CLKMGR HWIP technical specification.

Testbench architecture

CLKMGR 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/clkmgr/dv/tb.sv. It instantiates the CLKMGR DUT module hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv. In addition, it instantiates the following interfaces, connects them to the DUT and sets their handle into uvm_config_db:

Notice the following interfaces should be connected once the RTL adds support for them:

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 clkmgr_env_pkg. Some of them in use are:

  typedef virtual clkmgr_if clkmgr_vif;
  typedef virtual clk_rst_if clk_rst_vif;
  typedef enum int {PeriDiv4, PeriDiv2, PeriIo, PeriUsb} peri_e;
  typedef enum int {TransAes, TransHmac, TransKmac, TransOtbn} trans_e;

TL_agent

CLKMGR 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 CLKMGR device.

UVM RAL Model

The CLKMGR 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:

Stimulus strategy

This module is rather simple: the stimulus is just the external pins and the CSR updates. There are a couple stages for synchronization of the CSR updates for clock gating controls, but scanmode is used asynchronously. These go to the clock gating latches. The external pins controlling the external clock selection need no synchronization. The tests randomize the inputs and issue CSR updates affecting the specific functions being tested.

Test sequences

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

  • clkmgr_init: Sets the frequencies of the various clocks.
  • update_idle: Updates the idle input.

The sequence clkmgr_peri_vseq randomizes the stimuli that drive the four peripheral clocks. These clocks are mutually independent so they are tested in parallel. They depend on the clk_enables CSR, which has a dedicated enable for each peripheral clock, the pwrmgr’s ip_clk_en which controls all, and scanmode_i which is used asynchronously and also controls all. The sequence runs a number of iterations, each randomizing all the above.

The sequence clkmgr_trans_vseq randomizes the stimuli that drive the four transactional unit clocks. These are also mutually independent so they are tested in parallel. They depend on the clk_hints CSR, which has a separate bit for each, ip_clk_en and scanmode_i as in the peripheral clocks. They also depend on the idle_i input, which also has a separate bit for each unit. Any unit’s idle_i bit is clear when the unit is currently busy, and prevents its clock to be turned off until it becomes idle.

The sequence clkmgr_extclk_vseq randomizes the stimuli that drive the external clock selection. The selection is controlled by the extclk_sel CSR being lc_ctrl_pkg::On, provided the lc_dft_en_i input is also set to lc_ctrl_pkg::On. Alternatively, the external clock is also selected if the lc_ctrl_byp_req_i is lc_ctrl_pkg::On. When the external clock is selected the clock dividers for the clk_io_div2 and clk_io_div4 output clocks are stepped down, unless scanmode_i is set to lc_ctrl_pkg::On.

The sequence clkmgr_jitter_vseq sets the jitter CSR that drives the jitter_en_o output to the AST block. The sequence writes the CSR and checks that the jitter_en_o output tracks it.

Functional coverage

To ensure high quality constrained random stimulus, it is necessary to develop a functional coverage model. The following covergroups have been developed to prove that the test intent has been adequately met:

  • Covergroups for inputs to the peripherals clock gating. These are wrapped in class clkmgr_peri_cg_wrap and instantiated in clkmgr_env_cov.
  • Covergroups for inputs to the transactional units clock gating. These are wrapped in class clkmgr_trans_cg_wrap and instantiated in clkmgr_env_cov.

See more detailed description at hw/ip/clkmgr/data/clkmgr_testplan.hjson.

Self-checking strategy

Most of the CLKMGR outputs are gated clocks, which are controlled by both synchronous logic and asynchronous enables. If it were not for the asynchronous enables it would be possible to check them with SVA assertions. The reason asynchronous enables don’t work for SVA is because the latter uses sampled values at clock edges. It may be possible to consider the asynchronous enable as an additional clock, and deal with multiple clock assertions. However, it seems simpler to just check the clocks in the scoreboard, using regular SV constructs.

Scoreboard

The clkmgr_scoreboard combines CSR updates and signals from the the clkmgr vif to check the activity of the gated clocks.

The output clocks can be separated into two groups: peripheral ip clocks and transactional unit clocks. Please refer to the Test sequences section above. The clock gating logic is pretty similar across units in each group.

To get the right timing for the gated clocks the scoreboard follows these rules: CSR updates need one extra flop stage using the non-gated version of the clock they control. The pwrmgr.ip_clk_en input needs to be staged like the CSRs. Transactional unit idle bits are in the same clock domain as their controlled clocks so need no extra stages. The asynchronous scanmode_i input needs no stages. All synchronous signals the scoreboard needs from clkmgr_if are transferred via clocking blocks triggered by the corresponding unit’s powerup clock.

In pseudo code the prediction for the clock gate of unit peri becomes

unit_enable = staged(clk_enables[peri] && ip_clk_en) || is_on(scanmode_i)

The transactional units have an additional bit in idle that prevents disabling their clock unless this bit is on. In pseudo code the prediction for the clock gate of unit trans becomes

unit_enable  = staged(clk_hints[trans] && ip_clk_en) || !idle[trans] || is_on(scanmode_i)

The CSR updates are determined using the TLUL analysis port.

Assertions

  • TLUL assertions: The tb/clkmgr_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.

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/clkmgr/dv/clkmgr_sim_cfg.hjson -i clkmgr_smoke

Testplan

Testpoints

Milestone Name Tests Description
V1 smoke clkmgr_smoke

Smoke test disabling peripheral and transactional clocks.

  • Disables all peripheral clocks from their enabled reset state.
  • Transactional clocks gating depends on whether they are idle.
  • Initializes all units as busy (not idle).
  • Clears each unit's clk_hints bit, which has no effect until the unit becomes idle.
  • Sets the unit's idle_i bit, which should disable the clock.
  • Writes both values of the jitter_enable CSR.

Stimulus:

  • CSR writes to clk_enables and clk_hints.
  • Setting idle_i clkmgr input.

Checks:

  • SVA assertions for peripheral clocks enable and disable properties.
  • Transactional clocks check SVA properties as follows:
    • If the hint enables it, the clock becomes active.
    • If the hint disables it but the unit is busy, the clock remains active.
    • If the hint disables it and the unit is idle, the clock stops.
  • For transactional units the CSR clk_hints_status is checked to correspond to clk_hints once the units are idle.
  • Check in scoreboard the jitter_en_o output tracks updates of the jitter_enable CSR.
V1 csr_hw_reset clkmgr_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 clkmgr_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 clkmgr_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 clkmgr_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_resetclkmgr_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.
V2 peri_enables clkmgr_peri

Peripheral clocks are disabled if its clk_enables bit is off, or pwr_i.ip_clk_en is off, and scanmode_i is not lc_ctrl_pkg::On.

This test runs multiple rounds, and on each one it randomizes ip_clk_en and scanmode_i, and the initial setting of clk_enables, it sends a CSR write to csr_enables with this initial value followed by a write that flips all bits.

Checks:

  • The scoreboard checks the gated clock activities against its model of the expected behavior.
V2 trans_enables clkmgr_trans

Transactional unit clocks are disabled if they are not busy and their CSR clk_hints bit is off, or pwr_i.ip_clk_en is off, and scanmode_i is not lc_ctrl_pkg::On. This test randomizes ip_clk_en, the initial setting of idle_i and the desired value of clk_hints. Each round performs this sequence:

  • Writes the desired value to CSR clk_hints and checks that the CSR clk_hints_status reflects CSR clk_hints except for the units not-idle.
  • Marks all units as idle, and checks that csr_hints_status matches clk_hints.
  • Writes clk_hints to all ones and checks that csr_hints_status is all ones.
  • Writes clk_hints with its reset value.

Checks:

  • SVA assertions for transactional unit clocks described in clkmgr_smoke.
V2 extclk clkmgr_extclk

Tests the functionality of enabling external clocks.

  • External clock is enabled if the lc_clk_byp_req_i input from lc_ctrl is lc_ctrl_pkg::On.
  • External clock is also be enabled when CSR extclk_sel is set to lc_ctrl_pkg::On and the lc_dtl_en_i input from lc_ctrl is lc_ctrl_pkg::On.
  • Notice writes to the extclk_sel register are ignored unless the CSR extclk_sel_regwen is 1.
  • A successful switch to external clocks due to lc_clk_byl_req_i will cause the clkmgr to undo a divide by 2 for io_div4 and io_div2 clocks except when (scanmode_i == lc_ctrl_pkg::On).
  • A software triggered switch to external clock will undo divides by 2 if extclk_ctrl.step_down is set to lc_ctrl_pkg::On.

Stimulus:

  • CSR writes to extclk_sel and extclk_sel_regwen.
  • Setting lc_dft_en_i, lc_clk_byp_req_i, and the handshake to ast via ast_clk_byp_req_o and ast_clk_byp_ack_i.
  • Setting scanmode_i.

Checks: Clock divider checks: since scanmode_i is asynchronous, the checks are performed in behavioral code in the scoreboard; SVA assertions will be added, conditioned on scanmode being off.

  • When the external clock is selected (and not defeated by scanmode_i for scoreboard checks):
    • The clk_io_div2_powerup output matches the clk_io_powerup output.
    • The clk_io_div4_powerup output matches the clk_io_powerup output at half its frequency.
  • When the external clock is not selected or division is defeated:
    • The clk_io_div2_powerup output matches the clk_io_powerup output at half its frequency.
    • The clk_io_div4_powerup output matches the clk_io_powerup output at a quarter of its frequency. LC / AST handshake:
  • When the external clock functionality is triggered the ast_clk_byp_req_o output pin is set to lc_ctrl_pkg::On.
  • When ast_clk_byp_ack_i is set to lc_ctrl_pkg::On in response to a corresponding request:
    • The clock dividers are stepped down, unless defeated by scanmode_i being lc_ctrl_pkg::On.
  • If the initial request was due to the assertion of the lc_clk_byp_req_i, the lc_clk_byp_ack_o output is set to lc_ctrl_pkg::On.
V2 clk_status clkmgr_clk_status

This tests the pwr_o.clk_status output port.

The pwr_o.clk_status output must track the pwr_i.ip_clk_en input once the clock outputs are enabled when ip_clk_en is active, or disabled when ip_clk_en goes inactive.

Stimulus:

  • Randomize the pwr_i.ip_clk_en setting.
  • Randomize the various pwrmgr clock controls per settings in the pwrmgr control CSR.
  • This will trigger cases where the usb clock is inactive with ip_clk_en enabled.

Check:

  • The sequence checks that pwr_o.clk_status tracks pwr_i.ip_clk_en.
V2 jitter clkmgr_smoke

This tests the jitter functionality.

The jitter functionality is implemented by the AST block, but controlled by the jitter_enable CSR in this block. This CSR directly drives the jitter_en_o output pin.

Stimulus:

  • CSR write to jitter_enable.

Check:

  • The jitter_en_o output pin reflects the jitter_enable CSR. Test is implemented in the scoreboard, and runs always.
V2 frequency

This tests the frequency counters functionality.

These counters compute the number of cycles of some clock relative to the aon timer. It should trigger a recoverable alert when the count is not in the expected interval. This tests programs counts for each counter, with intervals set to trigger correct, fast, and slow counts.

Stimulus:

  • Randomly set slow, correct, and fast interval for each counter and test.

Check:

  • Slow and fast intervals should cause a recoverable alert.
  • Coverage collected per clock.
V2 frequency_overflow

This tests the overflow feature in prim_clock_meas.

This needs to modify the state of the counter to trigger the feature.

Stimulus:

  • Program the counter. Whenever it hits the value of 1, set it to the range - 2.

Check:

  • The internal cnt_ovfl flop is set.
  • The fast_o output should be set.
V2 intr_test clkmgr_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 clkmgr_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 clkmgr_tl_errors

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

V2 tl_d_illegal_access clkmgr_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 clkmgr_csr_hw_reset
clkmgr_csr_rw
clkmgr_csr_aliasing
clkmgr_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 clkmgr_csr_hw_reset
clkmgr_csr_rw
clkmgr_csr_aliasing
clkmgr_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 stress

Create standard stress tests.

Stress with the following sequences:

  • clkmgr_extclk_vseq,
  • clkmgr_peri_vseq,
  • clkmgr_smoke_vseq,
  • clkmgr_trans_vseq
V3 tl_intg_err clkmgr_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_resetclkmgr_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
extclk_cg

Collects coverage for the external clock selection.

The external clock selection depends on the extclk_sel CSR, and the lc_dft_en_i, lc_clk_byp_req_i, and scanmode_i input pins. This covergroup collects their cross.

freq_measure_cg

Collects coverage for the frequency measurement counters.

The relevant information is whether it got an okay, slow, and fast measurement.

jitter_cg

Collects coverage of the jitter CSR.

peri_cg

Collects coverage for each peripheral clock.

The peripheral clocks depend on a bit in the clk_enables CSR, the ip_clk_en input from pwrmgr, and the scanmode input. This collects the cross of them for each peripheral.

FIXME This is collected in an array, one instance for each clock, but the dvsim coverage flow doesn't yet support arrays.

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.

trans_cg

Collects coverage for each transactional unit clock.

The transactional unit clocks depend on a bit in the clk_hints CSR, the ip_clk_en input from pwrmgr, the respective idle input bit from the unit, and the scanmode input. This collects the cross of them for each transactional unit.

FIXME This is collected in an array, one instance for each clock, but the dvsim coverage flow doesn't yet support arrays.