Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

BNN MNIST — FPGA Inference Engine

Binarized neural network (BNN) for MNIST digit classification on the Tang Nano 9k FPGA (GW1NR-9 C6/I5). Three fully-connected layers: 784 → 320 → 320 → 10 with XNOR-popcount arithmetic and on-chip batch-norm thresholds.

Architecture

Parameter Value
Input 784 × uint8 (28×28 pixel)
FC1 weights 320 × 784 binary (±1)
FC2 weights 320 × 320 binary (±1)
FC3 weights 10 × 320 binary (±1)
Accumulator width 18-bit signed
Parallelism (N_PAR) 16
Neuron groups per layer FC1: 20, FC2: 20, FC3: 1

Resource Utilization (GW1NR-9 C6/I5)

Resource Used Available %
LUT4 5,496 8,640 63%
DFF 2,961 6,480 45%
ALU 1,602 6,480 24%
BSRAM 22 26 84%
DSP 0 10+ 0%

Timing

Metric Value
Clock frequency 66.857 MHz (PLL: 27 MHz × 25 / (7 × 4) / 6)
Max achievable 74.39 MHz
Inference cycles 22,482
Image load cycles 784
Inference latency 336 µs
Max framerate ~2,900 fps
At 74.39 MHz (max path) ~3,200 fps

Cycle breakdown

FC1:  20 groups × (784 + 2) cycles  =  15,720
FC2:  20 groups × (320 + 2) cycles  =   6,440
FC3:   1 group  × (320 + 2) cycles  =     322
                             Total  =  22,482

Per group: 1 clear cycle + N_IN accumulate cycles + 1 batch-norm cycle.

Project Structure

├── hdl/
│   ├── src/           # Verilog RTL
│   │   ├── bnn_top.v        # Top-level inference sequencer
│   │   ├── fc_layer.v       # Single layer engine (FSM + BN)
│   │   ├── acc_bank.v       # Parallel accumulator bank
│   │   ├── weight_bram.v    # $readmemh-initialized weight storage
│   │   ├── bn_thresh_rom.v  # Batch-norm threshold ROM
│   │   ├── img_buf.v        # Input image buffer
│   │   └── mem/             # Weight & threshold .mem files
│   ├── impl/          # FPGA implementation
│   │   ├── tang_nano_9k_top.v  # Board-level wrapper (PLL, buttons, LEDs)
│   │   ├── pll.v              # 27 MHz → 66.857 MHz PLL
│   │   ├── tang_nano_9k.cst   # Pin constraints
│   │   ├── Makefile           # synth → pnr → pack flow
│   │   └── build/             # Build artifacts + bitstream
│   └── test/           # Cocotb testbenches
│       ├── test_sim.py        # Pytest runner
│       ├── tb_bnn_top.py      # Full integration test vs golden data
│       ├── tb_bnn_debug.py    # Per-layer debug testbench
│       ├── tb_fc_layer.py     # Unit test (synthetic tiny config)
│       ├── tb_acc_bank.py     # Unit test (accumulator bank)
│       └── golden/            # Golden reference vectors (10 digits)
├── model/             # Python training & export
│   ├── bnn.py                # BNN model definition
│   ├── train.py              # Training script
│   ├── bnn_mnist.pth         # Trained checkpoint
│   ├── export_weights.py     # Export weights → .mem files
│   ├── export_bn_thresh.py   # Export BN thresholds → bn_thresh.mem
│   └── gen_golden.py         # Generate golden test vectors
└── README.md

Build & Run

Simulation (requires cocotb + Verilator)

uv run pytest hdl/test/test_sim.py -v

FPGA Bitstream (requires oss-cad-suite)

make -f hdl/impl/Makefile all    # synth → pnr → pack
make -f hdl/impl/Makefile prog   # flash via openFPGALoader

Pinout

Signal Pin
clk_27mhz 52
rst_n (S1) 4
start_btn_n (S2) 3
led_n[0] 10
led_n[1] 11
led_n[2] 13
led_n[3] 14
led_n[4] 15
led_n[5] 16

LEDs are active-low. led_n[3:0] shows the predicted digit, led_n[4] flashes on inference done, led_n[5] indicates PLL lock.

Image loading is not yet wired to physical I/O — start_btn_n triggers inference on a pre-loaded (currently zeroed) image buffer.

About

MNIST Classifier Implemented as Binary Neural Network on Tang Nano FPGA

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages