544 Proof

544 : Proof

Design render

How it works

Proof is a fixed-point dot-product engine that estimates how much a meal will raise blood sugar. It runs two modes over one datapath, selected by MODE.

  • Mode A — glycemic load. A single weighted sum over recipe ingredients, GL = sum(grams_i * w_i), where the host precomputes w_i = available_carb_per_gram_i * GI_i / 100 and available carbohydrate is total carbohydrate minus dietary fibre. Needs no per-user data, so it works the moment the chip powers on. The chip also reports the standard per-serving category: low ≤ 10, medium 11–19, high ≥ 20.
  • Mode B — personalised response. A two-layer quantised MLP, h = ReLU((W1·x + b1) >> s1) then y = (W2·h + b2) >> s2, over meal macronutrients and user context. Shift amounts are powers of two, so requantisation costs a shift rather than a second multiplier.

Every neuron of both layers is the same dot product. Only two things differ: where the activations come from, and what happens to the result. That is why this is one machine with a mode bit rather than two designs.

Weights are streamed in, not stored on chip. That began as an area decision — the parameters do not fit alongside the datapath in one tile — but it is also what makes the design personalisable: same silicon, different patient.

x is not buffered on chip either; the host re-streams it for each hidden neuron, which costs 48 bytes instead of 6 and saves 48 flip-flops. At meal timescales bandwidth is free and flip-flops are not. The hidden layer h is buffered, as a rotating shift register, because only the chip can produce it.

Biases need no bias-specific logic anywhere. In layer 1 they are ordinary extra terms. In layer 2 the chip supplies the constants 127 and 1 after the eight hidden values, so a bias is v8·127 + v9·1 — exact to the unit for any 15-bit value.

The chip checks its own safety precondition

The monotonicity property below holds if the streamed weights satisfy W1[j][carb] · W2[j] ≥ 0 for every hidden unit. That is a property of the weights, not of the design — and since the host streams a different weight set per patient, trusting it is not good enough. Refitting an unconstrained network per person violates the condition in 44 of 44 cases measured on CGMacros.

So the chip checks. Both operands already pass through the pins: the first weight byte of hidden neuron j is W1[j][carb], and layer-2 weight byte k is W2[k]. Their signs ride a register that rotates in lockstep with the hidden-activation register, and a disagreement raises UNTRUSTED. A unit is free to oppose carbohydrate twice — negative on both sides is a non-negative product — and a zero weight can never trigger it, which is why a non-zero bit is carried alongside each sign.

Everything saturates

The accumulator clamps instead of wrapping and raises the same sticky UNTRUSTED flag, and so do the output fields. One pin covers both: a numeric overflow and a void guarantee mean the same thing to a caller — do not act on this output — and the host holds the weights, so it can always tell which. This is not only about producing a sensible number: a saturating sum is monotone and a wrapping one is not.

That matters because the design's headline property is

holding every other input fixed, increasing carbohydrate must never decrease the predicted response.

Saturating sums, arithmetic shifts, ReLU and clamps are each monotone, so the composition is too, provided every hidden unit satisfies W1[j][carb] · W2[j] ≥ 0. During development the property held internally but failed at the pins, because the output fields truncated — a one-count rise in carbohydrate could make the reported response fall from 31,293 to −31,209. Truncation wraps. The fields now saturate, and the property survives all the way to what the host reads.

This is not a medical device. It is an educational and research artifact, and every output is an estimate.

How to test

The host drives a byte stream on DATA_IN, qualified by VALID, with each byte tagged by IS_WEIGHT. Do not present a byte while BUSY is high; bytes offered then are ignored, not queued.

A neuron is a shift byte, then its terms, with LAST on the final one:

Mode A / Mode B layer 1   [s|wt] [w0|wt] [a0] [w1|wt] [a1] ... [aN|last]
Mode B layer 2            [s|wt] [v0|wt] [v1|wt] ...      [v9|wt,last]

The shift byte carries the requantisation shift, so the host sends s1 for hidden neurons and s2 for output neurons. In layer 2 there are no activation bytes at all — each weight multiplies the next value the chip supplies: h[0..7], then 127, then 1.

LAST asserted on the shift byte — otherwise a don't-care — means "this is a new inference", which resets the neuron counter and clears the sticky overflow flag. Mode A ignores it, since every Mode A stream is its own inference.

UNTRUSTED marks the result unsafe to act on. DONE marks a neuron complete and its result readable. RD_SEL selects which byte appears on RESULT:

RD_SEL = 0 RD_SEL = 1
Mode A GL[7:0] {category[1:0], GL[13:8]}
Mode B value[7:0] value[15:8]

Eight output pins and a one-bit select give 16 bits, so rather than spend a whole byte on a 2-bit category it is packed above a 14-bit figure. "High" starts at 20, so a 14-bit field is orders of magnitude more than any real meal needs.

rst_n always recovers the chip, including from a truncated, stuck or mis-tagged stream.

The number of inputs is not fixed in silicon — a neuron ends when the host says LAST — and neither is the number of outputs, since the host simply stops streaming. Only the hidden-layer width is structural, being the depth of the h shift register.

There is no host MCU. The protocol is exercised by the cocotb testbench in test/, against both the RTL and the post-layout gate-level netlist, and every functional test is compared bit-exactly against an integer reference model. The gate-level run is done twice: once with no timing, and once with the post-route SDF back-annotated at all three corners, so the netlist is exercised with real cell and interconnect delays. Setup and hold are still checked by static timing analysis alone — the simulator implements no timing checks.

The monotonicity argument above is partly machine-checked rather than only reasoned: formal/ proves the saturating accumulator monotone by k-induction (unbounded), and proves the value the host reads monotone through the whole core for a single-term Mode A inference. Reverting the fix for the truncation defect makes that second proof fail, so the historical bug is reproduced by a solver. Composition across a whole Mode B network is still the hand argument.

VERIFICATION.md records what is verified and what is not; BUGS.md records every defect found, including the ones found in the testbench itself.

External hardware

None.

IO

#InputOutputBidirectional
0DATA_IN[0]RESULT[0]VALID (in)
1DATA_IN[1]RESULT[1]IS_WEIGHT (in)
2DATA_IN[2]RESULT[2]LAST (in)
3DATA_IN[3]RESULT[3]MODE (in)
4DATA_IN[4]RESULT[4]RD_SEL (in)
5DATA_IN[5]RESULT[5]DONE (out)
6DATA_IN[6]RESULT[6]UNTRUSTED (out)
7DATA_IN[7]RESULT[7]BUSY (out)

Chip location

Controller Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Analog Mux Mux Mux Mux Mux Mux Mux Mux tt_um_chip_rom (Chip ROM) tt_um_factory_test (Tiny Tapeout Factory Test) tt_um_ieee_LDO (LDO) tt_um_chip_ieee_analog (IEEE Bandgap Reference) tt_um_snn_voice_calculator_mauro_ciccone (snn-voice-calculator) tt_um_hx2003_delay (4 Channel - 32 Tap Programmable Delay with Delay Locked Loop Calibration) tt_um_adxl362_test (tt_um_adxl362_test) tt_um_larsnit_cfar (1D CA/GO/SO CFAR radar detector) tt_um_abeccari_swsynth (Sine Wave Synthesizer) tt_um_dpi_adexp (AdExp DPI Neuron ) tt_um_140oo041_fpu130 (FPU-130) tt_um_blonghi_uart (uart) tt_um_directsgg_mini_proceo_8bit (Mini 8-bit Processor) tt_um_umaece1982_lfsr (Low-Power LFSR-Based Test Pattern Generator) tt_um_deploy_timer (launch deployment timer) tt_um_urish_simon (Simon Says memory game) tt_um_nimelli_kinematic_wave_engine (Kinematic Wave Engine) tt_um_multi_seg_monitor (Multi Segment Monitor) tt_um_UART_TX (project) tt_um_crc8_lfsr (CRC-8 Serial LFSR) tt_um_tinynpu4 (TinyNPU4) tt_um_alu_bns (6-bit multi function ALU ( eldawly_V2) ) tt_um_echoworld424_tpv (Timing-Prediction Test Vehicle) tt_um_gyro_lockin (Laser Gyro Lock-in Readout Core) tt_um_josue_olivos_sar_adc (4-Bit Charge-Redistribution SAR ADC Controller) tt_um_flower (VGA Flower) tt_um_vperumal_l1_fabric (Scalable Banked L1 Memory Fabric for Edge AI) tt_um_preinception_top (Preinception: Simple Compute Accelerator) tt_um_italu (iTALU: Interactive Testable Arithmetic Logic Unit) tt_um_neuron (4-Input Signed Neuron / Perceptron) tt_um_4tap_mac (4-Tap Signed MAC Unit) tt_um_mac_engine (DSP MAC Engine) tt_um_crypto_led_demo (QAMER CryptoUART: Encrypted UART with LED Status) tt_um_layernorm (LayerNorm) tt_um_ez130_8t_mystery (EZ130 8T Mystery Circuit) tt_um_sent2spi (SENT Receiver with SPI Interface) tt_um_llr_hepiarisc (Hepiarisc with SPI flash) tt_um_rebeccargb_vga_pride (VGA Pride) tt_um_hasi_ising (Oscillator Ising Machine) tt_um_c061618g2 (Circuitli C061618G2) tt_um_tiny_dram_pim (Tiny Dual-Channel DRAM-PIM Controller + PU) tt_um_Tbilisi_CORDIC_Engine (Tbilisi CORDIC Engine) tt_um_rahulmascarenhas_folded_nn (Frozen ternary backbone + loadable head) tt_um_miniMAC (miniMAC_IHP26b) tt_um_rumcajs (IEEE DOORSH) tt_um_sg13g2_mystery (SG13G2 Mystery Circuit) tt_um_ULSR88 (ULSR demo) tt_um_ez130_7t_mystery (EZ130 7T Mystery Circuit) tt_um_tinyopt4 (ieee_tt_tinyopt4) tt_um_vga_example (IEEE VGA Animated Beach) tt_um_hyphen133_drone_detection (IEEE Acoustic Drone Detector) tt_um_nuatlabs_fifo_pwm (Async FIFO with CDC + PWM Peripheral) tt_um_nuatlabs_uart (8N1 UART Transceiver) tt_um_eeg_threshold_detector (IEEE Digital EEG Threshold Event Detector) tt_um_smart_traffic (Smart Traffic Light Controller) tt_um_94442024_mini_cpu (Mini 8-bit Accumulator CPU) tt_um_wokwi_475369131246576641 (IEEE_UPB_TT_1) tt_um_aion (AION) tt_um_rebeccargb_hardware_utf8 (Hardware UTF Encoder/Decoder) tt_um_rebeccargb_universal_decoder (Universal Binary to Segment Decoder) tt_um_rebeccargb_intercal_alu (INTERCAL ALU) tt_um_flappy_bird (IEEE Flappy Bird VGA Game) tt_um_oryan01_alu (ALU CASS PUCV) tt_um_S4xU4 (S4xU4) tt_um_vga_ca (Space CA) tt_um_llr_simplenpu (simple SPI flash streaming NPU) tt_um_pucv_pspwm (3LFCC PS-PWM Modulator) tt_um_yuri_fpga (Tiny FPGA) tt_um_mikailgedik_inverted_inverters (Inverted inverters) tt_um_esauqch_hamming74 (Hamming(7,4) encoder/decoder (IEEE)) tt_um_hackin7_analog_experiments (TinyAnalogExperiments) tt_um_snake (snake game) tt_um_mini_kraken (Kraken IO Subprocessor) tt_um_fabien_pio (AstraPIO) tt_um_chiplab (ChipLab) tt_um_wokwi_475490677474407425 (Tiny_Divider) tt_um_c061618g2tr (Circuitli C061618G2TR) tt_um_catalinlazar_nanopio (nanoPIO) tt_um_catalinlazar_uart_spi_i2c_bridge (UART-SPI-I2C Bridge) tt_um_enzonappi_sent_i2c (SENT to I2C bridge) tt_um_kush1434_proof (Proof) tt_um_schwallsunk_signal_discriminator (Highspeed voltage discriminator) tt_um_tiarinix_ttihp_verilog_template (8-bit educational SAP-style CPU) tt_um_vga_glyph_mode (BOOTCAMP) tt_um_GiulioGirelli_packet_processor (Configurable Low-Latency Match-Action Packet Processor) tt_um_vga_tictactoe (Tic Tac Toe) tt_um_vga_dvd_player (DVD player) tt_um_clea_katseye_rain (KATSEYE) tt_um_romd_uart_hello (UART Hello World) tt_um_vga_snake (CDM PYTHON GAME) tt_um_vga_slot_machine (tt_um_vga_slot_machine) tt_um_jet_seq8b (SEQ8 Programmable Sequencer) tt_um_kibo_leak_inspect (KIBO Leak-Inspection Target Controller (VGA)) tt_um_endless_runner (Endless Runner) tt_um_omega_infinity_kaoru (OMEGA INFINITY KAORU 3D Metal Grid Processor) tt_um_nikleberg_mixer (Mixer) tt_um_lahnb_sgdma (TinyDMA: A Descriptor-Based Dual-PSRAM Memory Mover) tt_um_gstj_lockin (Digital IQ Lock-in (IEEE)) tt_um_benpayne_ps2_decoder (PS/2 Keyboard Decoder for 68k) tt_um_cass_s_ui_neuron_lif (Neurona LIF con Aprendizaje STDP Dinamico (IEEE)) tt_um_vga_glyph_mode_CDM_Matrix (CDM Matrix) tt_um_qd39l_xor_stream (Fixed-ROM XOR Stream Engine) tt_um_conv3x3 (3x3 Clock Rate Streaming Input Convolution Engine) tt_um_mc14500b_soc_extended (MC14500B Extended 1-bit Microcontroller SoC) tt_um_vga_hypno_spiral (tt_um_vga_hypno_spiral) tt_um_mattizen_morse_tree (Morse Tree LED Decoder) tt_um_CDM (Colegio de Muntinlupa DVD-like Display) tt_um_romd_uart_loader (UART SPI RAM Loader) tt_um_TscherterJunior_stapel_geraet (stapel gerät) tt_um_das2225_dna_accel (DNA_Accel) tt_um_tinysoc (TinySoC) tt_um_barrel_shifter (Barrel Shifter) tt_um_approx_mac_coprocessor (Approximate DSP: Time-Multiplexed MAC Coprocessor) tt_um_joesagents_market_split_oracle (Market-split oracle) tt_um_mgpauly1458_ringmeter (Ring oscillator frequency meter) tt_um_pettit_prism_lite (PRISM with Risc-V (TinyQV) SoC) tt_um_workshop_cpu (IEEE Workshop Simple CPU) tt_um_algofoogle_analog_junk (Simple comparator + 2 DACs analog layout in a 1x1 tile) tt_um_lkhanh_cordic (TinyQV SoC (Dual Memory Backend)) tt_um_4x4npu (4x4NPU: Dual-Lane INT4 Neural Accelerator) tt_um_abiaselli_izh_bridge_3x2 (Izhikevich event bridge (4 contexts)) tt_um_fabulous_ihp_26b (Tiny FABulous FPGA) tt_um_zanderivo_voronoi (Four-Metric VGA Nearest-Prototype Visualizer)