Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
    • Intent and the Oracle
    • From Prose to Rules
    • Reference Models
    • Tables and Executable Specifications
    • Specification Review and Ambiguity
    • Requirements Traceability
    • What Not to Check
    • Drills: Specifications as Checkable Rules
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 2: Specifications as Checkable Rules
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 2: Specifications as Checkable Rules
  4. Tables and Executable Specifications

Part 2 · Specifications as Checkable Rules · Senior (S)

Tables and Executable Specifications

Much of a specification is really data: register maps, state tables, opcode tables, error codes. Written as machine-readable tables, the same data can generate documentation, RTL and checkers, so they cannot drift apart. A table-driven DCRouter register checker, built and run.

On this page

    Note

    Level: Senior (S) · Answers: What exactly are we checking against?

    Before this: Reference Models

    A chip has 4,000 registers. The specification lists them in a document, the designers type them into RTL, the verification team types them into a register model, and the software team types them into header files. Four copies of the same table, typed by four teams. Every mismatch between two copies is a bug, and most of them are typing errors, not design decisions.

    When a specification is really a table, how should it be written and checked?

    The obvious answer, and why it breaks

    The obvious approach writes the table once in the document and lets each team implement and check it by hand. Hand copies drift: a reset value updated in the RTL but not in the document, a field width changed in the header but not in the register model. Checking one hand copy against another finds some errors and enshrines others, and every change must be made four times, correctly.

    The idea: write the table once, as data

    • Machine-readable source: the register map (or state table, opcode table) is written once in a structured format; IP-XACT and SystemRDL are standard formats for register descriptions (fact V4).

    • Generate everything else: documentation, RTL register blocks, the verification register model and software headers all come from the source.

    • Check against the source: a generic checker reads the table and checks the design's behaviour, with no register-specific code to get wrong.

    • One change, one place: updating the source updates every consumer.

    Part of a specification

    As a table

    Checked by

    Register map

    Address, name, access type, reset value, fields, side effects

    A table-driven register checker

    State machine

    State, input, next state, outputs

    A table-driven transition checker or formal

    Opcode or command set

    Code, operands, expected effect

    A model that dispatches on the table

    Error responses

    Condition, response code, counters affected

    Error-injection tests with expected codes from the table

    Build it: a table-driven register checker

    DCRouter's register map (rules R9 to R12) as a table, and a checker that replays a log of APB transactions against it. The log contains a CTRL read after reset returning 0x37, as Bug Zoo BUG_17 would produce, and a read of an unmapped address answered without pslverr.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    """A table-driven register checker: the register map as data, not as code.
    
    DCRouter's register map (rules R9 to R12) is written once as a table: address, name,
    access, reset value, mask, and what a write does. The checker replays a log of APB
    transactions against the table: it tracks the expected value of every register, applies
    write effects, and compares every read. (In a full testbench, counters that hardware
    updates would be predicted by the reference model of lesson 2.3.) The log below contains one planted mistake, a CTRL read after reset returning 0x37
    instead of 0x3F (Bug Zoo BUG_17), and a read of an unmapped address.
    """
    TABLE = [  # addr, name, access, reset, mask, on write
        (0x00, "CTRL",     "RW", 0x3F, 0x3F, "store"),
        (0x04, "STATUS",   "RO", 0x00, 0x1F, "ignore"),
        (0x10, "ERR_CNT0", "RC", 0x00, 0xFF, "clear"),
        (0x14, "ERR_CNT1", "RC", 0x00, 0xFF, "clear"),
        (0x18, "ERR_CNT2", "RC", 0x00, 0xFF, "clear"),
        (0x1C, "ERR_CNT3", "RC", 0x00, 0xFF, "clear"),
        (0x20, "PKT_CNT0", "RC", 0x0000, 0xFFFF, "clear"),
        (0x24, "PKT_CNT1", "RC", 0x0000, 0xFFFF, "clear"),
        (0x28, "PKT_CNT2", "RC", 0x0000, 0xFFFF, "clear"),
        (0x2C, "PKT_CNT3", "RC", 0x0000, 0xFFFF, "clear"),
    ]
    BY_ADDR = {row[0]: row for row in TABLE}
    
    # a log: (op, addr, data, pslverr); "read" data is what the design returned
    LOG = [
        ("read", 0x00, 0x37, 0),      # CTRL after reset: expected 0x3F
        ("write", 0x00, 0x2F, 0),     # priority off
        ("read", 0x00, 0x2F, 0),
        ("read", 0x14, 0x00, 0),
        ("write", 0x14, 0x00, 0),     # clear ERR_CNT1
        ("read", 0x14, 0x00, 0),
        ("read", 0x40, 0x00, 0),      # unmapped: the design must answer pslverr
    ]
    
    def check(log):
        expect = {a: r[3] for a, r in BY_ADDR.items()}
        problems = []
        for n, (op, addr, data, err) in enumerate(log):
            row = BY_ADDR.get(addr)
            if row is None:
                if not err:
                    problems.append(f"#{n} {op} 0x{addr:02X}: unmapped address without pslverr (R12)")
                continue
            _, name, access, reset, mask, on_write = row
            if op == "write":
                if on_write == "store":
                    expect[addr] = data & mask
                elif on_write == "clear":
                    expect[addr] = 0
            elif (data & mask) != expect[addr]:
                problems.append(f"#{n} read {name}: got 0x{data & mask:X}, table says 0x{expect[addr]:X}")
        return problems
    
    problems = check(LOG)
    print(f"register table: {len(TABLE)} registers, log of {len(LOG)} transactions")
    for p in problems:
        print("  " + p)
    assert problems == [
        "#0 read CTRL: got 0x37, table says 0x3F",
        "#6 read 0x40: unmapped address without pslverr (R12)",
    ]
    print("PASS: the table-driven checker found the wrong CTRL reset value and the missing pslverr, with no register-specific code")

    It prints:

    Diagram
    register table: 10 registers, log of 7 transactions
      #0 read CTRL: got 0x37, table says 0x3F
      #6 read 0x40: unmapped address without pslverr (R12)
    PASS: the table-driven checker found the wrong CTRL reset value and the missing pslverr, with no register-specific code

    How to read it: the checker knows nothing about DCRouter beyond the table. It finds the wrong reset value (port 3 disabled) and the missing error response for address 0x40, and accepts the write to CTRL and the clear of ERR_CNT1. Adding a register means adding a row, not writing a new check.

    Limits of tables

    Works well for

    Needs more than a table

    Reset values, access types, address decoding

    Side effects that depend on hardware events (counters, status)

    Field layouts and masks

    Timing between a write and its effect

    Simple write effects (store, clear, ignore)

    Behaviour that depends on history or other registers

    Hardware-updated values, such as DCRouter's error and packet counters, need the reference model (lesson 2.3) to predict them; the table says how they are accessed, the model says what they should contain.

    How real teams use executable specifications

    Practice

    Purpose

    Register descriptions in IP-XACT or SystemRDL

    One source for documents, RTL, register models and headers

    Generated register models in the testbench (for example the UVM register layer, fact V4)

    Table-driven checks of every register

    State tables reviewed as data

    Missing and duplicate transitions are visible

    What breaks

    A project (illustrative) typed its 2,000-register map into RTL and into its verification model by hand. A late change moved a field in one register; the RTL was updated, the model was not, and the document was updated a week later. The verification model flagged a mismatch, the team "fixed" the RTL to match the stale model, and the software, written from the updated document, failed in the lab. Generating all three from one source would have made the inconsistency impossible.

    Why this matters

    Senior lens. Wherever part of a specification is data, insist on one machine-readable source and generate every consumer from it. It removes a whole class of bugs (copies that disagree) and turns many checks into generic code that is written once and trusted, leaving engineers to verify the behaviour a table cannot describe.

    Interview

    Asked: "How do you keep the register model and the specification in sync?"

    Weak answer: Review them against each other regularly.

    Strong answer: Don't keep separate copies at all. Write the register map once in a machine-readable form like IP-XACT or SystemRDL and generate the documentation, the RTL register block, the verification register model and the software headers from it. Then a generic, table-driven checker verifies reset values, access types, decoding and simple write effects, with no register-specific code. Hardware-updated fields like counters still need the reference model to predict their values.

    Follow-ups: (1) What can a register table not describe? (2) What goes wrong with hand-typed copies?

    Check yourself Why can a table-driven checker find a wrong reset value without any code specific to CTRL?

    The expected reset value is a row in the table; the generic checker compares every register's first read with its row.

    Check yourself What does the table leave to the reference model in DCRouter?

    The values of hardware-updated registers (ERR_CNT, PKT_CNT, STATUS), which depend on traffic, not on writes.

    Key takeaways

    • Much of a specification is data: registers, states, opcodes, error codes.

    • Write it once as a machine-readable table and generate documents, RTL, models and checkers from it.

    • Table-driven checkers cover access and reset behaviour; reference models cover hardware-updated values.

    Keep going

    Next: Lesson 2.5, Specification Review and Ambiguity. Reviewing a specification as a verifier: the questions that find gaps.

    Related topics

    • Part 2 overview: Specifications as Checkable Rules

    Previous

    28 / 160

    Next: Specification Review and Ambiguity

    Up next

    Up nextSpecification Review and Ambiguity

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Intent and the Oracle
    • From Prose to Rules
    • Requirements Traceability