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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • The NanoSoC Tour
  • Reading Path
  • SoC Glossary10
  • SoC Metrics Reference
  • SoC Cheatsheet
  • What SoC Integration Is12
    • The Integrator Role and Handoffs
    • Spec to Tapeout Flow
    • Contracts and Ownership
      • Interfaces as Contracts
      • RACI and Change Control
      • Hardware-Software Contract and Errata
    • Reading the SoC Spec
  • Architecture and Budgets21
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
  • Memories, Boot and Security21
  • IP Integration and Handoff21
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
Loading lesson

←Contracts and Ownership
  1. Topics
  2. SoC Design
  3. What SoC Integration Is
  4. Contracts and Ownership
  5. Hardware-Software Contract and Errata
On this page

    Part 1 of 14, lesson 9 of 12Part 1 of 14/What SoC Integration Is/Lesson 9 of 12All levels

    Hardware-Software Contract and Errata

    The driver rules the DMA reset values demand, the bring-up order, and the errata process for what escapes.

    Why it matters

    A driver starts a DMA job with a write of 0x1 to CTRL, meaning GO without the interrupt enable, and from then on every completion is silent: bit 1 of every CTRL write copies into IRQ_EN, so writing 0x1 clears it. The next job reuses a stale KEY because LEN and KEY writes while BUSY are silently ignored. And after one failed job, the interrupt never drops, because GO clears DONE but not ERR. Three driver bugs, zero hardware bugs, one missing contract. This lesson introduces the hardware-software contract and the errata process; the register-map detail lives with the memory-maps section. Write the contract first and all three disappear.

    The core idea

    The hardware-software contract has four parts. The reset-value table: every register's reset value, and which bits read back differently from what was written, like CTRL bit 0 always reading 0. The access-type table: read-only, read-write, write-1-to-clear, write-only with side effects, and what happens on access widths the block does not support. The sequencing rules: wait for BUSY equals 0 before touching LEN or KEY, clear both DONE and ERR after every job, always start with 0x3 when interrupts are wanted, write one variable to both LEN registers, and clear the NS bit before the stream bridge touches secure memory. And the bring-up order: reset values first, then one polled job, then the interrupt path, then the error-injection cases, and only then performance. Each part is generated from the same map source as the RTL, so the contract cannot drift from the hardware it describes.

    Driver rule on NanoSoC

    Hardware truth behind it

    Symptom of breaking it

    Start with CTRL 0x3, never 0x1

    Every CTRL write copies bit 1 into IRQ_EN

    Silent completions; polling works, interrupts never fire

    Touch LEN and KEY only when BUSY is 0

    Writes while BUSY ignored with no error

    Second job uses the stale KEY; data wrong, COUNT right

    Clear DONE and ERR after every job

    GO clears DONE but never ERR

    irq stuck high after a failed job followed by a good one

    Write one length to SBR LEN and DMA LEN

    Two separate registers, one stream

    ERR with COUNT 32, or SBR stuck BUSY forever

    Clear SBR CTRL.NS for secure buffers

    NS resets to 1, non-secure

    First beat SLVERR from secure SRAM; DMA stays BUSY

    The contract model

    The script below models the DMA register behaviour exactly as the NanoSoC specification specifies it, then demonstrates the three classic driver slips against the model: the 0x1 start that kills the interrupt, the KEY written while BUSY that never lands, and the sticky ERR that survives a good job. The passing driver at the end obeys the three DMA-side rules (the LEN-pair and NS rules belong to the stream bridge and are outside this model) and completes two jobs cleanly. Keep a model like this beside the driver: it runs in milliseconds and it encodes the contract better than prose.

    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    class DMA:  # behavioural model of the DMA register rules
        def __init__(self):
            self.busy = False
            self.done = False
            self.err = False
            self.irq_en = False
            self.len = 0
            self.key = 0
            self.count = 0
    
        def write_len(self, v):
            if not self.busy:
                self.len = v & 0xFFFF  # ignored while BUSY, no error
    
        def write_key(self, v):
            if not self.busy:
                self.key = v  # ignored while BUSY, no error
    
        def write_ctrl(self, v):
            self.irq_en = bool(v & 0x2)  # copied on EVERY write
            if (v & 0x1) and not self.busy:
                if self.len == 0:
                    self.done = True
                else:
                    self.busy, self.count, self.done = True, 0, False
                    # GO clears DONE; ERR is NOT cleared
    
        def write_status(self, v):  # write-1-to-clear bits 1 and 2
            if v & 0x2:
                self.done = False
            if v & 0x4:
                self.err = False
    
        @property
        def irq(self):
            return self.irq_en and (self.done or self.err)
    
    d = DMA()
    d.write_len(64)
    d.write_ctrl(0x1)  # slip 1: GO without IRQ_EN
    d.busy = False
    d.done = True  # job completes
    assert d.irq is False  # silent completion: interrupt never fires
    d = DMA()
    d.write_len(64)
    d.write_ctrl(0x3)
    d.write_key(0xDEAD)  # slip 2: KEY while BUSY, silently ignored
    assert d.key == 0
    d = DMA()
    d.err = True  # a failed job left ERR set
    d.write_len(64)
    d.write_ctrl(0x3)  # slip 3: GO clears DONE, not ERR
    d.busy, d.done = False, True  # good job completes
    assert d.irq is True  # DONE and the stale ERR both hold irq
    d.write_status(0x2)  # software clears DONE, as a naive driver does
    assert d.irq is True  # stuck: the stale ERR still holds irq high
    d.write_status(0x4)  # the rule: clear DONE and ERR after every job
    assert d.irq is False
    
    d = DMA()  # a driver obeying the three DMA-side rules: two clean jobs
    for _ in (1, 2):
        d.write_len(64)
        d.write_key(0xA5A5)  # BUSY is 0, so both land
        assert (d.len, d.key) == (64, 0xA5A5)
        d.write_ctrl(0x3)
        assert d.busy and d.irq is False
        d.busy, d.done, d.count = False, True, 64  # stream completes
        assert d.irq is True
        d.write_status(0x6)
        assert d.irq is False
    print("PASS: 3 driver slips shown; DMA-side driver completes clean")

    The errata process

    Some hardware behaviour escapes into silicon, and then the contract grows an errata list instead of an RTL fix. Each entry carries five fields: the symptom with a reproducer, the root cause, the software workaround with its cost, the affected revisions, and the planned fix with its spin. A typical erratum candidate is behaviour nobody specified: an SBR that stays BUSY forever when the DMA LEN is shorter than the SBR LEN, say. The workaround is one length written to both registers plus a timeout on SBR BUSY, the cost is a poll in every job, and the entry closes when a spin lets the job end cleanly. The DMA's silently-ignored LEN write is different: it is specified behaviour, so it belongs in the contract and the driver rules, not on the errata list, unless the specification had promised an error response. Errata are reviewed like waivers: each has an owner, each is re-read at every release, and software never works around an erratum that is not written down, because an undocumented workaround is a bug waiting for the next driver author.

    What the review looks for

    • The contract's four parts generate from the same source as the RTL and headers, with matching revision stamps.

    • The bring-up order is a test plan, not advice: each stage gates the next, and error injection is explicit.

    • Every erratum names its workaround cost, so software can budget for hardware honesty.

    Verify and debug

    When a driver misbehaves, replay its register sequence against the model before touching hardware: most slips show as a diverged flag in three steps. When hardware misbehaves against the model, the model is wrong and the handoff's register map is the next witness. The register-side detail, banks, access types and generation flow, is taught in full by the memory-maps section of this course.

    Register side-effect errors: a read-to-clear field hit by a debugger read, or a byte-wide write causing read-modify-write side effects, losing status silently. NanoSoC's STATUS registers are write-1-to-clear only and a read has no side effect, but any chip with read-to-clear registers needs the access-type table to name them, so that debug procedures avoid the read and tests use only the legal accesses.

    If asked how hardware and software teams stop blaming each other, answer with the four-part contract, the generated-from-one-source rule, and the errata entry format. Then offer the 0x1-versus-0x3 story as the one-line proof.

    Check yourself After a debugger session the DMA interrupt is lost and STATUS DONE is clear. Can the debugger have cleared it by reading STATUS?

    Not on NanoSoC: STATUS reads have no side effects and the bits clear only by a write of 1. Look for a stray write of 1 to STATUS bit 1. On a chip with read-to-clear registers the answer would be yes, which is why the access-type table must say which registers are read-to-clear and the debug procedure must avoid reading them.

    Related topics

    • RACI and Change Control

    • Next: Reading the SoC Spec

    • Glossary

    Previous

    14 / 289

    Next: Reading the SoC Spec

    Up next

    Up nextReading the SoC Spec

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Interfaces as Contracts