194 AdExp DPI Neuron

194 : AdExp DPI Neuron

Design render

🧠 AdExp DPI Neuron Network (tt_um_dpi_adexp)

A digital spiking-neuron network for the IHP SG13G2 shuttle (TTIHP-26b). The active source set emulates Adaptive Exponential (AdEx) integrate-and-fire dynamics with DPI-style synaptic coupling using shifts, adds and subtracts only: project.v, adex_config.v, adex_block.v, adex_pair.v, and adex_network.v contain no multiplier, divider, or lookup table.

The baseline configuration is a population of four blocks forming two excitatory/inhibitory (E/I) pairs with reciprocal inhibition. Each block is a self-contained population primitive: 1 membrane prime, 2 fast-positive units, 3 slow-negative adaptation units, and 3 small slow-period counters. The architecture is compositional: adex_block -> adex_pair -> adex_network.

How it works ⚙️

Arithmetic model (per block, per clock cycle)

All state variables are signed fixed-point. The membrane prime v is Q4.12 (1.0 = 4096), fast units are 10-bit signed, slow units are 12-bit signed. Every term is a shift-scaled constant or a state value; coupling is a binary selection of a constant, never a product.

Prime (membrane) update, no spike:

v' = v - (v >> KV) + fast_drive - slow_drive + Iext - inh + exc

Fast-positive units (drive the upstroke; accumulate while v > VTRIG):

f_i' = f_i - (f_i >> KF_i) + (v > VTRIG ? FINC_i : 0)

Slow-negative units use a 7-bit phase counter $p_i$ and their configured period $KS_i$:

p_i' = (p_i = KS_i - 1) ? 0 : p_i + 1

On a period tick, the unit relaxes toward zero by one eighth of its magnitude, with a minimum one-count change to avoid a quantisation dead zone. Otherwise it holds its value. Every slow unit receives the shared runtime WBUMP_Q on the block's own spike:

w_i' = w_i + (p_i = KS_i - 1 ? relax(w_i) : 0) + (v > VTH ? WBUMP_Q : 0)

On v > VTH the block emits a registered spike and performs a subtractive reset v' = v - VSTEP (classic AdEx spike-and-reset, without the exponential term: the fast units supply the upstroke instead). Drive contributions are

fast_drive = (f0 >> FSH0) + (f1 >> FSH1)
slow_drive = (w0 >> SSH0) + (w1 >> SSH1) + (w2 >> SSH2)

all state updates are saturated to their storage widths. This is the "shift-only AdEx emulation" described in src/implementation_plan.md; the synthesised RTL is definitive for fixed-point and shift semantics.

The three slow units and coprime periods

Each block carries three slow-negative units with distinct integer update periods. KS_i is the number of core cycles between relaxation updates, not a right-shift exponent. With the fixed one-eighth relaxation, the approximate exponential time constant is $8KS_i$ cycles at magnitudes above eight counts; the minimum one-count relaxation completes the return to zero at low magnitudes.

Pair E slow periods in cycles (KS0, KS1, KS2) I slow periods in cycles (KS0, KS1, KS2)
pair 0 (5, 7, 11) (13, 17, 19)
pair 1 (23, 29, 31) (37, 41, 43)
pair 2 (stretch, N_PAIRS=3) (47, 53, 59) (61, 67, 71)

All three slow units bump on the block's own spike (W += WBUMP_Q each). This produces spike-frequency adaptation: the slow units accumulate during a spike train and relax at their independently scheduled periods.

Network structure

  • adex_block — the population primitive above (parameters in the next section).
  • adex_pair — one E block and one I block with reciprocal inhibition: E spikes inhibit I and vice versa (inh_in port, magnitude = runtime INH_AMT_Q, default 512). E and I use different slow-period triples.
  • adex_network — two pairs (baseline, N_PAIRS=2), each pair isolated from the other. Its optional three-pair configuration (N_PAIRS=3) adds an excitatory ring E0 -> E1 -> E2 -> E0. The submitted top wrapper fixes N_PAIRS=2; using the stretch configuration also requires widening its wrapper ports.

Configuration controls

adex_config supplies a reset-defaulted active register bank. SPI writes first update a shadow bank; a separate COMMIT frame transfers every field to the active bank on one core-clock edge. The following controls are runtime configurable.

Group Runtime field Default Meaning
Per neuron VTH_Q 4096 signed 14-bit spike threshold (max +8191; E0 test uses 5120)
Per neuron IEXT_Q 1024 signed 12-bit input-current magnitude (default 1024, tests <=1024)
Global VTRIG_Q 3072 signed 14-bit fast-unit trigger
Global VSTEP_Q 4096 signed 14-bit subtractive reset step
Global FINC0, FINC1 128, 192 unsigned 9-bit fast-unit increments
Global WBUMP_Q 256 unsigned 10-bit bump for each slow unit (default 256, tests <=600)
Global INH_AMT_Q 512 unsigned 12-bit reciprocal-inhibition magnitude (default 512, tests <=256)

The 14-bit V/VTRIG/VSTEP, 12-bit IEXT, and 12-bit INH_AMT field widths are the demonstrated operating ranges (see src/adex_config.v header); the 14-bit signed thresholds are required because the E0 phase-locked test raises VTH_Q to 5120 (13-bit signed caps at +4095), and 12-bit signed IEXT is required for +1024 (11-bit signed caps at +1023).

VINIT_Q, KV, KF0/1, FSH0/1, KS0..2, SSH0..2, SLOW_DECAY_SHIFT, and the optional stretch-ring excitation magnitude remain compile-time constants. Keeping shift counts static avoids variable shifters in the neuron datapath.

Pin map (baseline, N_PAIRS=2)

Pin Direction Function
clk in system clock
rst_n in active-low reset
ena in unused
ui_in[0] in PWM input current, E0
ui_in[1] in PWM input current, I0
ui_in[2] in PWM input current, E1
ui_in[3] in PWM input current, I1
ui_in[7:4] in unused
uio_in[0] in SPI CS_N
uio_in[1] in SPI mode-0 SCLK
uio_in[2] in SPI MOSI
uio_in[7:3] in unused
uo_out[0] out registered E0 spike indicator
uo_out[1] out spike I0
uo_out[2] out spike E1
uo_out[3] out spike I1
uo_out[4] out any-spike aggregate (E0
uo_out[7:5] out tied low
uio_out, uio_oe out tied low; this is a write-only SPI interface

ui_in[7:4], uio_in[7:3], and ena are ignored by the RTL. The four active drive pins must be driven to known binary values. They are sampled once per clock and act as binary current enables; an external source may provide PWM, while a constant high level supplies the configured IEXT_Q every cycle. An unresolved active input can propagate an unknown value through the state update.

How to test 🧪

  1. Reset: hold rst_n low for at least 5 clock cycles, then release.
  2. Drive: set some of ui_in[3:0] high (constant PWM = constant input current). E.g. drive all four high.
  3. Observe: uo_out[0..3] is high after a clock edge when the corresponding block's pre-update v was greater than VTH_Q; it is not edge-detected. uo_out[4] is the OR of those four registered indicators.
  4. Configure (optional): hold CS_N low, send one or more 32-bit MSB-first SPI mode-0 write frames, then send a COMMIT frame. SCLK must be no faster than clk/8; keep CS_N stable for at least two clk cycles before and after each frame.
Frame Bits [31:28] Bits [27:24] Bits [23:20] Bits [19:4] Bits [3:0]
Write 0xA target 0=E0, 1=I0, 2=E1, 3=I1, F=global field ID 16-bit value 0
Commit 0xC 0 0 0 0

Per-neuron fields are 0=VTH_Q, 1=IEXT_Q. Global fields are 0=VTRIG_Q, 1=VSTEP_Q, 2=FINC0, 3=FINC1, 4=WBUMP_Q, and 5=INH_AMT_Q. Unsigned fields use the least-significant 9, 11, or 15 bits of the value field as applicable.

With the default parameters, the intended pin-level checks are:

  • All four blocks eventually spike when all four active drive pins are high.
  • Adaptation: E0's late inter-spike intervals exceed its early inter-spike intervals.
  • Inhibition: E0's firing rate decreases while I0 is driven and recovers after I0 stops.
  • Pair isolation: driving pair 0 does not create a direct input to pair 1 in the baseline configuration.
  • No lock: E0 and I0 do not sustain in-phase spiking under the default drive used by the testbench.

These are behavioural checks, not validated numerical characterisation. The RTL has been re-verified with the full 14-test suite (see Verification scope below).

The automated suite is test/test.py (cocotb, 14 tests: reset state, directed E0 block arithmetic against a Python fixed-point reference, SPI shadow/commit behavior, silence, spiking + aggregate OR, adaptation ratio, tonic f-I response, fast spiking, inhibition suppression, pair isolation, no-lock, phase-locked alternation, bursting pattern, and burst length vs WBUMP). The arithmetic test uses nine directed vectors and exercises period-counter wrap; the SPI test checks that a write has no effect before commit and reaches E0 after commit. Run with make -B in test/. Three tests reach into internal hierarchy (dut.net.pair0.e_block or dut.u_config); at gate level the netlist can flatten that hierarchy, so their internal checks log a warning and return. test_reset_state still checks that the visible outputs are zero in reset.

Verification scope (final pre-tapeout run, 2026-08-20)

  • The active synthesis source list contains the wrapper, configuration bank, block, pair, and network modules; the legacy LUT core is excluded.
  • Toolchain: Icarus Verilog 13.0 (stable) with cocotb 2.0.1 on Python 3.11 (the tt mamba environment), driven by make -B in test/.
  • Cocotb 14/14 PASS at RTL (TESTS=14 PASS=14 FAIL=0 SKIP=0). Measured spike metrics from this run: basic spiking E0=627, I0=376, E1=328, I1=329 over 6000 cycles with the aggregate-OR check clean; adaptation ISI head8=6.5 -> tail100=9.2 (E0 fired 872 times); tonic f-I IEXT=512->241 spikes/ISI 8.28 vs IEXT=1024->424 spikes/ISI 4.71; fast spiking 997 spikes/ISI 2.00 with max high-run 1 (pulse-clean); inhibition E0 alone=437, with-I0=419, after=435 while I0 fired 252; pair isolation drove pair 0 giving [E0,I0,E1,I1]=[315,191,0,0]; no-lock coincidence fraction=0.42 (threshold 0.5); phase-locked alternation E=399/I=400 at ISI (5.00, 5.00) with disjoint alternating one-cycle pulses; bursting 852 spikes/166 bursts, avg burst size 5.1, avg inter-burst gap 73.9 vs intra-burst ISI 4.1; WBUMP sweep avg burst size 5.1 (WBUMP=200) -> 2.0 (WBUMP=600).
  • Three tests (test_reset_state, test_arith_block, test_spi_shadow_commit) access internal hierarchy; at gate level these log a warning and return without checking internals. The cocotb run emits deprecation warnings from the testbench's use of the older units=/binstr/signed_integer APIs under cocotb 2.0.1; they are cosmetic and do not affect any assertion.
  • verilator --lint-only -Wall (Verilator 5.050) on the full five-source set (project.v, adex_config.v, adex_block.v, adex_pair.v, adex_network.v) is warning-clean — zero warnings, exit 0.

RTL area-reduction edits (behaviour-identical, verified by the 14-test suite)

  • Factored the duplicated (spike_now ? wbump_14 : 0) term into one shared wbump_term_14 wire across the three slow-unit accumulators.
  • Narrowed the sat16 helper input from 20-bit to 18-bit (the prime accumulator v_sum is 18-bit; the dropped bits were pure sign-extension).
  • Removed the dead, unreferenced wbump_q16 widen wire, and dropped the unreachable negative branch of slow_relax (the slow units start at 0 and only ever relax toward zero, so their state is provably non-negative). All of these preserve every state the reference model drives; the arithmetic lock test_arith_block still passes and the full five-source set now lints -Wall clean with zero warnings. Base area is roughly 78% of the 2x2 core by local yosys estimate (behaviour-identical to the pre-reduction baseline); final density is settled by the shuttle's OpenROAD place-and-route run, not by further RTL shaving.

Known limitations

  • SPI is write-only and clock-domain limited. There is no MISO/readback path. The implementation synchronises SPI inputs into clk, so it is intended for slow configuration traffic only (SCLK <= clk/8), not a high-speed independent SPI clock.
  • Runtime scope is intentionally lean. Shift counts, slow periods, reset value, and ring-excitation strength remain compile-time to avoid barrel shifters and a larger configuration bank. The N_PAIRS=3 stretch branch reuses E0/I0 runtime controls for pair 2; the submitted wrapper is fixed at N_PAIRS=2.
  • Observability: at gate level only the spike pins are visible; internal v/f/w states are not exposed (the old debug bus is gone).
  • src/adex_neuron_system_tt_lut32.v is the deprecated Q8.7 LUT-based core from the earlier iteration. It contains LUT, multiplication, and division logic, is not in info.yaml's source list, and is not synthesised.

External hardware

N/A. Self-contained digital core; no external components required.

IO

#InputOutputBidirectional
0PWM_E0spike_E0SPI_CS_N
1PWM_I0spike_I0SPI_SCLK
2PWM_E1spike_E1SPI_MOSI
3PWM_I1spike_I1
4any_spike
5
6
7

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)