Tiny Tapeout · Sky130 · Digital ASIC · Design Specification

FPU‑130 / numeric coprocessor

An area‑optimized 8‑bit arithmetic unit for a single Sky130 Tiny Tapeout tile, driven over SPI and a parallel full‑duplex byte port from the demo‑board host.

Document Rev 0.4 — bf16 + status byte Supersedes Rev 0.3 (bf16, single CRC) Target ttsky · 130 nm Date 2026-06-22 Status: DRAFT — decisions pending

This revision takes the original four‑page sketch and works it up into a build‑ready specification: a defined number format (bfloat16), a complete pin map, exact SPI and handshake timing at the chosen 40 MHz core / 10 MHz SCK operating point, a full instruction set, a host‑supplied control byte and FPU‑returned status byte, the datapath micro‑architecture, a clean control FSM, the CRC definition, an area budget, and a verification plan. Points where the original document was silent, ambiguous, or self‑contradictory are flagged inline as Design Decisions (a choice has been proposed — please confirm), Corrections (an error in an earlier revision was fixed), or Open Issues (still needs your input).

Revision history 0.1 original sketch · 0.2 expanded draft (E4M3, per‑operand CRC) · 0.3 switched format to bfloat16 and consolidated to a single operand‑region CRC · 0.4 fixed operating point (40 MHz core, 10 MHz SCK, 4× oversampling) and added the host‑to‑FPU control byte + FPU‑to‑host status byte.
SECTION 01

Introduction & Scope

FPU‑130 is a small numeric coprocessor intended to be fabricated as a digital design on a Tiny Tapeout Sky130 shuttle. It receives operands and a command from a host (the demo board's RP2040, or any SPI controller), performs one arithmetic or logic operation, and returns the result. The whole design must fit the severe area budget of a Tiny Tapeout tile while still supporting multiply and divide, so the architecture trades latency for area: complex operations are iterative and take multiple clock cycles rather than being fully combinational.

Goals

Scope of this document

This is an interface and micro‑architecture specification: it defines externally observable behavior (pins, protocols, instruction semantics, timing) and the internal block structure precisely enough to write RTL against. It does not include the Verilog source, the synthesis scripts, the physical floorplan, or the test‑bench code; those are downstream deliverables that must conform to this document.

Glossary

TermMeaning
TileThe unit of silicon area on Tiny Tapeout, ≈160×100 µm, ≈1000 logic gates. Multiple tiles can be purchased for one project.
HostThe controller driving FPU‑130 — normally the RP2040 on the demo board, scripted in MicroPython.
FrameOne fixed‑length unit of serial/parallel transfer (e.g. the 8‑bit instruction frame).
BurstMode in which operands arrive over the parallel port rather than over SPI.
AccumulateMode in which the previous result is used as the first operand.
op1 / op2First and second operand of a binary operation.
SECTION 02

Key Design Decisions

Earlier revisions left several load‑bearing questions unanswered — most importantly what an operand actually is. These are the decisions now locked in; each is also explained where it is used.

Design Decision — D1 · Number format = bfloat16 Operands and results are bfloat16 (1 sign · 8 exponent · 7 mantissa, 16 bits). This gives fp32‑width dynamic range and is a recognized ML format, so host conversion is a plain fp32 truncation. The cost vs. an 8‑bit format: operands are now two bytes, registers are 16‑bit, and the significand multiply is a full 8×8. See §4.
Design Decision — D2 · Iterative datapath A combinational 8×8 significand multiply alone can consume a whole tile. A single shared add/sub core is sequenced over multiple cycles for MUL and DIV. Costs latency (≈4–10 cycles), saves the majority of the area. See §14.
Design Decision — D3 · Pin map aligned to Tiny Tapeout SPI convention Tiny Tapeout's recommended SPI mapping is uio[0]=CS uio[1]=MOSI uio[2]=MISO uio[3]=SCK. The four full‑duplex status signals take uio[4..7]. The dedicated input byte uses ui_in, the result byte uses uo_out. See §6.
Design Decision — D4 · One CRC over the operand region The instruction byte keeps its own CRC‑3/GSM (poly 0x3), validated first. The control byte and all operand bytes are then covered by a single CRC‑8/SMBUS (poly 0x07); the payload length is fixed by the opcode (and the acc bit), so no per‑operand CRC is needed to delimit anything. See §13.
Design Decision — D6 · Operating point = 40 MHz core / 10 MHz SCK The core clocks at 40 MHz; SPI SCK runs at 10 MHz, giving exactly 4× oversampling — the minimum safe ratio. Two board clocks per SCK phase; usable but thin margin against SCK duty‑cycle distortion. SCK is therefore capped at board‑clock ÷ 4. See §7.
Design Decision — D7 · Control byte in, status byte out The host sends an 8‑bit control byte (rounding mode, clear‑ACC, clear‑flags, saturate, 2‑bit tag) as the first byte of the CRC‑protected payload. The FPU returns an 8‑bit status byte (busy, nan, ovf, unf, inexact, err, tag‑echo). See §11.
Open Issue — confirm tile count With bfloat16 the 16‑bit register file and the full 8×8 iterative significand multiply push the estimate (§15) clearly past one tile. Budget 2 tiles — plan a 2×2 footprint for routing headroom. If area must shrink, the cheapest single cut is dropping DIV.
SECTION 03

Target Platform

FPU‑130 targets a Tiny Tapeout Sky130 shuttle, mounted on the TT demo board and exercised by the on‑board RP2040. Tiny Tapeout exposes a fixed, standard interface to every project; the design must use exactly those pins.

Process
Sky130 130 nm, 1.8 V core
Tile budget
≈1000 gates per 160×100 µm tile
Dedicated inputs
8 ui_in[7:0]
Dedicated outputs
8 uo_out[7:0]
Bidirectional
8 uio[7:0] + uio_oe
Housekeeping
clk · rst_n · ena

The 24 user pins divide into three groups of eight: ui_in (input‑only), uo_out (output‑only), and uio (bidirectional, each pin's direction set by the matching bit of uio_oe). A global clk, an active‑low rst_n, and a tile‑enable ena are also provided. The host RP2040 can drive all of these and can read uo_out back, which is exactly the channel FPU‑130 uses to return results.

Correction — C1 · "bidirectional" clarified Rev 0.1 says "SPI used bidirectional pins." To be precise: the uio bus is configurable, but each individual uio wire has a single direction at a time, fixed by uio_oe. FPU‑130 assigns each uio pin a constant direction (e.g. MISO is always an output, MOSI always an input), so no wire is ever driven from both ends. The uio_oe mask is therefore a constant: 0x64 (outputs on bits 2, 5, 6 — see §6).
Open Issue — budget figures from Rev 0.1 The "$400 budget / $100 dev‑kit / ≈1000 gates per tile" numbers are carried over from your draft as project assumptions. Tile and dev‑kit pricing changes per shuttle, so confirm current figures on the Tiny Tapeout pricing page before committing. The gate budget (≈1000/tile) is consistent with current TT documentation.
SECTION 04

Number Format

Every operand and result in FPU‑130 is a bfloat16 value: a 16‑bit floating‑point number with a 1‑bit sign, an 8‑bit exponent (bias 127, identical to fp32), and a 7‑bit mantissa with an implicit leading 1 for normal values. Because the fields match the top half of an IEEE‑754 fp32, a host produces a bf16 simply by truncating (or round‑to‑nearest‑even) the upper 16 bits of an fp32 — conversion is trivial and the dynamic range is the same as fp32 (~10±38).

4.1 Field layout — two bytes

A bf16 value does not fit in one byte, so every operand and result crosses the 8‑bit interface as two bytes. The 8‑bit exponent straddles the byte boundary: 7 of its bits sit in the high byte alongside the sign, and its least‑significant bit sits in the low byte alongside the mantissa. Bytes travel high byte first to stay consistent with MSB‑first SPI.

15141312 111098 7654 3210 S exponent (8) mantissa (7) high byte [15:8] — sent first low byte [7:0] — second 8-bit exponent crosses the byte split: 7 bits high, 1 bit low
Fig 4.1 — bfloat16 layout. Sign[15], exponent[14:7], mantissa[6:0]; transmitted as two bytes, high byte first.
PropertyValueNotes
Exponent bias127stored = actual + 127 (same as fp32)
Largest finite≈ 3.39 × 10³⁸S=0, E=11111110, M=1111111
Smallest normal≈ 1.18 × 10⁻³⁸E=00000001, M=0000000
Precision~2–3 decimal digits8-bit significand (7 stored + implicit 1)
Zero / Inf±0 · ±InfE=0 / E=255, M=0 (fp32-compatible)
NaNE=255, M≠0fp32-compatible quiet/signalling encodings

What it costs in the datapath. bf16's significand is 8 bits (7 stored + the implicit 1), so MUL is a full 8×8 significand multiply and DIV an 8‑bit iterative divide — there is no small‑mantissa shortcut. The exponent path is 8 bits wide and the aligner/normalizer work on 8‑bit significands. All of this is still sequenced through the one shared add/sub core (§12), just wider and a few more cycles than a minifloat would need.

4.2 Why bfloat16

PropertyBenefit
fp32-width exponentHuge dynamic range; rarely overflows/underflows in practice — robust for the host's data.
Top 16 bits of fp32Host conversion is a truncation; no lookup tables or complex packing on either side.
Recognized ML formatReference models and test vectors are widely available, simplifying §17 verification.
8-bit significandMultiply/divide stay 8‑bit — affordable iteratively, though larger than a 4-bit minifloat.
Design Decision — D1 (restated) The document assumes bfloat16 throughout. Only §12 (datapath) and the ALU semantics in §9 depend on the format; the pin map, SPI framing, control/status bytes, handshake, CRC, and FSM are all format‑agnostic and would be unchanged by a different float width.
SECTION 05

Top-Level Architecture

FPU‑130 is five blocks: two transport handlers (SPI and the parallel I/O port), a register file, the iterative datapath, and a control unit that sequences everything. The redrawn block diagram below tightens up the Rev 0.1 sketch and shows the actual signal grouping on the pins.

tt_um_fpu130 uio[3:0] SPI: SCK CS MOSI MISO uio[7:4] in_valid in_ready out_valid out_ready ui_in[7:0] byte in uo_out[7:0] byte out SPI Handler shift reg · CRC3/8 I/O Handler valid/ready handshake · CRC8 Control Unit FSM · decode Register File A · B · ACC Datapath add/sub core mul/div seq · norm clk · rst_n · ena → all blocks (board clock)
Fig 5.1 — Top-level block diagram. Amber = pin groups, slate = control, teal = arithmetic.
BlockResponsibility
SPI HandlerClocks bits in/out on uio[3:0], deserializes 8‑bit frames, checks the 3‑bit (instruction) and 8‑bit (data) CRC, and serializes the result back on MISO.
I/O HandlerRuns the valid/ready handshake on uio[7:4], latches operand bytes from ui_in, and drives result bytes onto uo_out.
Register FileHolds operand A, operand B, and the accumulator (ACC) which doubles as the result register.
DatapathOne shared add/subtract core plus the sequencer logic that turns it into multiply, divide, negate, abs, and compare; includes the float normalizer.
Control UnitThe FSM (§13). Decodes the instruction frame, schedules operand fetches, drives the datapath cycle‑by‑cycle, and routes the result to the requested transport.
SECTION 06

Pin Interface

FPU‑130 uses every group of the standard Tiny Tapeout interface. The dedicated input byte carries burst operands; the dedicated output byte carries results; the bidirectional bus carries SPI on its low nibble and the four flow‑control signals on its high nibble.

FPU‑130 tt_um_fpu130 uio[7:0] — SPI + flow control (bidirectional) 7 out_rdy in 7654 3210 ui_in[7:0] byte in (burst operands) in uo_out[7:0] byte out (results) out clk · rst_n · ena board clock + housekeeping
Fig 6.1 — Pad assignment. Low nibble of uio = SPI, high nibble = handshake.

6.1 Complete pin map

PinDirSignalFunction
uio[7:0] — bidirectional, uio_oe = 0x64
uio[0]inspi_cs_nSPI chip select, active low.
uio[1]inspi_mosiSPI host→FPU data (command & operands).
uio[2]outspi_misoSPI FPU→host data (result read‑back).
uio[3]inspi_sckSPI clock, Mode 0.
uio[4]inin_validHost asserts: input byte on ui_in is valid.
uio[5]outin_readyFPU asserts: ready to accept an input byte.
uio[6]outout_validFPU asserts: result byte on uo_out is valid.
uio[7]inout_readyHost asserts: ready to accept a result byte.
Dedicated buses
ui_in[7:0]inbyte_inParallel operand byte (burst mode).
uo_out[7:0]outbyte_outParallel result byte.
Housekeeping (provided by Tiny Tapeout)
clkinclkSynchronous board clock; clocks the whole core.
rst_ninrst_nActive‑low synchronous reset.
enainenaTile enable; high when this design is selected.
Correction — C2 · MISO restored Rev 0.1 lists the four SPI pins as "Spi_clk, Spi_cs, Spi_mosi, Spi_mosi" — Spi_mosi appears twice. The fourth pin is the FPU‑to‑host line, spi_miso. Corrected above.
Open Issue — MISO idle behavior Because uio_oe is a constant, MISO is always an output even when spi_cs_n is high. It will drive 0 while deselected. If you want true tri‑state on a shared SPI bus, MISO's uio_oe bit must be driven dynamically from ~spi_cs_n — a small change, but it makes the OE mask non‑constant. Confirm which you want.
SECTION 07

SPI Interface

FPU‑130 is an SPI peripheral (slave). The host owns spi_sck and spi_cs_n. The link runs in Mode 0: clock idles low (CPOL=0) and data is sampled on the rising (leading) edge (CPHA=0). All frames are 8 bits, MSB‑first.

CS_n SCK MOSI MISO frame active (low) sample ↑ b7 b6 b5 b4 b3 b2 b1 b0 result byte shifted out on same clocks (read-back)
Fig 7.1 — SPI Mode 0, one 8-bit frame. MOSI sampled on each rising SCK edge, MSB first.

7.1 Frame structure

A transaction begins when the host pulls spi_cs_n low and clocks an instruction byte (its own CRC‑3 is validated first, which fixes how many bytes follow). When operand data travels over SPI (burst = 0), a control byte and the operand bytes follow, all covered by a single trailing CRC‑8. Each bf16 operand is two bytes (high byte first).

Instruction byte — 8 bits (CRC-3 validated first) burst [7] acc [6] opcode [5:3] CRC-3 [2:0] CRC-protected payload — length set by opcode & acc control 1 byte op1 hi op1 lo op2 hi binary only op2 lo binary only CRC-8 over payload single CRC-8 covers control + all operand bytes payload bytes = 1 (control) + (arity − acc) × 2. Unary op (NEG/ABS): control + op1(2) + CRC. acc = 1: op1 is taken from ACC, so only op2 is sent. NOP: control + CRC, no operands.
Fig 7.2 — SPI frame. Instruction byte (own CRC-3), then a control byte + bf16 operands under a single CRC-8.

7.2 Reading the result over SPI

SPI is full‑duplex at the wire level: every byte the host clocks out on MOSI simultaneously clocks a byte in on MISO. While the FPU is busy it returns the status byte (§11) with BUSY=1, so the host polls; once BUSY clears, a read returns the two result bytes (high byte first). Each transaction's status byte echoes the control byte's TAG field for request/response correlation.

7.3 Clocking & oversampling

The core clocks at 40 MHz; SPI SCK runs at 10 MHz. FPU‑130 is an oversampled SPI peripheral: SCK is never used as a clock. The SPI inputs (spi_sck, spi_cs_n, spi_mosi) pass through 2‑flop synchronizers into the core domain; a one‑cycle edge‑detect pulse off the synchronized SCK enables MOSI sampling (rising edge) and MISO update (falling edge).

ParameterValueNotes
Core clock40 MHz (25 ns)clocks the entire design
SCK10 MHz (100 ns)host-driven; sampled, not used as a clock
Oversampling ratiominimum safe ratio; 2 core clocks per SCK phase
Max SCKboard ÷ 4 = 10 MHzat the ceiling — no headroom without a faster core
Input sync latency2 core clocks (+1 edge-detect)consistent, so MOSI sampling stays aligned
Open Issue — oversampling margin 4× gives exactly two core clocks per SCK phase — workable, but SCK duty‑cycle distortion or jitter eats directly into that budget. The RP2040 divides a clean internal clock so its duty is normally tight; if a noisy SCK is a concern, either hold SCK at 8 MHz (5×) or raise the core toward 48–50 MHz if timing closes.
SECTION 08

Parallel Full-Duplex Port

For streaming workloads, operands can be pushed in over the dedicated 8‑bit input bus and results pulled out over the dedicated 8‑bit output bus, each governed by an independent valid/ready handshake. This is the "burst" path (instruction bit 7 = 1). It is faster than SPI because a whole byte moves per clock instead of one bit per SCK edge.

8.1 Handshake rule

A byte transfers on the rising board‑clock edge where both valid and ready are high. Either side may assert its signal first; neither waits on the other to begin asserting. This is the standard AXI‑Stream‑style contract and is deadlock‑free as long as valid never waits for ready.

Correction — C3 · handshake wording Rev 0.1 step 2 reads "Valid asserts ready," which is garbled. The intended rule is: the sender asserts valid while the receiver asserts ready; the transfer happens on the cycle both are high. Steps corrected below.
  1. Receiver raises ready when it can accept a byte.
  2. Sender raises valid and drives the byte onto the bus. (Steps 1–2 may occur in either order.)
  3. On the first clock edge with valid & ready both high, the byte is transferred.
  4. Each side deasserts its signal (or keeps it high to stream the next byte back‑to‑back).
clk valid ready data xfer A xfer B byte A byte B
Fig 8.1 — valid/ready handshake. Transfer occurs only where both are high at a clock edge.

8.2 Burst frame

A burst carries the same CRC‑protected payload as SPI, just moved over the parallel port one byte per handshake: control, then each bf16 operand as two bytes (high byte first), then the single CRC‑8 over the whole payload. Each operand therefore takes two in_valid/in_ready transfers. Results stream back over uo_out as two bytes (high first) with the FPU as sender (out_valid) and the host as receiver (out_ready); the status byte is available on the same path. The instruction byte and its CRC‑3 are always delivered over SPI first — burst only relocates the operand data.

SECTION 09

Instruction Set

The 3‑bit opcode selects one of eight operations, completed with exact semantics, operand count, and result for the bfloat16 format of §4. Operands are a = op1 (or ACC in accumulate mode) and b = op2; rounding follows the RND field of the control byte (§11).

OpcodeMnemonicOperandsResultNotes
000ADDa, ba + bfloat add; align exponents, add, normalize, round‑to‑nearest‑even.
001SUBa, ba − badd with b's sign flipped.
010MULa, ba × badd exponents, multiply significands (4×4), normalize.
011DIVa, ba ÷ bsubtract exponents, iterative significand divide; b=0 → NaN.
100NEGa−aunary; flip sign bit only.
101ABSa|a|unary; clear sign bit.
110SLTa, b(a < b) ? 1.0 : 0.0set‑less‑than; returns a float 1.0 or 0.0 flag.
111NOPACC unchangedno operands, no write; used for status read‑back / padding.
Design Decision — SLT result encoding SLT returns bfloat16 1.0 (0x3F80) for true and +0.0 (0x0000) for false, so the flag can feed straight back into arithmetic in accumulate mode.

9.1 Control byte (host → FPU) & status byte (FPU → host)

Each transaction carries a host‑supplied control byte as the first byte of the CRC‑protected payload (§7.1), and the FPU returns a status byte on read‑back. The two are symmetric and share a 2‑bit TAG that the FPU echoes, letting the host match each result to its request in a pipelined or burst stream.

Control byte (host → FPU) 7:654321:0 TAG rsv SAT CLRF CLRA RND Status byte (FPU → host) 7:6543210 TAG echo ERR INX UNF OVF NAN BUSY
Fig 9.1 — Control byte (in) and status byte (out). TAG is echoed for request/response correlation.
FieldDirMeaning
RND[1:0]inRounding mode: 00 = round‑nearest‑even, 01 = toward zero, 10 = toward +∞, 11 = toward −∞.
CLRAinClear ACC to +0.0 before this op (start a fresh accumulate chain).
CLRFinClear sticky exception flags before this op.
SATinOn overflow, saturate to max‑finite instead of producing ±Inf.
TAG[1:0]in/outHost‑chosen transaction tag; echoed in the status byte.
BUSYoutComputation in progress; result not yet valid.
NAN / OVF / UNFoutResult is NaN / overflowed / underflowed.
INXoutInexact — rounding occurred (sticky until CLRF).
ERRoutLast CRC check failed; the frame was dropped.
Design Decision — D7 (restated) Exact bit assignments above are the proposal; reorder freely. The control byte is always present (even for NOP, where NOP + CLRA or NOP + CLRF becomes a useful control‑only operation, and a bare NOP is a status read).
SECTION 10

Control Modes

The two mode bits in the instruction frame are orthogonal and can be combined freely.

10.1 Burst (instruction bit 7)

burst = 0: operands and result travel over SPI (data frames on MOSI, read‑back on MISO). burst = 1: operands arrive over the parallel ui_in port and the result leaves over uo_out, each under valid/ready flow control (§8). The instruction frame itself is always delivered over SPI regardless of burst — burst only changes where the operand data flows.

10.2 Accumulate (instruction bit 6)

acc = 0: both operands come from the incoming frames. acc = 1: the first operand is taken from the ACC register (the previous result), and only op2 is supplied. This lets the host chain operations — e.g. a running sum — without re‑transmitting the accumulator each time.

burstaccOperand sourceResult destination
00op1 & op2 over SPISPI MISO read‑back
01op1 = ACC; op2 over SPISPI MISO read‑back
10op1 & op2 over ui_inuo_out stream
11op1 = ACC; op2 over ui_inuo_out stream
Correction — C4 · mode definitions made explicit Rev 0.1 defined burst and accumulate in one‑line footnotes only. The table above makes the four combinations and their data routing unambiguous.
SECTION 11

CRC Definition

FPU‑130 uses two layered CRC scopes, both computed MSB‑first with a small LFSR. The instruction byte is protected on its own and validated first — that is what tells the receiver the payload length, so a single CRC over the rest is unambiguously placed.

FieldCoversStandardPolyInit
Instruction CRC‑3burst·acc·opcode (5 bits)CRC‑3/GSM0x30x0
Payload CRC‑8control byte + all operand bytesCRC‑8/SMBUS0x070x00

The payload CRC‑8 spans the control byte plus (arity − acc) × 2 operand bytes — for a binary op that is the control byte and four operand bytes under one CRC, versus the two separate CRCs an earlier draft would have used. The LFSR is reset at the start of the payload, clocked through every payload byte, and checked once against the trailing byte. By CRC linearity an 8‑bit CRC over the whole block keeps the same ~1/256 random false‑accept and still catches errors that straddle the op1/op2 boundary; the only thing given up is per‑operand fault localization, which costs nothing because recovery resends the whole frame anyway.

On any mismatch the FPU sets the ERR status bit and drops the frame rather than computing on corrupt data; the host re‑sends. Both CRCs together are a few XOR gates and two short shift registers — negligible area for the integrity.

Design Decision — D4 (restated) Keeping the instruction CRC‑3 separate and validated first is load‑bearing: the receiver must trust the arity before it can know where the payload CRC lands. Folding everything into one end‑of‑transaction CRC would mean mis‑counting payload bytes off a corrupt opcode. CRC‑3/GSM and CRC‑8/SMBUS have published reference vectors; swap polynomials freely — the hardware cost is identical.
SECTION 12

Datapath Micro-architecture

This is the only section that depends on the number‑format choice. The datapath is deliberately narrow and reused: one shared add/subtract core does the heavy lifting for every instruction, sequenced over several clocks by the control unit (Decision D2). For bfloat16 the core and the unpacked operands are 8‑bit significands (7 stored + the implicit 1) with an 8‑bit exponent path; MUL is a full 8×8 significand multiply and DIV an 8‑bit iterative divide, both shift‑sequenced through the one core.

A reg op1 / ACC B reg op2 Unpack split S/E/M add implicit 1 Exponent unit ±, align shift Shared add/sub significand core + shift register Mul/Div seq shift-add / shift-sub Normalize leading-1 detect round (RNE) Pack S/E/M → byte ACC / result → transports result feeds back to A reg (accumulate)
Fig 12.1 — Datapath. The single teal add/sub core is reused by the sequencer for MUL and DIV.

12.1 How each op uses the core

OpMechanism≈ cycles
ADD / SUBCompare exponents, shift the smaller significand to align, add/subtract in the core, normalize, round.3–4
MULAdd exponents (exponent unit); multiply 8‑bit significands by shift‑add through the core; normalize, round.6–8
DIVSubtract exponents; non‑restoring division of significands by shift‑subtract through the core; normalize, round.6–10
NEG / ABSPure bit manipulation of the sign field; no core pass.1
SLTSign check, then compare exponent/mantissa magnitudes; emit 1.0 / 0.0.1–2

Latency is variable, which is exactly why the FPU exposes a busy status: the host either polls the status byte or watches out_valid, rather than assuming a fixed delay.

SECTION 13

Control Unit FSM

The Rev 0.1 state diagram was a dense, partly‑overlapping sketch. Redrawn as a single clean flow, the control unit is a straightforward request→decode→fetch→execute→writeback→return loop. The same FSM serves both transports; only the fetch and return states branch on the burst bit.

RESET IDLE wait CS↓ RX_INSTR shift 8 · CRC3 DECODE RX PAYLOAD control + operands SPI or burst · CRC-8 EXECUTE assert busy · N cycles WRITEBACK store ACC RETURN MISO or out_valid NOP / no op2 done → IDLE CRC fail → err, back to IDLE
Fig 13.1 — Control FSM. Amber = transport-dependent states, teal = compute, slate = bookkeeping.
StateActionExit condition
RESETClear ACC, registers, status; deassert all outputs.rst_n high → IDLE
IDLEWait for spi_cs_n to fall.CS asserted → RX_INSTR
RX_INSTRShift 8 SPI bits; verify CRC‑3.CRC ok → DECODE; CRC bad → set err, IDLE
DECODELatch burst/acc/opcode; compute payload length = 1 (control) + (arity − acc) × 2 operand bytes.→ RX PAYLOAD (every op has at least a control byte)
RX PAYLOADClock in the control byte, then each bf16 operand as two bytes (high first) over the selected transport, accumulating one CRC‑8; apply CLRA/CLRF from the control byte.CRC ok → EXECUTE; CRC bad → set ERR, IDLE
EXECUTEAssert BUSY; run the datapath for the op's cycle count with the selected rounding mode.datapath done → WRITEBACK
WRITEBACKStore 16‑bit result into ACC; update status (NAN/OVF/UNF/INX), echo TAG.→ RETURN
RETURNPresent the two result bytes + status on MISO (poll‑read) or stream on uo_out via out_valid.host consumed → IDLE
SECTION 14

Worked Transactions

Two end‑to‑end examples make the protocol concrete.

14.1 SPI multiply, no accumulate (burst=0, acc=0)

  1. Host pulls spi_cs_n low.
  2. Host clocks the instruction byte: burst=0, acc=0, opcode=010 (MUL), CRC3.
  3. FPU verifies CRC‑3, decodes MUL → payload = control + 4 operand bytes + CRC‑8.
  4. Host clocks the payload: control · op1_hi · op1_lo · op2_hi · op2_lo · CRC8 (one CRC over all five data bytes).
  5. FPU verifies CRC‑8, asserts BUSY, runs the multiply (≈6–8 cycles), writes the 16‑bit ACC, sets any NAN/OVF/INX flags.
  6. Host polls: a read returns the status byte (BUSY=1) until done, then the two result bytes (high first); the status byte's TAG echoes the control byte's TAG.
  7. Host raises spi_cs_n.

14.2 Burst accumulate‑add stream (burst=1, acc=1)

  1. Host sends the instruction byte over SPI: burst=1, acc=1, opcode=000 (ADD), then the control byte (e.g. CLRA=1 on the first to zero ACC).
  2. For each value: host drives ui_in + raises in_valid; FPU raises in_ready; the two operand bytes (high first) transfer, then the CRC‑8 byte; FPU checks CRC.
  3. FPU computes ACC ← ACC + op2 per value (running bf16 sum), updating ACC in place.
  4. To read the total, host raises out_ready; FPU raises out_valid and streams the two ACC bytes (high first) plus status.
Open Issue — stream framing For a continuous accumulate stream, confirm how a new instruction interleaves with an in‑flight burst. Recommended: one instruction byte arms the mode and the values stream (each as control?/operands + CRC) until the host deasserts cs_n and re‑issues. Decide whether the control byte repeats per value or is sent once when arming.
SECTION 15

Area & Gate Budget

A first‑order gate estimate (2‑input NAND equivalents, before place‑and‑route overhead). These are planning numbers, not synthesis results; routing and tap/antenna cells typically add 30–50%, which — with the bf16 datapath and 16‑bit registers — puts the design clearly into a 2‑tile budget.

0175350525 estimated gate equivalents (scale: 175 gates ≈ 150 px) Datapath (bf16) ~480 Register file (16b) ~150 Control FSM ~140 SPI handler ~120 I/O handler ~100 Synchronizers ~50 CRC + status/ctrl ~70
Fig 15.1 — Pre-route estimate ≈ 1110 gates. With ~40% P&R overhead ≈ 1550 — comfortably a 2-tile (2×2) design.
BlockEst. gatesDominated by
Datapath (bf16)~4808‑bit add/sub core, 8×8 iterative mul, 8‑bit exponent path, normalizer
Register file~150A, B, ACC (3 × 16 bits)
Control FSM~140state register, decode, payload/byte counters, control-byte handling
SPI handler~120byte receiver, bit pointer, MISO mux
I/O handler~100handshake logic, 2‑byte operand assembly
Synchronizers~50SPI CDC flops + edge detect
CRC + status/control~70CRC‑3 + CRC‑8 LFSRs, control decode, status flags
Subtotal~1110before place & route
Design Decision — D5 · target 2 tiles (2×2) ~1110 pre‑route gates plus routing overhead exceed a single tile and a 1×2 leaves little margin. Budget a 2×2 footprint. If area must shrink, the cheapest cut is dropping DIV (removes the divide sequencer); a deeper cut would be a narrower float, which shrinks the registers and the significand multiply.
SECTION 16

Reset, Clocking & Power

Resolved — operating point fixed (D6) Core clk = 40 MHz, SCK = 10 MHz, 4× oversampling. The worst‑case path is expected to be the normalizer's leading‑one detect over the 8‑bit bf16 significand — a short path that should close comfortably at 25 ns, but it must be confirmed against actual synthesis. SCK is capped at clk ÷ 4 = 10 MHz (§7.3).
SECTION 17

Verification Plan

Tiny Tapeout supports cocotb test benches that can later be replayed against the real chip via the demo board. The plan:

LevelWhat it checks
Unit — datapathEach op (ADD/SUB/MUL/DIV/NEG/ABS/SLT) against a golden software bfloat16 reference across a directed corner set (±0, ±Inf, NaN, max finite, smallest normal, div‑by‑0) plus randomized vectors; exercise all four rounding modes.
Unit — CRCCRC‑3/CRC‑8 against published reference vectors; deliberate bit‑flips must raise err.
Protocol — SPIMode‑0 framing, instruction+data sequencing, busy polling, read‑back; bad‑CRC frames dropped.
Protocol — burstvalid/ready handshake including back‑to‑back transfer, stalls (ready low), and result streaming.
Mode matrixAll four burst×acc combinations from §10.
IntegrationFull transactions from §14, then re‑run as hardware‑in‑the‑loop on the demo board.
SECTION 18

Open Issues & Roadmap

Consolidated list of everything flagged above that needs your sign‑off before RTL starts:

IDDecisionStatus / recommendation
D1Number formatResolved: bfloat16 (1·8·7). Drop to a narrower float only if area forces it.
D2Iterative vs combinational datapathResolved: iterative, shared add/sub core.
D5Tile countResolved: 2 tiles (2×2). Confirm against live TT pricing.
D6Operating pointResolved: 40 MHz core / 10 MHz SCK (4× oversampling).
D7Control byte / status byteResolved (confirm bits): control byte in (RND/CLRA/CLRF/SAT/TAG), status byte out (BUSY/NAN/OVF/UNF/INX/ERR/TAG).
O2MISO tri‑state vs always‑drivenOpen — recommend driving 0 when deselected (constant OE).
O4Burst stream framingOpen — recommend one instruction arms the mode until CS toggles; decide if control byte repeats per value.
O5Confirm current TT pricing / 2‑tile costOpen — check the live TT pricing page.

Resolved since Rev 0.2: the format (D1 → bf16), the single operand‑region CRC (D4), the operating point and oversampling margin (was O3, now D6), and the exception/status reporting (was O1, now D7 + §9.1).

Suggested next steps

  1. Confirm the D7 bit assignments (control/status bytes) and O2 (MISO drive) — these freeze the host‑facing interface.
  2. Confirm O4 (burst stream framing) so the streaming case is unambiguous.
  3. Write the cocotb bfloat16 reference model first (it doubles as the spec's executable check), then the RTL; close timing against the 40 MHz target.