Skip to content

Commit 17ade36

Browse files
oharboeclaude
andcommitted
asap7/coralnpu: a README, with the surgery an exercise left to the reader
What the four coralnpu designs build and their results at global route; where the RTL comes from and what is missing (a recipe that regenerates it from a pinned upstream commit); which surgery closes the gap between upstream and an idiomatic ORFS build, and which stands in for a fresh snapshot; the EX/MEM register with a diagram, what is public about how the silicon met the same cycle, and what is not solved here. Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent ec346c1 commit 17ade36

1 file changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# coralnpu on asap7
2+
3+
[CoralNPU](https://github.com/google-coral/coralnpu) is Google's open
4+
RISC-V core for edge machine learning: an in-order scalar core that
5+
dispatches up to four instructions a cycle, with a float unit, two TCM
6+
SRAMs and AXI ports. `../../src/coralnpu/CoreMiniAxi.sv` is firtool's
7+
output for `CoreMiniAxi`, every file of the build concatenated.
8+
9+
| design | what it builds |
10+
|---|---|
11+
| `coralnpu` | the snapshot as a synthesis build |
12+
| `coralnpu-regfile` | the integer register file generated as an array |
13+
| `coralnpu-exmem` | an EX/MEM register in front of the load/store unit |
14+
| `coralnpu-regfile-exmem` | both |
15+
16+
## Results at global route
17+
18+
OpenROAD's global route with timing repair, built with bazel-orfs.
19+
Each design's clock sits just under what it reaches, so repair has a
20+
target and the WNS is slightly negative.
21+
22+
| design | clock | period at global route | std-cell area | CTS + global route |
23+
|---|---|---|---|---|
24+
| `coralnpu` | 1,900 ps | 2,031 ps | 16,757 µm² | about 8,700 s |
25+
| `coralnpu-regfile` | 1,900 ps | 1,902 ps | 15,628 µm² | 836 s |
26+
| `coralnpu-exmem` | 1,700 ps | 1,701 ps | 16,041 µm² | 1,172 s |
27+
| `coralnpu-regfile-exmem` | 1,650 ps | not yet measured; meets 1,700 ps | 15,565 µm² at 1,700 ps | 642 s at 1,700 ps |
28+
29+
In `coralnpu`, nearly all of the CTS and global route time is
30+
`repair_timing` chasing the one family of paths described below.
31+
With the EX/MEM register, the worst path is the register file into the
32+
CSR's `minstret` counter, a path upstream has also worked on
33+
([8baac41](https://github.com/google-coral/coralnpu/commit/8baac41897c40c6eafdbaabe491117dc60c6175e),
34+
"Fix critical path in CSR minstret register"); the snapshot does not
35+
record whether it predates that change.
36+
37+
## Where the RTL comes from, and what is missing
38+
39+
The snapshot records neither the coralnpu commit it was generated from
40+
nor how. It should: a `MODULE.bazel` here that builds the Verilog from a
41+
pinned coralnpu commit, with the output checked in, so that testing
42+
coralnpu for ORFS and OpenROAD needs neither Bazel nor Chisel, and a
43+
fresh copy is one command. Until then the snapshot stands, and upstream
44+
has moved on from it (below).
45+
46+
`surgery.py` (`SYNTH_VERILOG_SURGERY`) edits the snapshot before
47+
synthesis. Some of it is the gap between what upstream ships and what an
48+
idiomatic ORFS build needs, which upstream is not expected to close:
49+
50+
- firtool's verification layers removed, as a build without layers
51+
enabled would leave them;
52+
- the two TCM SRAMs moved into memory modules that `AUTO_MEMORIES`
53+
generates, write masks and all;
54+
- `--regfile`: the integer register file's 31 words moved into a module
55+
that `AUTO_MEMORIES_REGFILES` generates.
56+
57+
`--address-register` is not that. It is a placeholder for a fresh
58+
snapshot from upstream.
59+
60+
## Surgery: an exercise left to the reader, solved
61+
62+
In the snapshot, one execute cycle reads the register file, forwards an
63+
ALU result through it, lets dispatch decide which lanes issue, and has
64+
the load/store unit build its micro-ops and enqueue them in its input
65+
queue (`FifoX`, eight slots filled in one cycle). On asap7 this one
66+
family of paths sets the period, 2,031 ps at global route.
67+
68+
The classic pipeline puts a register here: the EX/MEM latch of Hennessy
69+
and Patterson's five-stage pipeline, which holds the effective address,
70+
the store data and the request between execute and the memory side
71+
(Rocket and CV32E40P do the same). `surgery.py --address-register` adds
72+
it on `LsuV1`'s inputs:
73+
74+
```mermaid
75+
flowchart LR
76+
subgraph snapshot["snapshot: one cycle"]
77+
direction LR
78+
RF1["register file<br/>read + forward"] --> D1["dispatch<br/>lane issue"] --> L1["LSU: micro-ops"] --> Q1["FifoX<br/>enqueue"]
79+
end
80+
subgraph surgered["coralnpu-exmem: two cycles"]
81+
direction LR
82+
RF2["register file<br/>read + forward"] --> D2["dispatch<br/>lane issue"] --> R[["EX/MEM register<br/>request, address, data"]] --> L2["LSU: micro-ops"] --> Q2["FifoX<br/>enqueue"]
83+
Q2 -. "count + in flight" .-> RDY["io_req_N_ready"]
84+
R -. "in flight" .-> RDY
85+
R -. "busy" .-> ACT["io_active"]
86+
end
87+
```
88+
89+
- `io_req_N_ready` keeps the snapshot's thresholds, applied to the
90+
queue's count plus the entries the register will enqueue, so a request
91+
accepted now has its room a cycle later.
92+
- `io_active` includes the register, so a fence waits for it.
93+
- Loads and stores take one cycle longer. Nothing else changes.
94+
95+
bazel-orfs's `test/coralnpu_exmem` simulates both load/store units
96+
under the same random requests, up to four lanes a cycle with the buses
97+
stalling at random, and compares every bus transaction, writeback,
98+
flush and fault in order; a negative control without the ready
99+
adjustment must differ, and does. It also checks that the surgery
100+
changes `LsuV1` and no other module.
101+
102+
### What the silicon did
103+
104+
CoralNPU is in silicon: Synaptics' Astra SL2610 is its first production
105+
implementation, and VeriSilicon, Google's commercial partner for the
106+
IP, has built test silicon. Whoever implemented it met this cycle, in
107+
one of three ways: a register as above, less work per cycle (fewer
108+
lanes, a smaller configuration), or a slower clock. We probably cannot
109+
know which. What is public:
110+
111+
- the SL2610 datasheet rates the NPU at "up to 1 TOPS" and gives no
112+
clock for the CoralNPU core;
113+
- Google states targets of 1 GHz (22 nm) and VeriSilicon 800 MHz
114+
(22 nm), not measured silicon;
115+
- upstream coralnpu has reworked this cycle in Chisel since the
116+
snapshot was taken:
117+
[43d2030](https://github.com/google-coral/coralnpu/commit/43d20300e9968a444eec4756f42135226e4fbc59)
118+
"Drive Regfile busPort.data from unforwarded rdata" takes the
119+
ALU-to-register-file forwarding out of the load/store unit's enqueue
120+
timing, and a series of LSU timing changes follows, against a stated
121+
target of 800 MHz in 12 nm
122+
([03fa31a](https://github.com/google-coral/coralnpu/commit/03fa31aa589fa4bf8616ffcae54b41d301b57e73)).
123+
124+
### What is not solved here
125+
126+
The proper fix is in the source: a patch to coralnpu's Chisel,
127+
Verilog regenerated from it, and coralnpu's own tests run on the
128+
result, or simply a fresh snapshot of an upstream that has done the
129+
work. That is out of scope for this design. The surgery is an
130+
implementation step on a snapshot, verified by the simulation above
131+
and not by coralnpu's own test suite.

0 commit comments

Comments
 (0)