Comportability in Pavona
A Guide for Design and Verification Engineers
What Is Comportability? (Start Here)
The word comes from Johnson’s 1808 Dictionary of the English Language:
“Comportable, a. consistent, suitable, fit”
In Pavona, comportability is a design philosophy and enforceable standard which requires every hardware IP block to follow a common set of interfaces, conventions, and behaviors. This facilitates a subsystem or an entire SoC can be assembled, verified, and maintained in a uniform, scalable way.
Think of it like a building ordinance for hardware IP. Just as a building ordinance’s ensures every contractor uses standard electrical outlets, plumbing fittings, and load bearing rules; comportability ensures every IP block exposes registers the same way, signals interrupts the same way, raises security alerts the same way, and connects to the bus the same way.
The payoff: tools, firmware, and verification infrastructure can treat all blocks uniformly. No block-specific special cases (However there can be exceptional block level outliers).
Why Does Comportability Exist?
With comportability, a new IP block that follows the spec gets:
- Auto-generated register RTL, RAL model, and documentation from a single
.hjsonfile - Auto-generated top-level connections for bus, alerts, and inter-module signals
- Shared DV infrastructure that gives you CSR tests, interrupt tests, and alert tests for free
- Compliance gating through a formal checklist before a block can be taped out
The result: roughly a significant portion of RTL development and DV effort is handled by shared infrastructure, leaving the block owner to focus only on the block’s unique functionality. At the top-level, interconnect is fully checked and generated by the tooling.
The Two .hjson Files Every Design and DV Engineer Must Know
Comportability in practice is anchored by two Hjson configuration files. Understanding them is the most important first step for anyone contributing to Pavona DV.
| File | Owner | Purpose |
|---|---|---|
hw/ip/<block>/data/<block>.hjson | Design + DV | Declares what the block is: registers, interrupts, alerts, clocks, countermeasures |
hw/ip/<block>/dv/<block>_sim_cfg.hjson | DV | Declares how the block is simulated: DUT, testbench, tool, testplan, tests, regressions |
The design <block>.hjson drives reggen, tlgen, and topgen to auto-generate RTL and connectivity.
<block>_sim_cfg.hjson drives dvsim.py to build and run simulations.
Together they form the machine-readable specification that make Pavona’s DV automation possible.
Key Insight for New Contributors: Two files unlock the entire comportability DV framework for a block — the design
.hjson(what the block is) and thesim_cfg.hjson(how to simulate it). Get these right with the correct imports, and roughly 30-40% of the DV work is done by shared infrastructure before a single block-specific test is written.
The Design <block>.hjson — The Comportability Declaration
Every comportable IP ships with a block-level Hjson configuration file (<block>.hjson) in its data/ directory.
This is the single source of truth for everything the block declares about itself.
A simplified example for a UART:
{
name: "uart",
clocking: [
{clock: "clk", reset: "rst_n"}
],
bus_interfaces: [
{ protocol: "tlul", direction: "device", racl_support: true}
],
available_input_list: [
{ name: "rx", desc: "Receive bit" }
],
available_output_list: [
{ name: "tx", desc: "Transmit bit" }
],
interrupt_list: [
{ name: "tx_watermark", desc: "Raised if the transmit FIFO is past high water mark", type: "status" },
{ name: "rx_overflow", desc: "Raised if the receive FIFO has overflowed" }
],
alert_list: [
{ name: "fatal_fault", desc: This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected." }
],
countermeasures: [
{ name: "BUS.INTEGRITY", desc: "End-to-end bus integrity scheme." }
],
regwidth: "32",
registers: [ ... ]
}
All tooling reads this file.
reggen generates the register RTL, RAL model, INTR_STATE/ENABLE/TEST registers,
ALERT_TEST register, software headers, and HTML documentation.
topgen uses it to auto-wire the top level.
Documentation: All IP must use the standard Pavona specification template.
Register documentation is auto-generated by reggen from the <block>.hjson.
Comportable Item List
The following items are declared in the design <block>.hjson.
Important items are required for every peripheral; optional ones are included as needed.
This is not a complete list: see util/reggen for details.
| Feature | Important/Optional | Description |
|---|---|---|
| Clocking | Important | Declare primary functional clock and any other clocks needed |
| Bus Interfaces | Important | At least one TL-UL device interface required |
| Registers | Important | All CSRs defined in reggen format |
| Available IO | Optional | Peripheral pins available for chip-level IO |
| Interrupts | Optional | Signals sent to the processor interrupt controller |
| Alerts | Optional | Security-critical signals sent to the alert handler |
| Inter-module Signals | Optional | Bundled signals connecting to other peripherals |
| Security Countermeasures | Optional (expected for security-critical IP) | Named protection mechanisms against FI/SCA attacks |
Feature Details
Clocking and Reset
Each peripheral specifies its clocking using the clocking field.
One item is designated the primary clock
— this is the clock that drives the bus interface; Additional clocks can be added as needed.
Resets in Pavona are asynchronous active-low with synchronous deassertion:
- Reset assertion can happen at any time (asynchronous)
- Reset release is synchronized to the associated clock edge
Security-sensitive storage elements require extra care — for example, resetting to a randomized value rather than all-zeros, to prevent an attacker inferring sensitive state from Hamming distance analysis.
Bus Interface — TL-UL
All comportable peripherals connect to the chip bus using TL-UL (TileLink-Uncached-Lite).
This is currently the only supported bus protocol.
Every peripheral must declare at least one device direction interface.
Some peripherals (e.g., DMA) also act as a host.
The TL-UL protocol handles addressed register reads and writes, error signaling (d_error) for invalid accesses, and end-to-end bus integrity (ECC on data and address).
Available IO
Peripherals can optionally expose pins for chip-level IO using available_input_list, available_output_list, and available_inout_list.
These map to module ports: For example,
- An input named
rxappears as an input port namedcio_rx_i; - an output port named
txappears as two output ports namedcio_tx_oandcio_tx_en_o; and - an inout port appears as all three signals above.
Pin multiplexing and pad control are handled centrally by the pinmux module at the top level — not by the peripheral itself.
Registers
All registers are defined in the design <block>.hjson file.
From this single definition, reggen auto-generates register RTL, a UVM RAL model, software header files, and HTML documentation.
This means you never manually write register RTL or a RAL model for a comportable block.
More details is found in documentation for util/reggen
Interrupts
Peripherals declare interrupts in interrupt_list.
For each declared interrupt, reggen automatically creates a field in three registers at the top of the peripheral’s address map:
| Register | Offset | Description |
|---|---|---|
INTR_STATE | 0x0 | Current state of each interrupt; RO or W1C depending on type |
INTR_ENABLE | 0x4 | Enables/masks INTR_STATE bits to produce the wired interrupt output |
INTR_TEST | 0x8 | Write-only; forces interrupt assertion for testing |
The output interrupt wire: intr_name_o = INTR_STATE & INTR_ENABLE.
INTR_STATE always reflects raw hardware state regardless of INTR_ENABLE.
Two Interrupt Types
Event Type (default): An instantaneous event (e.g., a counter crossing a threshold) latches INTR_STATE high.
Software clears it by writing 1 to the bit (W1C). If the event is still active when software clears it, the bit sets
again immediately.
{signal: [
{name: 'clk_i', wave: 'p.............'},
{name: 'hw_i', wave: '0..1......0...'},
{name: 'INTR_STATE', wave: '0...1......0..'},
{name: 'intr_o', wave: '0....1....0...'},
{name: 'SW w1c', wave: '0.......10....'},
]}
Status Type: A persistent condition (e.g., FIFO non-empty) drives INTR_STATE directly. INTR_STATE is RO — software cannot W1C it.
The interrupt only deasserts when the root cause is addressed. If software needs to defer handling, it must mask INTR_ENABLE.
{signal: [
{name: 'clk_i', wave: 'p.............'},
{name: 'hw_i', wave: '0..1......0...'},
{name: 'INTR_STATE', wave: '0...1......0..'},
{name: 'intr_o', wave: '0...1......0..'},
{name: 'SW addresses the cause', wave: '0........10...'},
]}
Alerts
Alerts are security-critical signals sent from a peripheral to the central Alert Handler module (not directly to the processor).
Each alert is transmitted as a differential signal pair via prim_alert_sender, to protect against single-point failures.
There are two categories of alerts:
- Recoverable (
recov_*): One-time triggered for recoverable error conditions; Alert sender fires once when the error is asserted. - Fatal (
fatal_*) : Continuously triggered until system reset; Used for terminal conditions. Should also trigger local security countermeasures (e.g., moving an FSM into a locked terminal state).
reggen by default auto-generates an ALERT_TEST register for every peripheral with alerts — a write-only register that lets software force-trigger each alert for testing.
Common guideline: one fatal alert + one recoverable alert per peripheral to keep the system-level alert count manageable. The following table lists examples of events that cause alerts and whether they are considered recoverable or fatal. Examples drawn from multiple peripherals. A given peripheral would expose only the rows relevant to it.
| Error Event | Regular IRQ | Recoverable Alert | Fatal Alert |
|---|---|---|---|
| ECC correctable in NVM | Optional | Yes | |
| ECC uncorrectable in Flash | Optional | Yes | |
| ECC uncorrectable in OTP | Optional | Yes | |
| Any ECC/Parity error in SRAMs | Optional | Yes | |
| Glitch detectors (invalid FSM encoding) | Optional | Yes | |
| Incorrect usage of security IP | Optional | Yes | |
| Incorrect usage of regular IP | Yes |
Inter-Module Signals
Signals connecting peripherals to each other (beyond interrupts and alerts) are declared in inter_signal_list.
The topgen tool uses this to auto-wire the top level.
There are two signal types:
req_rsp: Request-response pair. The requester sends a request struct; the responder returns a response struct. Example:flash_ctrlsending read/write/erase requests to the flash macro.uni: Unidirectional one-way signal. Used for broadcasts or signals that need no response.
| Attribute | Description |
|---|---|
name | Port name; for req_rsp type generates name_req and name_rsp ports |
struct | Data structure type (e.g., a package-defined struct, or logic for simple signals) |
package | SystemVerilog package containing the struct definition |
type | req_rsp or uni |
act | Role: req or rsp (for req_rsp); req or rcv (for uni) |
width | Vector width if connecting to multiple peripherals |
Security Countermeasures
If this IP block is considered security-critical, it will probably have design features that try to mitigate against attacks like fault injection or side channel analysis. These features can be loosely categorized and named with identifiers of the following form:
[UNIQUIFIER.]ASSET.CM_TYPE
Here, ASSET is the asset that is being protected.
This might be secret information like a key, or it might be internal state like a processor’s control flow.
The countermeasure that is providing the protection is named with CM_TYPE.
The UNIQUIFIER label is optional and allows to add a custom prefix to make the identifier unique or more concise.
Below are a few examples for generic and more concise labels following this format:
LFSR.REDUN or MASKING_PRNG.LFSR.REDUN
FSM.SPARSE or HASHING_INTERFACE.FSM.SPARSE
INTERSIG.MUBI or ALERT.INTERSIG.MUBI
The following standardized assets are defined:
| Asset name | Intended meaning |
|---|---|
| KEY | A key (secret data) |
| ADDR | An address |
| DATA_REG | A configuration data register that doesn’t come from software (such as Keccak state) |
| DATA_REG_SW | A data holding register that is manipulated by software |
| CTRL_FLOW | The control flow of software or a module |
| CTRL | Logic used to steer hardware behavior |
| CONFIG | Software-supplied configuration, programmed through the comportable register interface |
| LFSR | A linear feedback shift register |
| RNG | A random number generator |
| CTR | A counter |
| FSM | A finite state machine |
| MEM | A generic data memory; volatile or non-volatile |
| CLK | A clock |
| RST | A reset signal |
| BUS | Data transferred on a bus |
| INTERSIG | A non-bus signal between two IP blocks |
| MUX | A multiplexer that controls propagation of sensitive data |
| CONSTANTS | A netlist constant |
| STATE | An internal state signal (other than FSM state, which is covered by the FSM label) |
| TOKEN | A cryptographic token |
| LOGIC | Any logic. This is a very broad category: avoid if possible and give an instance or net name if not. |
The following standardized countermeasures are defined:
| Countermeasure name | Intended meaning | Commonly associated assets |
|---|---|---|
| MUBI | A signal is multi-bit encoded | CTRL, CONFIG, CONSTANTS, INTERSIG |
| SPARSE | A signal is sparsely encoded | FSM |
| DIFF | A signal is differentially encoded | CTRL, CTR |
| REDUN | There are redundant versions of the asset | ADDR, CTRL, CONFIG, CTR |
| REGWEN | A register write enable is used to protect the asset from write access | CONFIG, MEM |
| REGWEN_MUBI | A multi-bit encoded register write enable is used to protect the asset from write access | CONFIG, MEM |
| SHADOW | The asset has a shadow replica to cross-check against | CONFIG |
| REGREN | A register write enable is used to protect the asset from read access | CONFIG, MEM |
| SCRAMBLE | The asset is scrambled | CONFIG, MEM |
| INTEGRITY | The asset has integrity protection from a computed value such as a checksum | CONFIG, REG, MEM |
| READBACK | A readback check is performed to validate that the asset has been correctly modified or fetched | MEM |
| ADDR_INFECTION | The asset is infected using the read address | MEM |
| CONSISTENCY | This asset is checked for consistency other than by associating integrity bits | CTRL, RST |
| DIGEST | Similar to integrity but more computationally intensive, implying a full hash function | CONFIG, REG, MEM |
| LC_GATED | Access to the asset is qualified by life-cycle state | REG, MEM, CONSTANTS, CONFIG |
| BKGN_CHK | The asset is protected with a continuous background health check | |
| GLITCH_DETECT | The asset is protected by an analog glitch detector | CTRL, FSM, CLK, RST |
| SW_UNREADABLE | The asset is not readable by software | MEM, KEY |
| SW_UNWRITABLE | The asset is not writable by software | MEM, KEY |
| SW_NOACCESS | The asset is not writable nor readable by software (SW_UNWRITABLE and SW_UNREADABLE at the same time) | MEM, KEY |
| SIDELOAD | The asset can be loaded without exposing it to software | KEY |
| SEC_WIPE | The asset is initialized or cleared using pseudo-random data | KEY, DATA_REG, MEM |
| SCA | A countermeasure that provides side-channel attack resistance | |
| MASKING | A more specific version of SCA where an asset is split into shares | |
| LOCAL_ESC | A local escalation event is triggered when an attack is detected | |
| GLOBAL_ESC | A global escalation event is triggered when an attack is detected | |
| UNPREDICTABLE | Behaviour is unpredictable to frustrate repeatable FI attacks | |
| TERMINAL | The asset goes into a terminal state that no longer responds to any stimulus | |
| COUNT | The number of operations or items processed is counted which can be checked by software to ensure the correct number have occurred | |
| CM | Catch-all for countermeasures that cannot be further specified. This is a very broad category: avoid if possible and give an instance or net name if not. |
Design Verification (DV) for Comportability
Because every comportable IP follows the same spec, DV can be built generically across all blocks.
This shared DV infrastructure lives in cip_lib (Comportable IP Library).
The first file a DV engineer works with on any block — is the <block>_sim_cfg.hjson.
The <block>_sim_cfg.hjson — The DV Entry Point
Location: hw/ip/<block>/dv/<block>_sim_cfg.hjson
For templated blocks the editable file is located at: hw/ip_templates/<block>/dv/<block>_sim_cfg.hjson.tpl.
This is used to generate a top specific hjson file located at hw/<specific_top>/ip_autogen/<block>/dv/<block>_sim_cfg.hjson
This is the mandatory input to dvsim.py — Pavona’s command-line simulation and regression manager. It is the single entry point
for all DV activity on a block. Without it, you cannot build or run any simulation.
Just as the design .hjson declares what a block is, the sim_cfg.hjson declares how that block is simulated and regressed.
It tells dvsim.py everything needed to build a simulation executable and run tests:
- Which RTL module is the DUT and which testbench top to elaborate
- Which simulator to use for sign-off (VCS, Xcelium)
- Which FuseSoC core file provides the RTL + DV file list
- Which testplan to track DV progress against
- Which RAL spec to use for auto-generating the RAL model at build time
- What simulation bind-file tops (coverage, assertions) to include
- What tests exist, their build/run options, and which regression suites they belong to
Every DV action flows through this file:
# Run a single smoke test
util/dvsim/dvsim.py hw/ip/uart/dv/uart_sim_cfg.hjson -i uart_smoke
# Run the CSR hw_reset test (auto-generated from .hjson)
util/dvsim/dvsim.py hw/ip/uart/dv/uart_sim_cfg.hjson -i uart_csr_hw_reset
# Run with waveforms for debugging
util/dvsim/dvsim.py hw/ip/uart/dv/uart_sim_cfg.hjson -i uart_smoke --waves fsdb
# Run the full V1 regression (comportability tests)
util/dvsim/dvsim.py hw/ip/uart/dv/uart_sim_cfg.hjson -i v1
# Run the full V2 regression with coverage
util/dvsim/dvsim.py hw/ip/uart/dv/uart_sim_cfg.hjson -i v2
Key Fields
The following table shows key fields of a sim config. The annotated example below puts all of these fields together.
| Field | Description |
|---|---|
name | DUT name — typically the block name (e.g., uart) |
dut | Top-level RTL module name |
tb | Top-level testbench SV module name |
tool | Default simulator for sign-off (typically vcs) |
fusesoc_core | FuseSoC VLNV identifier that resolves the complete RTL + DV filelist |
testplan | Path to the block’s <block>_testplan.hjson — used to track DV progress |
ral_spec | Path to the block’s design .hjson; dvsim invokes ralgen at build time to auto-generate the RAL model |
sim_tops | Additional top-level SV modules to elaborate (e.g., bind files for coverage, assertions, FI) |
import_cfgs | Shared sim cfg files to import (typically common_sim_cfg.hjson) |
build_modes | Named sets of compile-time options (e.g., different parameters, tool-specific flags) |
run_modes | Named sets of simulation-time options (plusargs, UVM verbosity, etc.) |
tests | List of test specifications — each names a UVM test class and its build/run options |
regressions | Named groups of tests: sanity, nightly, v1, v2, v3 — tied to DV stage gates |
Annotated Example
// Copyright Pavona contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Name of the sim cfg - same as the block name.
name: uart
// Top-level RTL DUT module.
dut: uart
// Top-level testbench SV module.
tb: tb
// Default sign-off simulator.
tool: vcs
// FuseSoC core file that resolves the RTL + DV filelist.
fusesoc_core: lowrisc:dv:uart_sim:0.1
// Points to the block testplan .hjson for tracking DV progress.
testplan: "{proj_root}/hw/ip/uart/data/uart_testplan.hjson"
// RAL spec: dvsim invokes ralgen at build time to auto-generate the RAL model.
ral_spec: "{proj_root}/hw/ip/uart/data/uart.hjson"
// Additional top-level elaboration targets: bind files for coverage and
// countermeasure fault injection.
sim_tops: ["uart_bind", "uart_cov_bind"]
// Import the shared sim cfg common to all comportable CIP blocks.
// This pulls in common build options, coverage config, and tool flags.
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
// Block-specific tests.
tests: [
{
name: uart_smoke
uvm_test_seq: uart_smoke_vseq
}
{
name: uart_tx_rx
uvm_test_seq: uart_tx_rx_vseq
run_opts: ["+zero_delays=1"]
}
]
// Regression suites mapped to DV stage gates.
regressions: [
{
name: sanity // Quick CI check - smoke only
tests: ["uart_smoke"]
}
{
name: v1 // D1 gate: comportability tests
tests: ["uart_smoke", "uart_csr_hw_reset", "uart_csr_rw", "uart_csr_bit_bash"]
}
{
name: v2 // D2 gate: full functional + alert/interrupt tests
tests: ["all"]
}
]
}
How sim_cfg.hjson Connects to Comportability
The critical link is through the testplan and import_cfgs fields.
The block’s testplan (<block>_testplan.hjson) imports shared comportability testplans from the common dvsim library.
These shared testplans define all the standard CSR, alert, interrupt, and TL-UL test points that every comportable block must satisfy.
// uart_testplan.hjson
{
name: "uart"
import_testplans: [
"hw/dv/tools/dvsim/testplans/csr_testplan.hjson",
"hw/dv/tools/dvsim/testplans/alert_test_testplan.hjson",
"hw/dv/tools/dvsim/testplans/intr_test_testplan.hjson",
"hw/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson",
"hw/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson",
"uart_sec_cm_testplan.hjson" // countermeasure tests
]
testpoints: [
// Block-specific tests only — all comportability tests come from imports above
{ name: smoke, ... }
{ name: tx_rx, ... }
]
}
This means every comportability test — CSR checks, alert tests, interrupt tests, TL-UL access type tests is automatically included in the block’s regression simply by the import. The block-specific testplan only adds its own functional testpoints on top.
Similarly, import_cfgs: common_sim_cfg.hjson pulls in shared build options, simulator flags, and coverage configuration common to all comportable blocks and will not need to configure these per-block.
The result: a sim_cfg.hjson with the correct imports gives you a complete, runnable regression covering all comportability requirements from the moment you first build it.
Auto-Generation with uvmdvgen
For a brand-new block, the uvmdvgen tool auto-generates a skeleton sim_cfg.hjson along with the entire
initial DV environment:
util/uvmdvgen.py <block_name> -e -c -hi -ha -eo hw/ip/<block>
Key flags: -c extends from cip_lib (for a comportable IP), -hi adds interrupt interface support, -ha adds alert interface support. The generated sim_cfg.hjson already has the correct import_cfgs, ral_spec, and testplan imports.
The cip_lib Class Hierarchy
Every comportable IP’s testbench extends from cip_lib base classes:
uvm_env
+-- cip_base_env <- in cip_lib; auto-instantiates TL-UL agent,
+-- uart_env alert agents, interrupt interfaces
uvm_scoreboard
+-- cip_base_scoreboard <- handles TL-UL, CSR, interrupt, alert checking
+-- uart_scoreboard <- adds block-specific checking
uvm_sequence
+-- cip_base_vseq <- CSR tests, TL-UL integrity, alert/interrupt tests
+-- uart_base_vseq <- block-specific sequences
By inheriting from these, every block gets standard comportability checks for free.
1. Registers : Auto-Generated RAL + CSR Tests
reggen generates a UVM RAL model from the design .hjson.
dvsim invokes ralgen at build time (via the ral_spec field in sim_cfg.hjson) to produce this model.
DV is then enabled to run standard CSR test suite on every comportable block:
| Test | What It Checks |
|---|---|
csr_hw_reset | All reset values match the spec |
csr_rw | Read/write access works correctly for each register |
csr_bit_bash | Bit-level access types: RO, RW, WO, W1C, RC, etc. |
csr_aliasing | No two addresses alias to the same register unexpectedly |
csr_mem_walk | For embedded memories: walks all addresses |
These can be run on every block automatically from shared sequences in cip_base_vseq.
A correct design .hjson means these tests pass with zero block-specific code.
2. TL-UL Interface : Shared TL-UL Agent
The shared TL-UL UVM agent drives and monitors every comportable block’s mandatory device interface.
It provides protocol checking on every transaction, integrity error injection (ECC faults on data/address bits), and error response checking (d_error assertion on integrity violations).
The shared sequence tl_intg_err_vseq handles bus integrity fault injection on every comportable block.
3. Interrupts : Shared Interrupt Verification
Because the spec mandates INTR_STATE, INTR_ENABLE, and INTR_TEST with fixed behavior, cip_lib provides scoreboard
hooks that work for any comportable block:
- Monitor when the interrupt condition occurs in the DUT
- Predict expected
INTR_STATEbit using the RAL model - Check
intr_o=INTR_STATE & INTR_ENABLE - Use
INTR_TESTto force-trigger interrupts and verify the path end-to-end
The scoreboard distinguishes between Event type (checks W1C clearing) and Status type (checks interrupt stays asserted until root cause resolved).
4. Alerts : Shared Alert Agent + Sequences
Every comportable IP with alerts gets a shared alert UVM agent connected in the testbench.
DV verifies that alerts fire on the expected error condition, the alert handshake (ping/response between prim_alert_sender and the alert handler) completes correctly, recoverable alerts fire once while fatal alerts keep firing until reset, and ALERT_TEST can force-trigger each alert end-to-end.
5. Security Countermeasures : Standardized FI Tests
Countermeasures declared in the design .hjson directly drive what DV must test.
Below table is an example:
| Declared Countermeasure | What DV Tests |
|---|---|
BUS.INTEGRITY | Inject TL-UL ECC errors; expect alert + d_error |
FSM.SPARSE | Force FSM into invalid encoding; expect fatal alert + terminal behavior |
CONFIG.SHADOW | Test shadow register update errors and storage errors |
MEM.SCRAMBLE | Verify scrambling is applied and read-back is correct |
Pavona has standardized sequences for each countermeasure type, largely reusable across blocks.
6. Shadow Register Verification
For security-critical configuration registers, Pavona uses shadow registers: a primary register and a hidden shadow copy.
Any write must go through a two-phase commit.
If the two copies disagree, an update error is flagged.
cip_lib sequences verify write phase consistency, update errors when primary and shadow disagree, storage errors injected via fault injection, and that fatal alerts are raised for unrecoverable shadow errors.
7. Comportability Compliance Checklist
Pavona maintains a formal Hardware Design & DV Checklist. Before a block advances through design and verification stages, comportability sign-offs must be completed across both the Design (D) and Verification (V) stage gates:
| Checklist Item | Stage |
|---|---|
| Design spec 90% complete, all features defined | D1 |
Design .hjson complete with CSRs defined and reviewed | D1 |
IP top-level .sv meets comportability requirements | D1 |
| IP instantiable in top-level (compiles, no X propagation on TL-UL, no spurious alerts/interrupts) | D1 |
ASSERT_KNOWN added on all IP outputs | D1 |
| Security countermeasures scoped and reviewed with Security WG | D1 |
| Lint flow set up and compiling | D1 |
| All features implemented (feature complete) | D2 |
| Feature and port specifications frozen | D2 |
| Block diagram and interface documentation updated | D2 |
| Integration guide documented for non-comportable specifics | D2 |
| Security countermeasures documented (custom + standard) | D2 |
| Lint, CDC, RDC flows passing clean | D2 |
| Area and timing checks completed (FPGA or ASIC synthesis) | D2 |
Countermeasure assets listed in canonical format in IP .hjson | D2S |
| All security countermeasures implemented per Secure HW Design Guidelines | D2S |
Sparse FSM encoding, prim_count, prim_mubi_pkg usage verified | D2S |
| Shadow registers implemented for critical control storage | D2S |
| Security council review of assets and countermeasures complete | D2S |
| All TODOs resolved; lint, CDC, RDC flows fully clean and signed off | D3 |
| Independent RTL design review completed | D3 |
| SW-visible changes and errata reviewed by SW team | D3 |
| DV document drafted (goals, strategy, TB architecture, coverage rationale) | V1 |
| Testplan completed in Hjson (testpoints mapped to features, coverage plan) | V1 |
sim_cfg.hjson created with correct imports | V1 |
| TB top created: DUT instantiated, TL-UL/clk/rst/interrupts/alerts connected | V1 |
| UVM environment created (agents, UVCs, TLM ports, scoreboard shell) | V1 |
RAL model generation automated via regtool | V1 |
| CSR check generation automated and bound in TB | V1 |
| All CSR/memory test suites passing (hw_reset, rw, bit_bash, aliasing, mem_walk) | V1 |
| Smoke test exercising main datapath passing | V1 |
| Structural coverage model checked in; functional coverage shell created | V1 |
| Nightly regression set up with multiple seeds | V1 |
| Alt-tool smoke regression passing | V1 |
| TB lint (VeribleLint) set up in nightly regression | V1 |
| Pre-verified sub-modules at V1 or higher | V1 |
| Testplan reviewed by designer, DV peers, SW, chip lead, security architect | V1 |
| DV document fully completed; design deltas captured | V2 |
| Functional coverage plan fully implemented and sampled | V2 |
| All interfaces exercised; all planned assertions enabled | V2 |
| Scoreboard end-to-end checks enabled | V2 |
| All testplan tests written and passing (≥1 seed each) | V2 |
| Interrupt tests implemented and passing | V2 |
| Alert tests implemented and passing | V2 |
| TL-UL integrity error tests passing | V2 |
| Nightly regression ≥90% passing | V2 |
| Code coverage (line, toggle, FSM, branch, assertion) ≥90% | V2 |
| Functional coverage ≥90% | V2 |
| Security countermeasures planned and documented in testplan | V2 |
| All P0/P1 bugs closed; all P2/P3 bugs root-caused | V2 |
| DV document + testplan re-reviewed (coverage exclusions scrutinised) | V2 |
Security countermeasures testplan completed (auto-generated via reggen, imported) | V2S |
| All countermeasure tests implemented and verified in simulation | V2S |
| FPV security countermeasure assertions proven | V2S |
| Coverage exclusions for sec_cm blocks removed; UNR re-generated | V2S |
| Sec_cm DV effort reviewed by designer, DV peers, security architect | V2S |
| X-propagation analysis completed, no unsuitable logic reported | V3 |
| Nightly regression 100% passing (≥1 week soak) | V3 |
| Code coverage at 100% (line, toggle, FSM, branch, assertion) | V3 |
| Functional coverage at 100% | V3 |
| All TB TODOs resolved; no tool warnings | V3 |
| TB lint flow clean and signed off | V3 |
| Pre-verified sub-modules at V3 | V3 |
| All design and testbench bugs closed | V3 |
Practical Onboarding: Adding a New Comportable IP
Here is the end-to-end workflow for a new peripheral from both design and DV perspectives:
Step 1: Write the design .hjson
Declare clocks, bus interfaces, registers, interrupts, alerts, inter-signals, and countermeasures.
Location: hw/ip/
Step 2: Run reggen Auto-generates: - Register RTL (the register file submodule) - UVM RAL model - INTR_STATE/ENABLE/TEST registers - ALERT_TEST register - Software headers and HTML documentation
Step 3: Run topgen Auto-wires inter-module signals and alert/interrupt connections at the top level.
Step 4: Bootstrap with uvmdvgen (strongly recommended)
util/uvmdvgen/uvmdvgen.py
Auto-generates:
- <block>_sim_cfg.hjson with correct import_cfgs and ral_spec
- <block>_testplan.hjson importing shared comportability testplans
- cip_lib-based UVM environment skeleton
- Testbench, base sequences, scoreboard shell
- FuseSoC core file for the DV filelist
Step 5: Complete sim_cfg.hjson - Verify dut, tb, fusesoc_core, ral_spec fields - Confirm testplan imports: csr_testplan.hjson, alert_test_testplan.hjson, intr_test_testplan.hjson - Add your block-specific tests to tests[] - Define regression suites: sanity, v1, v2, v3
Step 6: Create the UVM environment Extend cip_base_env, cip_base_scoreboard, cip_base_vseq. Connect DUT’s TL-UL, alert, and interrupt ports.
Step 7: Run shared comportability tests
util/dvsim/dvsim.py hw/ip/
Step 8: Write block-specific functional tests Cover the unique functionality of your IP. Add them to tests[] and regressions[] in sim_cfg.hjson.
Step 9: Complete the DV checklist
util/dvsim/dvsim.py hw/ip/