NanoSoC is a small 32-bit system that the whole course integrates together. A generic in-order core fetches through a 4 KB instruction cache, crosses into a 96 MHz AXI4 fabric with two masters and four slaves, and reaches on-chip ROM and RAM, a streaming DMA fed by a stream bridge, and a slow peripheral subsystem behind two bridges. One controller owns clocks, resets and power; one controller gathers eight interrupts. This page is the single place every number in the course comes from: the block list, the address map, the three clocks, the three resets, the two power domains, the interrupt table and the boot flow. When any two pages disagree, this one wins.
Solid edges carry bus traffic and dotted edges are interrupts. The data path SRAM0 to DMA to SRAM1 runs through the fabric: the SBR is the second master of nano_xbar, it reads one SRAM through the crossbar, streams the words through the DMA, and writes them back to the other SRAM through the crossbar. nano_rstpwr sits with the slow peripherals and drives the reset, clock and power sequences.
Block | Role in one line | Clock | Power |
|---|---|---|---|
| Generic 32-bit in-order core, 4 KB I-cache, one AXI4 master port |
| PD_SW |
| Asynchronous AXI bridge between the CPU port and the fabric |
| PD_SW |
| AXI4 interconnect, 2 masters by 4 slaves, round-robin, secure filter, default slave |
| PD_SW |
| 64 KB on-chip RAM, retained when PD_SW is off |
| PD_AON |
| 64 KB on-chip RAM, secure-only |
| PD_SW |
| 16 KB boot ROM, holds the reset vector |
| PD_AON |
| The UVM course DMA: XOR-with-KEY stream DMA, APB registers, AXI-Stream in and out, one interrupt |
| PD_SW |
| Stream bridge: AXI4 master that turns memory into a stream and back, with an APB register block |
| PD_SW |
| AXI4 slave to APB bridge plus the APB slot decoder |
| PD_SW |
| APB request and acknowledge bridge to the slow peripherals |
| PD_SW |
| UART, 8N1, 16x oversampling, one interrupt |
| PD_AON |
| SPI master, mode 0, loads the second-stage image |
| PD_AON |
| 16 pins, direction register, level interrupt |
| PD_AON |
| Interrupt controller: 8 level inputs, one level output, fixed priority |
| PD_AON |
| Reset, clock and power controller, with the PLL wrapper and the power sequencer |
| PD_AON |
PLL (analog macro) | 24 MHz in, 768 MHz VCO out |
| PD_AON |
The top module is nanosoc_top. Pads and the crystal sit outside it: the chip has 31 pads (19 signal and 12 supply), in a conceptual 36-ball package. Where a lesson needs the wiring detail, the DMA's ports and the crossing inventory are given below; the full per-pin tables live in the lessons.
A 32-bit physical address, byte addressed, little endian. Peripheral registers are 32-bit and word aligned. Every base is aligned to its own power-of-two size, and no two rows overlap.
Region | Base | Size | End | Notes |
|---|---|---|---|---|
|
| 16 KB |
| Reset vector at |
Hole H1 |
| 0x1FFF_C000 |
| No slave: default slave answers DECERR |
|
| 64 KB |
| Retained in SLEEP; stack top |
|
| 64 KB |
| Secure-only: a non-secure access returns SLVERR |
Hole H2 |
| 0x1FFE_0000 |
| DECERR |
|
| 4 KB |
| Registers at 0x00 to 0x10; aliased 16 times, see below |
|
| 4 KB |
| SRC, DST, LEN, CTRL, STATUS |
|
| 4 KB |
| |
|
| 4 KB |
| |
|
| 4 KB |
| |
|
| 4 KB |
| RAW, ENABLE, PENDING, TOP |
|
| 4 KB |
| Secure-only |
Hole H3 |
| 36 KB |
| Reserved APB slots: PSLVERR, seen as AXI SLVERR |
Hole H4 |
| 0xBFFF_0000 |
| DECERR |
Decode in nano_xbar: addr[31:14] all zero selects ROM; addr[31:16] equal to 0x2000 selects SRAM0, 0x2001 SRAM1, 0x4000 the APB bridge; anything else reaches the default slave. Inside the APB window nano_apb_dec uses addr[15:12] as the slot number: slots 0 to 6 are mapped, slots 7 to 15 hit the default APB slave. The secure filter checks AxPROT[1], where 0 means secure and 1 means non-secure, at the interconnect and again per APB slot. DECERR is 2'b11 and SLVERR is 2'b10 on RRESP and BRESP. The deliberate alias: the decoder gives each slot 12 address bits but the DMA has only an 8-bit paddr port, so the wrapper passes apb_paddr[7:0] and the 256-byte DMA window repeats 16 times across 0x4000_0000 to 0x4000_0FFF. Software uses only 0x4000_0000 to 0x4000_00FF; offsets 0x14 to 0xFF inside each image still answer PSLVERR.
Domain | Frequency | Period | Blocks |
|---|---|---|---|
| 192 MHz ( | 5.208 ns |
|
| 96 MHz ( | 10.417 ns | Fabric, memories, ROM, DMA, SBR, AXI-to-APB path, system side of both CDC bridges |
| 24 MHz (crystal) | 41.667 ns | UART, SPI, GPIO, INTC, RSTPWR, peripheral side of |
One 24 MHz crystal feeds a PLL with reference divide 1 and feedback multiply 32, so the VCO runs at 768 MHz, inside the 600 to 1200 MHz teaching range. Post-VCO dividers of 4 and 8 give clk_cpu and clk_sys; clk_per is the crystal itself, bypassing the PLL, so peripherals, the reset controller and wake logic keep running while the PLL is off. The lock counter waits 256 clk_per cycles, 10.667 us. The ratio is clk_cpu : clk_sys : clk_per = 8 : 4 : 1. The CPU and system clocks come from one PLL and are phase-related, but signoff treats them as asynchronous: separate roots after the divider, separate trees. Clock gating is glitch-free and latch-based throughout: the CPU clock gates on wfi, and clk_dma and clk_sbr gate on the CLK_EN bits in nano_rstpwr, all enabled by nano_rstpwr.
Four sources feed one controller, nano_rstpwr, which fans out three active-low nets, each asynchronously asserted and synchronously released through a two-flop synchroniser in its own domain. por_n comes from the analog power-on-reset cell and clears everything including the cause register. pad_rst_n comes from the RESET_N pin, debounced over 16 clk_per cycles (666.7 ns), and clears everything except the cause register and SRAM0 contents. sw_rst is a software write to RSTPWR.SWRST with the 0xA5 key, and resets rst_sys_n and rst_cpu_n only, for 16 clk_sys cycles. pwr_rst is the power-up of PD_SW. The rule is slaves and fabric first, masters last: rst_per_n leaves first, then rst_sys_n, and the CPU is the last thing to leave reset, 8 clk_sys plus 2 clk_cpu after rst_sys_n. The hardware sequence from por_n release to the CPU leaving reset takes 53.948 us, dominated by the 1024-cycle oscillator wait and the 256-cycle PLL lock.
One voltage, 0.9 V, so there are no level shifters, only isolation. PD_AON is always on and holds nano_rstpwr, the PLL, nano_intc, nano_uart, nano_spi, nano_gpio, nano_sram0 and nano_rom; the SRAM0 arrays sit in PD_AON while their wrapper logic sits in PD_SW. PD_SW holds everything else: the CPU, both CDC bridges, the fabric, SRAM1, the DMA, the SBR and the APB path. RUN keeps everything on; IDLE gates the CPU clock; SLEEP switches PD_SW off with SRAM0 retained at 0.6 times its leakage. Power-down takes 70 clk_per (2.917 us) in the order isolate, gate the clocks, reset, then power off. Wake takes 14.073 us: switch ramp, power-good, PLL relock, clocks open, isolation released while the resets are still asserted, then rst_sys_n and last rst_cpu_n, with RST_CAUSE[3] set and the ROM jumping to the retained SRAM0 image. Isolation clamps to 0 on all 114 signals from PD_SW into PD_AON: 47 from the apb_cdc peripheral side, 52 on the SRAM0 macro port, 13 on the ROM port, and the two interrupt lines. The area and power figures are NanoSoC teaching values: 0.585 mm2 of blocks, 0.655 mm2 with clock tree, routing and fill, against a 0.940 mm2 budget (69.7% utilisation); RUN 82.74 mW, IDLE 16.89 mW, SLEEP 1.40 mW.
All eight inputs are active-high levels held by the source until software clears them. nano_intc stores nothing: PENDING equals RAW anded with ENABLE, the output irq_cpu is one active-high level to the CPU, and priority is fixed with the lowest number winning. A pulse shorter than 2 clk_per cycles (83.3 ns) can be missed.
IRQ | Name | Source | Cleared by |
|---|---|---|---|
0 |
|
| Write-1-to-clear |
1 |
|
| Write-1-to-clear DONE or ERR in DMA STATUS |
2 |
|
| Write-1-to-clear in SBR STATUS |
3 |
| Transmit-empty or receive-not-empty, each gated by its enable | Read or write DATA |
4 |
| Enable ANDed with receive-not-empty | Read DATA |
5 |
| OR of data inputs ANDed with enables | Remove the pin condition |
6-7 | Reserved | Tied to 0 | Not applicable |
From irq_dma rising to irq_cpu reaching the CPU takes 135.4 ns in the best case and 182.3 ns in the worst, through the two-flop synchroniser into clk_per, the controller register, and the two-flop synchroniser into clk_cpu.
Cold boot takes 12.581 ms from por_n release to the jump to 0x2000_0000, under a blocking model with a cold instruction cache. The hardware reset sequence above is only 0.43% of that. The ROM first reads RST_CAUSE: a wake skips the clear because SRAM0 is retained and holds the image. On a cold boot it sets the stack pointer to 0x2001_0000 and clears 4 KB of SRAM0, then initialises the UART, prints the 16-character banner at 115,384.6 baud (24 MHz divided by 16 times 13), and self-tests the DMA before loading anything: five reads check the reset values, seven writes program the key, the DMA length and start, and the SBR source, destination, length and start, the 64-word job runs in 72 clk_sys (750 ns), and 64 words are read back and compared. The second-stage image is 8 KB at 0x2000_0000, read over SPI at 6 Mb/s with a 4-byte command, then checksummed as 2048 single-word reads. The shares: the SPI load 86.9%, the UART banner 11.0%, everything else 2.1%. The boot lesson replays this table step by step.
The DMA is the UVM course's dma_dut, wrapped as nano_dma with WAIT_STATES set to 1, so one APB access takes 3 clocks. It has no AXI memory master: data enters on AXI-Stream and leaves on AXI-Stream. Its registers live at offsets 0x00 (CTRL: bit 0 GO, bit 1 IRQ_EN; every write copies bit 1 into IRQ_EN, and bit 0 reads back 0), 0x04 (STATUS: bit 0 BUSY read-only, bits 1 and 2 DONE and ERR, each write-1-to-clear), 0x08 (LEN, 16 bits of words to move), 0x0C (KEY, the 32-bit XOR key) and 0x10 (COUNT, words moved, cleared by GO). Any other offset in the 8-bit paddr space answers PSLVERR. After GO with a nonzero LEN, s_tready stays high whenever the one-word output register is free, each accepted word is stored as the input XOR KEY, and the LEN-th accepted word sets m_tlast, clears BUSY and sets DONE on the same edge. An early s_tlast ends the job with ERR instead; a GO with LEN 0 sets DONE at once and moves nothing; a GO while BUSY is ignored without an error; LEN and KEY writes while BUSY are silently ignored; GO clears DONE but never ERR; and the interrupt is a level, enable ANDed with done-or-err, held until software clears it. Because the DMA cannot touch memory itself, NanoSoC adds the stream bridge nano_sbr: an AXI4 master whose read engine turns SRAM words into the DMA's input stream and whose write engine turns the DMA's output stream back into SRAM words. The bridge has its own APB block with SRC_ADDR, DST_ADDR, LEN, CTRL (START, IRQ_EN, and the NS bit that sets AxPROT[1], reset to non-secure) and STATUS (BUSY, DONE, ERR for AXI error responses seen). Software writes one word count to both LEN registers, because the two lengths are separate and a mismatch stalls or errors the job. Known weakness of NanoSoC v1: the SBR register slot is non-secure, so non-secure software can clear NS and reach SRAM1 through the SBR; the partition is not closed until NS is writable only from the secure side. The UVM bench runs the DMA at 100 MHz; inside NanoSoC it runs at clk_sys, 96 MHz. For the block behind the wrapper see The DMA You Will Verify.
If you want | Read |
|---|---|
The integrator's desk: roles, handoffs, milestones | |
Budgets: area, timing, power, bandwidth | |
This address map, designed and checked | Sizes, Alignment and Windows, Holes, Aliases and Reserved Space |
Decode, default slaves and error answers | Generating Decode from a Map Table, DECERR Versus SLVERR Versus PSLVERR |
The secure partition | |
The fabric, the bridges and the burst rules | Shared Bus, Crossbar and Hierarchy, The 4 KB Rule and Burst Splitting |
The PLL, the dividers and the three domains | |
Resets and the release order | |
The ten crossings | |
Power states, isolation and the cold-start line | RUN, IDLE and SLEEP, Where the 114 Clamps Go, One Timeline from POR to First Fetch |
Interrupts, routing and latency | The Wiring Table and the Controller Sketch, From DMA irq to CPU Entry |
The DMA and the stream bridge | Programming the DMA Registers, The Stream Bridge and Length Agreement |
Memories, the boot ROM and this boot flow | |
Security across the chip | |
IP handoff and integration checks | |
Top-level verification and bring-up | |
Package, pads and I/O | |
Signoff, waivers and tapeout | |
Interview preparation | |
The full integration, checked end to end |
Ten integration mistakes are planted in the lab data for the capstone to catch. Their names only, no spoilers:
# | Name |
|---|---|
1 | Address overlap |
2 | Reset release order |
3 | Mis-wired interrupt |
4 | Width and addressing mismatch |
5 | Unsynchronised crossing |
6 | Missing isolation |
7 | Secure attribute mismatch |
8 | Stream length mismatch |
9 | Silently ignored writes while BUSY, and sticky ERR |
10 | Clock gated off during an APB access |
Each one is a real failure with a symptom, a root cause and a fix, and each is guarded by a check the course installs. They all meet again in the Capstone, whose final script prints PASS only when the stage checks and the ten-bug test matrix agree.
The script below rechecks the numbers this page is most quoted for: the burst-16 sustained rate with its shared-bank half, and the boot-time shares. Change an input and see which assertion breaks first.
PEAK = 4 * 96.0 # 32-bit fabric at 96 MHz: 384.0 MB/s
SUSTAINED_16 = PEAK * 16 / 17 # one bubble per burst
assert abs(PEAK - 384.0) < 1e-9
assert abs(SUSTAINED_16 - 361.4) < 0.05
assert abs(SUSTAINED_16 / 2 - 180.7) < 0.05 # shared bank splits the port
BOOT_US = 12580.917
assert abs(10928.0 / BOOT_US - 0.869) < 0.001 # SPI load share
assert abs(1386.667 / BOOT_US - 0.110) < 0.001 # UART banner share
assert abs(53.948 / BOOT_US - 0.00429) < 0.00001 # hardware reset share
print("PASS: fabric 361.4, shared 180.7 each; boot 86.9% SPI, 11.0% banner, 0.43% reset")Each stream drops from 361.4 MB/s to about 180.7 MB/s: one single-port bank serves reads and writes together, so the two streams split the port.