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
  • Architecture and Budgets21
  • Memory Maps and Address Decode26
    • Designing the Address Map
    • Implementing Address Decode
    • Decode Errors and Default Slaves
    • Register Maps and Banks
    • Security and Privilege Regions
      • Secure and Non-Secure Regions
      • Where the Filter Lives
      • Testing Access Control
    • The Software View and the Hardware-Software Contract
    • IP-XACT and Automated Assembly
    • Memory-Map Verification
  • 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

←Security and Privilege Regions
  1. Topics
  2. SoC Design
  3. Memory Maps and Address Decode
  4. Security and Privilege Regions
  5. Testing Access Control
On this page

    Part 3 of 14, lesson 17 of 26Part 3 of 14/Memory Maps and Address Decode/Lesson 17 of 26All levels

    Testing Access Control

    Building the master-by-region test matrix, proving it complete with a script, and the cases that must never pass.

    Why it matters

    Access control fails silently in the dangerous direction: a missing check returns good data to the wrong caller, and nothing in the system complains. The only defence is a test matrix that enumerates every master against every region class in every relevant state and asserts the answer, including the answers that must be errors. This lesson builds that matrix for NanoSoC and checks its completeness mechanically, because a matrix with a hole in it is worse than none: it looks like coverage.

    The core idea

    The matrix dimensions come straight from the map: masters with a security state (the CPU, and the stream bridge in each of its two states) crossed with region classes (open memory, secure-only memory, open slots, the secure-only slot, and one hole of each responder kind: H1 answering DECERR and H3 answering SLVERR). Each cell predicts data, SLVERR or DECERR. Twenty-four cells cover NanoSoC: secure callers succeed everywhere mapped, non-secure callers succeed everywhere except SRAM1 and the reset controller, and holes answer their responder's code regardless of state. The script below encodes the table, verifies that every master-region pair is present exactly once, verifies the secure-only denials, and verifies the rule that holes never change their answer with the caller's state. It also asserts the known NanoSoC weakness from the previous lesson: because the bridge's NS bit is writable by non-secure software, a non-secure caller can reprogram the bridge into its secure state, so the matrix alone does not make SRAM1 a boundary.

    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
    REGIONS = [  # (name, secure_only, kind)
        ("SRAM0", False, "mem"), ("SRAM1", True, "mem"),
        ("DMA", False, "slot"), ("RSTPWR", True, "slot"),
        ("H1", False, "hole_decerr"), ("H3", False, "hole_slverr"),
    ]
    CALLERS = ["cpu_secure", "cpu_nonsecure", "sbr_ns0", "sbr_ns1"]
    
    def expected(caller, region):
        _name, secure_only, kind = region
        ns = caller.endswith("nonsecure") or caller.endswith("ns1")
        if kind == "hole_decerr":
            return "DECERR"  # holes answer the same in either state
        if kind == "hole_slverr":
            return "SLVERR"
        if secure_only and ns:
            return "SLVERR"
        return "DATA"
    
    matrix = {(c, r[0]): expected(c, r) for c in CALLERS for r in REGIONS}
    assert len(matrix) == len(CALLERS) * len(REGIONS) == 24
    for c in CALLERS:  # every caller meets every region exactly once
        assert sum(1 for (cc, _r) in matrix if cc == c) == len(REGIONS)
    denied = [k for k, v in matrix.items() if v == "SLVERR" and not k[1].startswith("H")]
    assert len(denied) == 4  # 2 secure-only regions x 2 non-secure caller-states
    assert all(matrix[(c, "H3")] == "SLVERR" for c in CALLERS)  # H3 in every state
    for c in ("cpu_secure", "sbr_ns0"):
        assert all(v != "SLVERR" for (cc, r), v in matrix.items() if cc == c and r != "H3")
    assert matrix[("cpu_secure", "H1")] == matrix[("cpu_nonsecure", "H1")] == "DECERR"
    # Known weakness (previous lesson): the NS bit sits in a non-secure slot,
    # so a non-secure caller can move the bridge from sbr_ns1 to sbr_ns0 at will.
    assert matrix[("sbr_ns1", "SRAM1")] == "SLVERR" and matrix[("sbr_ns0", "SRAM1")] == "DATA"
    print("PASS: 24-cell matrix complete; 4 secure denials; NS weakness asserted")

    One count in there deserves a pause: four denials, because two caller-states are non-secure (the CPU in non-secure software and the bridge with NS set) and each is denied at exactly two regions. If a future region goes secure-only, the secure-denial count must grow by exactly the number of non-secure caller-states; a test plan where it does not is a test plan with a hole. The four H3 cells are SLVERR too but are not denials: H3 refuses everyone, in every state, because nothing is there behind the bridge. The matrix is a living artifact: it regenerates from the map table, and the verification topic reruns it on every map change.

    What the review looks for

    • The matrix is generated from the map table, and its cell count multiplies out: callers times region classes, no gaps.

    • Denial cells assert no data as well as the error code; an SLVERR with valid-looking data still fails.

    • Both master states of configurable masters (the bridge with NS set and clear) appear as separate rows.

    Verify and debug

    Run the matrix as directed accesses in simulation, then again on the bench through the debug access where possible. A cell that returns data where SLVERR is predicted means the filter is bypassed for that master; a cell that returns DECERR where SLVERR is predicted means the fault is routed to the default slave, the decode inconsistency from the first topics. Either way the failing cell names the level at fault, which is what makes the matrix a debug tool and not just a test list.

    Testing access control only from the CPU. The bridge, the DMA path and any second master each carry their own state, and a filter checked for one master says nothing about the others. Enumerate masters as first-class rows, configurable states included.

    If asked how you would test security regions, answer with the matrix: callers times region classes, predicted response per cell, completeness proven by counting, denials asserting no data. Then name the two NanoSoC subtleties: holes are state-independent, and the bridge appears twice.

    Check yourself A new secure-only SRAM2 is added. How many new test cells and how many new denials?

    One new region class adds four cells, one per caller-state. Denials grow by the two non-secure caller-states (non-secure CPU and bridge with NS set), so the denial count goes from four to six.

    Related topics

    • Where the Filter Lives

    • Memory-Map Verification

    • Verification First Principles: Security Verification

    Previous

    55 / 289

    Next: Interconnect and Bus Fabrics

    Up next

    Up nextInterconnect and Bus Fabrics

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Secure and Non-Secure Regions