This project solves Advent of Code Day 7 puzzle using a synthesizable SystemVerilog design. The core architecture exploits FPGA-native parallelism to simulate a "wavefront" of beams falling through a grid, processing the map line-by-line
My initial instinct was to solve this using recursion, hashmaps, and 2D grid searches. However, fitting this onto an FPGA required a shift in thinking to facilitate hardware:
-
Hardware Design Prototype (C++): I wrote a C++ prototype that strictly mimiced hardware constraints. Instead of
std::findor random memory access, I restricted myself to single pass loops and fixed size boolean vectors -
Architecture:
-
O(W) Memory: Instead of storing the full
$H \times W$ grid, I only store the state of the current beam -
Logic: To avoid write conflicts (multiple beams merging), each cell calculates its next state by looking at its neighbors (
$i-1$ ,$i$ ,$i+1$ ) in parallel
-
O(W) Memory: Instead of storing the full
-
Realistic I/O (
top.sv): Physical chips have a limited number of input pins. The core solver requires a 2,048-bit wide input, which is impossible for standard FPGA I/O. I implemented a Serial In Parallel Out (SIPO) top module. It treats the input as a stream of 8-bit characters (similar to UART), separating the communication throughput from the computation. This reduces the I/O requirement to just 10 pins.
In the real world, data arrives sequentially over time. This module acts as a bridge between the external flow of data and the internal logic.
- Serial In: The
rx_validsignal pulses as bytes arrive one by one. The logic captures these bytes into a line buffer. - Parallel Out: When the newline
\narrives, the buffer is full. Thecompute_startsignal pulses high, dumping the entire line into the computation module in a single clock cycle.
This is essentially Day7_laboratories.cpp implemented in SystemVerilog, the key to counting the number of times the beam splits. In software, moving a beam is easy. However, in hardware, having multiple beams attempting to write to the same location may cause write collisions. To resolve this, instead of looking at where the beams will move, we instead calculate the next state while looking at the neighbours.
An advantage of using hardware approach to this challenge is the ability of parallelism, where we are able to do n logic updates and split checks in a single clock cycle for an n width grid input.
Testbench that drives the Verilator model by simulating UART input by reading the raw puzzle input input.txt and bit banging the input signals. Breaking every line of text into individual bytes and toggle rx_valid high or low for each one. From this, we are able to verify that the hardware correctly handles streaming data, pauses, and line termination sequences.
- Language: SystemVerilog
- Simulation: Verilator (C++ Testbench)
- Key Optimisation: The entire row update happens in a single clock cycle, regardless of map width
- Compile the top and component modules, and testbench:
verilator -Wall --cc --exe --build top.sv tachyonSplit.sv top_tb.cpp- Run the simulation:
./obj_dir/Vtop