Revision sheets drawn from the course. Formulas are exact where they are definitions. Every NanoSoC number is a teaching value from the running chip; none is a target for any real process or product. Addresses are byte addresses, sizes use 1 KB = 1024 bytes and MB/s means 10^6 bytes per second.
peak bandwidth = W x f W = 4 bytes, f = 96 MHz -> 384.0 MB/s
burst efficiency = L / (L + 1) one bubble cycle per burst of L beats
sustained bandwidth = peak x efficiency min over every link of the path
single-port memory = reads + writes share one port, each of two streams gets sustained / 2
APB access = bytes x f / cycles per access (2 without wait state, 3 with one)
period = 1000 / f(MHz) ns 1 T_sys = 2 T_cpu, 1 T_per = 4 T_sys = 8 T_cpu
PLL output = f_ref x N / R 24 x 32 / 1 = 768 MHz; dividers 4 and 8
baud = clk_per / (16 x BAUDDIV) 24e6 / (16 x 13) = 115,384.6
SPI clock = clk_per / (2 x (CLKDIV + 1)) CLKDIV = 1 -> 6 MHz
DMA job time = (N + 8) clk_sys N = 64 -> 72 cycles = 750 ns
interrupt latency = 3 T_per + 2 T_cpu best; + T_per + T_cpu worst 135.4 / 182.3 ns
utilisation = core area / die core budget 0.655 / 0.940 = 69.7%
power in a state = dynamic + leakage RUN 82.74, IDLE 16.89, SLEEP 1.40 mW
energy = power x time 82.74 mW x 750 ns = 62.1 nJ
aligned base = base mod size == 0 sizes are powers of two
last address = base + size - 1Code | Where | Meaning | NanoSoC example |
|---|---|---|---|
OKAY (2'b00) | AXI RRESP and BRESP | Access completed normally. | Any access to a mapped, permitted address. |
EXOKAY (2'b01) | AXI RRESP and BRESP | Exclusive access succeeded. | Not used: the fabric has no exclusive access. |
SLVERR (2'b10) | AXI RRESP and BRESP | A slave was reached and refused or failed the access. | Non-secure access to SRAM1; a write to the read-only ROM window; APB hole H3 (via PSLVERR). |
DECERR (2'b11) | AXI RRESP and BRESP | No slave claims the address. | Holes H1, H2 and H4, from the default slave. |
PSLVERR = 1 | APB, in the completing cycle | APB slave error; the bridge makes it SLVERR. | DMA offset not in 0x00, 0x04, 0x08, 0x0C, 0x10; any reserved slot 7 to 15. |
Region | Base | End | Size | Secure | Notes |
|---|---|---|---|---|---|
BOOTROM | 0x0000_0000 | 0x0000_3FFF | 16 KB | S and NS read | Reset vector at 0x0000_0000 |
Hole H1 | 0x0000_4000 | 0x1FFF_FFFF | - | - | DECERR |
SRAM0 | 0x2000_0000 | 0x2000_FFFF | 64 KB | S and NS | Retained in SLEEP; stack top 0x2001_0000 |
SRAM1 | 0x2001_0000 | 0x2001_FFFF | 64 KB | Secure only | NS access gives SLVERR |
Hole H2 | 0x2002_0000 | 0x3FFF_FFFF | - | - | DECERR |
DMA | 0x4000_0000 | 0x4000_0FFF | 4 KB | NS OK | Registers 0x00 to 0x10; aliased 16 times |
SBR | 0x4000_1000 | 0x4000_1FFF | 4 KB | NS OK | SRC_ADDR, DST_ADDR, LEN, CTRL, STATUS |
UART | 0x4000_2000 | 0x4000_2FFF | 4 KB | NS OK | |
SPI | 0x4000_3000 | 0x4000_3FFF | 4 KB | NS OK | |
GPIO | 0x4000_4000 | 0x4000_4FFF | 4 KB | NS OK | |
INTC | 0x4000_5000 | 0x4000_5FFF | 4 KB | NS OK | |
RSTPWR | 0x4000_6000 | 0x4000_6FFF | 4 KB | Secure only | |
Hole H3 | 0x4000_7000 | 0x4000_FFFF | 36 KB | - | PSLVERR then SLVERR |
Hole H4 | 0x4001_0000 | 0xFFFF_FFFF | - | - | DECERR |
decode (nano_xbar): addr[31:14] == 0 ROM
addr[31:16] == 0x2000 SRAM0
addr[31:16] == 0x2001 SRAM1
addr[31:16] == 0x4000 APB bridge, then addr[15:12] = slot (0 to 6 mapped, 7 to 15 default)
otherwise default slave -> DECERR
secure filter: AxPROT[1] == 0 secure, 1 non-secure
DMA registers: 0x00 CTRL 0x04 STATUS 0x08 LEN 0x0C KEY 0x10 COUNT (byte offsets, 8-bit paddr)# decode and map check in one place; the checker must be able to FAIL
regions = [
("BOOTROM", 0x0000_0000, 0x4000), ("SRAM0", 0x2000_0000, 0x1_0000), ("SRAM1", 0x2001_0000, 0x1_0000),
("DMA", 0x4000_0000, 0x1000), ("SBR", 0x4000_1000, 0x1000), ("UART", 0x4000_2000, 0x1000),
("SPI", 0x4000_3000, 0x1000), ("GPIO", 0x4000_4000, 0x1000), ("INTC", 0x4000_5000, 0x1000),
("RSTPWR", 0x4000_6000, 0x1000),
]
def check_map(regs):
problems = []
for name, base, size in regs:
if size & (size - 1):
problems.append(name + " size is not a power of two")
elif base % size:
problems.append(name + " base is not aligned to its size")
ordered = sorted(regs, key=lambda r: r[1])
for a, b in zip(ordered, ordered[1:]):
if a[1] + a[2] > b[1]:
problems.append(a[0] + " overlaps " + b[0])
return problems
def decode(addr, secure=True):
if addr >> 14 == 0:
return "BOOTROM", "OKAY"
if addr >> 16 == 0x2000:
return "SRAM0", "OKAY"
if addr >> 16 == 0x2001:
return "SRAM1", "OKAY" if secure else "SLVERR"
if addr >> 16 == 0x4000:
slot = (addr >> 12) & 0xF
names = ["DMA", "SBR", "UART", "SPI", "GPIO", "INTC", "RSTPWR"]
return (names[slot], "OKAY") if slot < 7 else ("default APB slave", "SLVERR")
return "default slave", "DECERR"
assert check_map(regions) == []
assert regions[-1][1] + regions[-1][2] - 1 == 0x4000_6FFF
# the planted overlap: an 8 KB DMA window collides with the SBR slot
bad = [("DMA", 0x4000_0000, 0x2000) if r[0] == "DMA" else r for r in regions]
assert check_map(bad) == ["DMA overlaps SBR"]
# a misaligned base is caught too
assert check_map([("X", 0x4000_0800, 0x1000)]) == ["X base is not aligned to its size"]
assert decode(0x0000_4000) == ("default slave", "DECERR") # H1
assert decode(0x2002_0000) == ("default slave", "DECERR") # H2
assert decode(0x4000_7000) == ("default APB slave", "SLVERR") # H3
assert decode(0x4001_0000) == ("default slave", "DECERR") # H4
assert decode(0x2001_0000, secure=False) == ("SRAM1", "SLVERR")
assert decode(0x4000_0100)[0] == "DMA" # the alias image
assert decode(0x4000_6000)[0] == "RSTPWR"
print("PASS: map is clean, planted overlap and misalignment are caught, holes answer as listed")Domain | Frequency | Period | Source | Blocks |
|---|---|---|---|---|
clk_cpu | 192 MHz | 5.208 ns | 768 / 4 | CPU core, I-cache, CPU side of the async AXI bridge |
clk_sys | 96 MHz | 10.417 ns | 768 / 8 | Fabric, SRAMs, ROM, DMA, SBR, AXI to APB bridge, APB decoder, bridge system side |
clk_per | 24 MHz | 41.667 ns | Crystal, bypasses the PLL | UART, SPI, GPIO, INTC, reset and power controller, bridge peripheral side |
Ratio clk_cpu : clk_sys : clk_per = 8 : 4 : 1. The first two share a PLL but are signed off as asynchronous.
Ten crossing groups. Rule: nothing multi-bit crosses without a handshake or a FIFO, nothing single-bit crosses without two flops. Clock gates are not crossings.
Gates: clk_cpu on wfi; clk_dma and clk_sbr on CLK_EN bits; all latch-based and enabled by the reset and power controller.
PLL lock 256 clk_per = 10.667 us. Clocks open only after lock.
Source | Condition | Resets | RST_CAUSE bit |
|---|---|---|---|
por_n | Supply below threshold | Everything including RST_CAUSE | [0] POR |
pad_rst_n | Pin low for more than 16 clk_per | Everything except RST_CAUSE and SRAM contents | [1] PAD |
sw_rst | SWRST write with key 0xA5 | rst_sys_n and rst_cpu_n for 16 clk_sys | [2] SW |
pwr_rst | PD_SW powers up | rst_sys_n and rst_cpu_n | [3] WAKE |
cold boot release (hardware 53.948 us):
R1 oscillator ready 1024 clk_per 42.667 us
R2 rst_per_n released 2 clk_per 83.333 ns
R3 PLL locked 260 clk_per 10.833 us
R4 clock gates open 2 clk_per 83.333 ns
R5 rst_sys_n released 18 clk_sys 187.5 ns
R6 rst_cpu_n released 8 clk_sys + 2 clk_cpu 93.75 ns slaves and fabric first, CPU last
reset is asserted asynchronously, released synchronously in each domain (2-flop nano_rst_sync)State | PD_AON | PD_SW | PLL | Power | Entry | Exit |
|---|---|---|---|---|---|---|
RUN | on | on | on | 82.74 mW | Boot or wake | wfi or SW_OFF_REQ |
IDLE | on | on, CPU clock gated | on | 16.89 mW | CPU wfi | Any enabled interrupt |
SLEEP | on, SRAM0 retained | off, outputs isolated | off | 1.40 mW | SW_OFF_REQ | GPIO, UART or power interrupt |
power-down (70 clk_per = 2.917 us): quiesce DMA and SBR -> iso_sw_en (2) -> gate clocks (2) -> assert resets (2) -> open switch, ramp 64
isolation before reset before power off
power-up (wake, 14.073 us): close switch, ramp 64 -> pgood +8 -> PLL relock 256 -> clocks open +2
-> release isolation (+4 clk_sys) -> rst_sys_n (18 clk_sys) -> rst_cpu_n (8 clk_sys + 2 clk_cpu)
RST_CAUSE[3] set; ROM jumps to 0x2000_0000 (SRAM0 retained)
isolation: 47 (APB bridge) + 52 (SRAM0 port) + 13 (ROM port) + 2 (irq_dma, irq_sbr) = 114 clamps to 0; AON to SW needs none
one voltage (0.9 V): isolation only, no level shiftersIRQ | Name | Clear by | Priority |
|---|---|---|---|
0 | irq_pwr | W1C PWR_IRQ | 0 (highest) |
1 | irq_dma | W1C DONE and ERR in DMA STATUS, or clear IRQ_EN | 1 |
2 | irq_sbr | W1C in SBR STATUS | 2 |
3 | irq_uart | Read or write DATA | 3 |
4 | irq_spi | Read DATA | 4 |
5 | irq_gpio | Remove the condition | 5 |
6, 7 | Reserved, tied to 0 | n/a | 6, 7 |
All inputs are active-high levels. PENDING = RAW & ENABLE. TOP returns the lowest pending index, 0x1F if none. A pulse under 2 clk_per (83.3 ns) can be missed.
DMA CTRL: write 0x3 to start with the interrupt on (a write of 0x1 clears IRQ_EN). LEN and KEY writes are ignored while BUSY, with no error. GO does not clear ERR: write 0x6 to STATUS after every job.
SBR LEN and DMA LEN are separate: set both to the same word count. SBR CTRL.NS resets to 1 (non-secure); a secure source needs it cleared, and because the SBR slot is non-secure, non-secure software can clear it too: known weakness, partition not closed.
Source and destination in one SRAM bank halve the stream rate to 180.7 MB/s each.
hardware reset 53.948 us (0.43% of boot)
first fetch (line fill) 125 ns
read RST_CAUSE 281.25 ns WAKE: skip the clear, SRAM0 holds the image
clear 4 KB of SRAM0 10.667 us (cold boot only)
UART init 843.75 ns
banner, 16 chars 1,386.667 us (11.0%)
DMA self-test 8.385 us
SPI load 8196 bytes 10,928.000 us (86.9%)
checksum 2048 words 192.000 us
jump to 0x2000_0000 total 12.581 msBlock handoff into the top (before wiring)
Collateral complete and versioned: RTL, register description, constraints, power intent, test and debug models, change notices.
Port widths and address conventions recorded per block (for example byte offsets against word offsets, 8 address bits against 12).
Clock, reset and power domain of every port named; every crossing has a mechanism.
Reset values and what writes to read-only, busy and unmapped locations do are written down.
Unused inputs have tie-offs, unused outputs are listed, and lint runs with width errors not waived.
Before tapeout
Map checker clean on the frozen map description: no overlap, every base aligned, holes and aliases listed and tested.
Connectivity and register sweeps pass on the frozen baseline, including each interrupt line against its controller input.
Reset order proven: the CPU leaves reset last, in simulation of the netlist as well as the RTL.
Power-aware simulation switched the domain off and on; all 114 clamps present and enabled before reset and power-off.
Crossing inventory matches the ten groups; no open crossing violations.
Every waiver has an owner, evidence, a review-by date and an expiry; none is past expiry.
Pad reset states and pulls reviewed against the ball-out (strap and test pins too, on any chip that has them: NanoSoC v1 has none).
Manifest checker passes against the baseline tuple; each owner has signed their evidence.
First suspect for a symptom
Symptom | First suspect |
|---|---|
CPU stalls at PC = 0x0000_0000, ARVALID high, no RVALID | Reset release order: CPU released with or before the fabric |
DMA output has bit 0 flipped after the SBR is started | Overlapping decode windows: an SBR write also wrote the DMA KEY |
DMA DONE is set and irq high, but the ISR never runs | Interrupt wired to the wrong controller input (check RAW) |
Only CTRL reads right; STATUS, LEN, KEY give PSLVERR and a COUNT read returns STATUS | Address connected as word offsets instead of byte offsets |
Interrupt missed once in thousands of jobs | Missing two-flop synchroniser on a crossing |
Sleep and wake loop, X on always-on side | Missing isolation on interrupt or memory-port signals |
SBR ERR when copying from SRAM1 | SBR issues non-secure accesses: NS attribute reset value |
DMA ERR with COUNT short, or SBR BUSY forever | LEN mismatch between the DMA and the SBR |
Second job wrong or irq stuck high | LEN or KEY write ignored while BUSY; stale ERR not cleared |
CPU hangs on first DMA access after CLK_EN = 0 | Clock gated during an APB access with no bridge timeout |
No. It is the alias: the slot is 4 KB but the DMA decodes only 8 address bits, so its 256-byte window repeats 16 times. Address 0x4000_0204 is offset 0x04 of the image at 0x4000_0200, which is STATUS. The remedy is a software rule (use only 0x4000_0000 to 0x4000_00FF) and a test that no slave uses apb_paddr[11:8], not a change inside the DMA.