Back to blog Verification

Custom Verification Rules for RF Block Libraries

Custom Verification Rules for RF Block Libraries

Skycore includes assertion templates for common RF interfaces: SPI register access, power-up sequencing, PLL lock and loss-of-lock clock-domain checks, and more. They cover much of a typical RF verification plan. For many early-access teams, the defaults were enough to pass a first simulation without hand-written assertions.

As reusable RF IP accumulates across tape-out projects, gaps appear. A mixer may impose a gain-step ordering rule that no generic template represents. A VCO tuning register may require a write-before-enable sequence that is clear to block experts but absent from generic rules. Such constraints remain in engineers' memories and old simulation logs instead of a shared assertion library.

This post shows how Skycore encodes team-specific constraints as reusable verification rules, using an internal example based on our RF building blocks.

Where Generic Assertions Miss Shared IP Bugs

Generic assertions catch clear protocol errors: writing a read-only field, popping an empty FIFO, or toggling a clock before reset deassertion. These are structural defects. One template can define the pattern and apply it wherever that pattern occurs.

Shared RF IP libraries also collect semantic constraints tied to analog behavior. An LNA may require gain mode before bias-current programming because its bias table depends on gain mode. Programming bias first still lets the simulator run and the register map retain the values, but silicon receives the wrong bias. A generic assertion misses this because the transaction sequence is valid at the protocol level.

The pattern also applies to PLL charge-pump settings, PA power-class initialization, and ADC reference-voltage ordering. Each may appear in a datasheet or design-review note, but that scattered documentation is the issue: the requirement is absent from the verification environment that must enforce it.

Skycore Custom Rule Format

A Skycore custom verification rule is a named constraint with four fields: scope, identifying the block or register map; trigger, defining the transaction or signal event; predicate, stating what must be true; and severity. Rules reside in YAML read during block configuration, and the verification agent converts each one into an SVA property within the generated testbench.

Here is a shortened gain-before-bias rule:

YAML
# skycore-rules.yaml for lna_ctrl block
rules:
  - name: gain_mode_before_bias_write
    scope: lna_ctrl
    trigger:
      register: BIAS_CTRL
      access: write
    predicate:
      register_written_before: GAIN_MODE
      field: GAIN_MODE.enable
      expected_state: 1
    severity: error
    message: "BIAS_CTRL written before GAIN_MODE.enable is set; bias table selection will be incorrect"

The verification agent parses this rule and creates an SVA property that triggers when a write to BIAS_CTRL occurs before GAIN_MODE.enable is set. The resulting SystemVerilog is approximately:

SystemVerilog
property gain_mode_before_bias_write;
  @(posedge clk)
  (spi_write && spi_addr == BIAS_CTRL_ADDR)
  |-> ##[0:$] (gain_mode_enable_latched == 1'b1)
    throughout (spi_write && spi_addr == BIAS_CTRL_ADDR)[*1];
endproperty

assert property (gain_mode_before_bias_write)
  else $error("BIAS_CTRL written before GAIN_MODE.enable is set");

Generated output differs with the block clock setup and register map, but its logic comes straight from the rule. The YAML author need not know SVA syntax, while an engineer reviewing the SVA can trace it to the source YAML rule.

Structuring Rules for a Block Library

After the first few custom rules, organize them for discovery, tape-out reuse, and straightforward updates when block behavior changes.

Use one rule file per block, matching its module name. Keep these files in a shared, source-controlled verification-rules directory apart from RTL. During testbench generation, Skycore searches a configurable path for the matching file. Found rules merge with the default assertions; otherwise, only defaults are used.

For blocks reused across chip projects with different register-map revisions, Skycore supports versioning through schema_version. Increment that field when the map changes and create rules for the new version. At generation, the verification agent chooses the version matching the supplied block configuration. This avoids stale assertions pointing to addresses from an earlier block revision.

Capturing Initialization Order

Initialization constraints are often the hardest rules in an RF block library. They describe register writes that must occur in order before normal operation begins. A single-trigger predicate is insufficient because the requirement spans transactions over time.

Skycore uses a sequence rule type for this case. It lists the required steps in order, while the verification agent builds a state machine and reports failure when a step is skipped or arrives out of order:

YAML
rules:
  - name: pll_init_sequence
    type: sequence
    scope: pll_ctrl
    steps:
      - register: PLL_RST
        field: reset_n
        value: 0
      - register: PLL_CFG
        field: ref_div
        any_value: true
      - register: PLL_CFG
        field: fb_div
        any_value: true
      - register: PLL_RST
        field: reset_n
        value: 1
    severity: warning
    message: "PLL initialization sequence violated; lock behavior undefined"

With any_value: true, the rule checks only that a write occurs at that sequence position, not its value. This suits project-specific divider settings when the ordering remains fixed.

Limits of This Approach

This custom format fits register-map constraints and transaction ordering. It does not replace analog coverage, transistor-level simulation, or block characterization. Because the rules observe the digital control interface, they cannot detect analog drift caused by process corners or temperature. Those checks belong elsewhere in the verification plan.

Rule generation also depends on rule quality. If a team has not recorded a constraint in a rule file, Skycore cannot infer it. The tool translates documented requirements into executable assertions; it does not uncover unspecified constraints.

Early-access teams have gained the most by converting one engineer's mental model of block quirks into a shared, executable, version-controlled artifact. The block expert records the rules once, and each later tape-out using that block receives them automatically, without relying on someone to recall the sequencing requirements.

Building Your First Rule Set

Start with one RF block in the current project. Identify the top three constraints behind simulation bugs or bring-up problems in the previous tape-out, then encode those three as Skycore rules. Run the verification agent and check that its assertions match your intent. Add the file to the verification repository, then configure Skycore to load it during the next RTL generation pass.

The payoff grows with each block and tape-out. By the third project, a carefully written rule set clearly repays its cost: the engineer on that project may not have designed the block and would otherwise lack its constraints.

Good rule-file documentation also matters. The message field supplies the simulation-log error text. Explaining both the constraint and its purpose reduces triage time when an assertion fires and an engineer unfamiliar with the block must determine the fix.

Teams setting up Skycore around an existing IP library can use its documentation to convert verification notes or block datasheets into rule files. It defines the YAML schema and available rule types. Once the format is understood, turning written requirements into executable rules is largely mechanical.