Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions flow/designs/asap7/coralnpu/FRegfile_rf.regfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# coralnpu's floating-point register file, FRegfile_rf, the module
# surgery.py moves FRegfile's words into: 32 x 32 (sign, exponent,
# mantissa from the top), four combinational reads (FRegfile's three
# read ports and its bus port), two writes of which the later
# (FRegfile's write port 0) wins a word both write, every word reset to
# zero while reset is high, a clock gate per word.
module FRegfile_rf
words 32
bits 32
clock clock
async_reset reset high
read raddr0 rdata0
read raddr1 rdata1
read raddr2 rdata2
read raddr3 rdata3
write waddr0 wdata0 we0
write waddr1 wdata1 we1
write_priority last
write_style clock_gate
78 changes: 78 additions & 0 deletions flow/designs/asap7/coralnpu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# coralnpu on asap7

[CoralNPU](https://github.com/google-coral/coralnpu) is Google's open
RISC-V core for edge machine learning: an in-order scalar core that
dispatches up to four instructions a cycle, with a float unit, an ITCM
and a DTCM, and AXI ports. The top is `CoreMiniAxi`.

Its memories are generated: the five TCM banks as SRAM macros with
their byte write masks (`AUTO_MEMORIES`), and the integer and
floating-point register files as standard cells by OpenROAD's
`generate_ram` (`AUTO_MEMORIES_REGFILES`), every word with its
asynchronous reset. What is left as flip-flops is a handful of queues
of two or three entries.

## Results

The whole flow at a 1,900 ps clock, in about 90 minutes:

| stage | worst setup slack | achieved period |
|---|---|---|
| CTS | +4.6 ps | 1,895 ps |
| global route | +6.2 ps | 1,894 ps |
| final, extracted parasitics | -108 ps | 2,008 ps |

Standard cells take 19,411 µm² and the five SRAM macros 13,965 µm².
Detailed route ends with no DRC errors. CTS takes 2.7 minutes and
global route 9.1; detailed route takes an hour.

The worst path at the end runs from the debug module's request queue
through the CSRs, the float core and the load/store unit to the
retirement buffer, in and out of the kept units.
The generated register files' write enables close with 36 ps to spare.

## Where the RTL comes from

`../../src/coralnpu/CoreMiniAxi.sv` is generated from coralnpu's source
at the commit pinned in `../../src/coralnpu/generate/MODULE.bazel`, by
Bazel from that directory:

cd flow/designs/src/coralnpu/generate
bazelisk run //:update

`generate/CoreMiniAxiGenerator.scala` elaborates coralnpu's `CoreAxi` with the
parameters of coralnpu's own `CoreMiniAxi` build (128-bit fetch and
load/store, float with Zfbfmin), in a wrapper that ties the test enable
`te` to 0, so synthesis removes the test-mode reset bypasses and clock
gate overrides. firtool runs without the verification layers and
without register randomization. To move to a newer coralnpu, change the
commit and its `sha256` in `MODULE.bazel` and run `//:update`.

## Memories

- The ITCM (one bank) and DTCM (four banks) are coralnpu's `Sram`,
512 x 128, one read-write port and a 16-lane byte mask.
`sram.memories` names its pins (`ADDITIONAL_MEMORIES`), and
`AUTO_MEMORIES` replaces it with a generated macro.
- `Regfile` and `FRegfile` keep their words as separate registers, with
no array for yosys to infer. `surgery.py` (`SYNTH_VERILOG_SURGERY`)
moves them into modules of their own, `Regfile_rf` and `FRegfile_rf`,
and `generate_ram` builds those from `Regfile_rf.regfile` and
`FRegfile_rf.regfile`. The integer register file has nine
combinational reads (eight read ports and the debug port) and six
writes; the floating-point one four reads and two writes.
- The largest array left is a ROM, the divider's SRT bound table
(128 x 83), which yosys maps to logic. `SYNTH_MEMORY_MAX_BITS` counts
ROMs today and stops synthesis on it; the design builds once a ROM
is no longer counted, a separate change.

## Surgery

`surgery.py` recognises each register file token for token, comments
and whitespace aside, and anything else stops the build. It moves
storage and changes no function: each register file before and after
the surgery was simulated side by side on the same random inputs,
every output compared every cycle (200,000 cycles, resets included,
read and write addresses biased to collide), and a deliberate error in
the moved storage, a write port's mask dropped or the write priority
swapped, is caught within 20 cycles.
28 changes: 28 additions & 0 deletions flow/designs/asap7/coralnpu/Regfile_rf.regfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# coralnpu's integer register file as standard cells, inlined in its
# place: Regfile_rf, the module surgery.py moves Regfile's storage into.
# 32 x 32 with x0 reading zero, nine combinational reads (the eight read
# ports and the debug port), six writes ORed when two write one word
# (Regfile's own data_N), a clock gate per word, every word reset to
# zero while reset is high.
module Regfile_rf
words 32
bits 32
clock clock
async_reset reset high
read raddr0 rdata0
read raddr1 rdata1
read raddr2 rdata2
read raddr3 rdata3
read raddr4 rdata4
read raddr5 rdata5
read raddr6 rdata6
read raddr7 rdata7
read raddr8 rdata8
write waddr0 wdata0 we0
write waddr1 wdata1 we1
write waddr2 wdata2 we2
write waddr3 wdata3 we3
write waddr4 wdata4 we4
write waddr5 wdata5 we5
zero_word 0
write_style clock_gate
33 changes: 23 additions & 10 deletions flow/designs/asap7/coralnpu/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,39 @@ export PLATFORM = asap7
export DESIGN_NICKNAME = coralnpu
export DESIGN_NAME = CoreMiniAxi

export VERILOG_FILES = $(DESIGN_HOME)/src/$(DESIGN_NICKNAME)/$(DESIGN_NAME).sv \
$(PLATFORM_DIR)/verilog/fakeram_512x128.sv \
$(PLATFORM_DIR)/verilog/fakeram_2048x128.sv
export VERILOG_FILES = $(DESIGN_HOME)/src/$(DESIGN_NICKNAME)/$(DESIGN_NAME).sv

export VERILOG_DEFINES += -D USE_ASAP7
# The integer and floating-point register files' words moved into
# modules of their own, Regfile_rf and FRegfile_rf.
export SYNTH_VERILOG_SURGERY = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/surgery.py

export ADDITIONAL_LEFS = $(PLATFORM_DIR)/lef/fakeram_512x128.lef \
$(PLATFORM_DIR)/lef/fakeram_2048x128.lef
# The five TCM banks (ITCM one, DTCM four) are coralnpu's Sram, 512 x
# 128, an array with one read-write port and a 16-lane byte mask;
# sram.memories names its pins, and AUTO_MEMORIES replaces it with a
# generated macro, mask and all.
export AUTO_MEMORIES = 1
export ADDITIONAL_MEMORIES = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/sram.memories

export ADDITIONAL_LIBS += $(PLATFORM_DIR)/lib/NLDM/fakeram_512x128.lib \
$(PLATFORM_DIR)/lib/NLDM/fakeram_2048x128.lib
# The integer and floating-point register files, moved by surgery.py
# into Regfile_rf and FRegfile_rf, built by generate_ram as standard
# cells inlined in their place.
export AUTO_MEMORIES_REGFILES = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/Regfile_rf.regfile \
$(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/FRegfile_rf.regfile

export SDC_FILE = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/constraint.sdc

export SYNTH_HDL_FRONTEND ?= slang

export CORE_UTILIZATION = 65

# coralnpu's units, kept by name: the core, its fetch, dispatch,
# register files, load/store unit, CSRs and FPU, and the two AXI
# bridges. The ALUs, branch units, multiplier and divider are small and
# belong with the dispatch and writeback around them. The size
# threshold is out of reach so that the list alone decides.
export SYNTH_HIERARCHICAL = 1
export SYNTH_MINIMUM_KEEP_SIZE = 40000
export SYNTH_KEEP_MODULES = SCore UncachedFetch DispatchV2 Regfile FRegfile LsuV3 Csr FloatCore AxiSlave DBus2AxiV2
export SYNTH_MINIMUM_KEEP_SIZE = 1000000000
export OPENROAD_HIERARCHICAL = 1

export RTLMP_MIN_CHANNEL_SIZE = 4 4

17 changes: 14 additions & 3 deletions flow/designs/asap7/coralnpu/constraint.sdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@ current_design CoreMiniAxi

set clk_name io_aclk
set clk_port_name io_aclk
set clk_period 10.00
# The period the flow closes at global route, with timing repair.
set clk_period 1900

set clk_port [get_ports $clk_port_name]
# IO budgets as optimization targets, the platform's set_max_delay
# model: 0.8 of the period for a path with a register at one end, 0.6
# for a combinational path straight through.
set in2reg_max [expr { $clk_period * 0.8 }]
set reg2out_max [expr { $clk_period * 0.8 }]
set in2out_max [expr { $clk_period * 0.6 }]

create_clock -period $clk_period -name $clk_name $clk_port
# io_aresetn is asynchronous and reaches the core through RstSync's
# synchroniser; its recovery and removal are not what sets the core's
# frequency.
set_false_path -from [get_ports io_aresetn]

source $::env(PLATFORM_DIR)/constraints.sdc
66 changes: 66 additions & 0 deletions flow/designs/asap7/coralnpu/sram.memories
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"version": 1,
"memories": [
{
"name": "Sram",
"rows": 512,
"bits": 128,
"addr_w": 9,
"rw_ports": 1,
"mask_lanes": 16,
"idiomatic": true,
"reason": "the TCMs' SRAM, NUM_ENTRIES=512: one read-write port, byte mask",
"pins": [
{
"name": "clock",
"direction": "input",
"width": 1,
"port_id": "RW0",
"function": "clk"
},
{
"name": "enable",
"direction": "input",
"width": 1,
"port_id": "RW0",
"function": "en"
},
{
"name": "write",
"direction": "input",
"width": 1,
"port_id": "RW0",
"function": "wmode"
},
{
"name": "addr",
"direction": "input",
"width": 9,
"port_id": "RW0",
"function": "addr"
},
{
"name": "wdata",
"direction": "input",
"width": 128,
"port_id": "RW0",
"function": "data_in"
},
{
"name": "wmask",
"direction": "input",
"width": 16,
"port_id": "RW0",
"function": "mask"
},
{
"name": "rdata",
"direction": "output",
"width": 128,
"port_id": "RW0",
"function": "data_out"
}
]
}
]
}
Loading