xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine
commit a2fa577fb0c3bb5a176b4668f71d69a28eb2cc2b
parent 51a64e9e0dfe25c9eb116a39a1364e21e0996635
Author: Pranav Prabhu Kumble <kumblepranavprabhu@gmail.com>
Date:   Tue, 24 Mar 2026 13:25:42 +0530

Week 1: Add System Specifications (Part 3: Finalizing)

Diffstat:
Mdocs/specifications.md | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+), 0 deletions(-)

diff --git a/docs/specifications.md b/docs/specifications.md @@ -351,3 +351,108 @@ The following are explicitly not part of this project: | Property | Value | | ---------- | --------------------------------------------------------------------------------------------- | | Framing | 8-N-1 | +| Baud rate | Parameterised; default 115200 at 100 MHz (`CLKS_PER_BIT = 868`) | +| Data order | LSB first (standard UART) | +| Interface | `tx_data [7:0]`, `tx_start` (one-cycle pulse), `tx_busy` (high during Tx), `tx` (serial line) | + +The transmitter implements a 4-state Moore FSM (`IDLE → START_BIT → DATA_BITS → STOP_BIT`). The serial line idles high. `tx_busy` is asserted for the entire duration of a frame (start + 8 data + stop = 10 × `CLKS_PER_BIT` clock cycles). The caller must poll `tx_busy` before asserting the next `tx_start`. + +--- + +### 13.2 Input FIFO (`uart_rx_fifo.v`) + +| Property | Value | +| ---------------- | -------------------------------------------------------------------------- | +| Depth | 16 bytes (parameterisable via `DEPTH_LOG2`) | +| Width | 8 bits | +| RAM type | Distributed RAM (SRL16 on Xilinx 7-series) | +| Clock domains | Single-clock; no grey-code synchronisation required | +| Overflow policy | Silently drops incoming byte when `full` is asserted | +| Underflow policy | `rd_data` is undefined when `empty` is asserted; caller must check `empty` | + +The FIFO replaces the previous `rx_latched_data` / `rx_pending` single-byte latch. With up to a 16-byte buffer, the NFA control FSM can spend several cycles on the end-of-string / UART-TX sequence without risking byte loss for any input string up to the FIFO depth. + +--- + +### 13.3 Updated `top_fpga.v` + +#### 13.3.1 Instantiation hierarchy + +``` +top_fpga +├── uart_rx (UART byte receiver) +├── uart_rx_fifo (16-byte input FIFO) ← NEW +├── top (parallel NFA engine) +├── uart_tx (UART byte transmitter) ← NEW +└── [control FSM + TX serializer FSM] ← UPDATED +``` + +#### 13.3.2 Main control FSM + +The main FSM has 12 states (previously 6). The key additions are: + +- **`S_FETCH` / `S_DECODE`**: Read from the FIFO rather than from a latch; classify the byte as a printable character, a line terminator, or a `?` query. +- **`S_EOL_LATCH`**: After capturing `match_bus`, update all `match_count[k]` registers and snapshot `byte_count` for inclusion in the TX packet. +- **`S_TX_ARM`**: Invoke the `build_response` task to format the ASCII response into `tx_buf[0..tx_len-1]` and pulse `tx_send`. +- **`S_TX_WAIT`**: Spin until the TX drain sub-FSM completes (`tx_state == TX_IDLE`). +- **`S_QUERY_TX`**: Service a `?` byte from the host — build and send a counter snapshot immediately without modifying NFA state. + +#### 13.3.3 TX drain sub-FSM + +A 4-state FSM (`TX_IDLE → TX_LOAD → TX_WAIT → TX_NEXT`) drains `tx_buf` byte-by-byte through `uart_tx`. It runs concurrently with the main FSM; the main FSM simply waits in `S_TX_WAIT`. + +#### 13.3.4 `build_response` task + +A synthesisable Verilog `task` that takes the match bitmask and byte counter as inputs and writes a fixed-format ASCII response string into the `tx_buf` register array. The task executes combinationally within the `always` block and completes in a single clock cycle (no loops remain in the synthesised netlist — they are unrolled at elaboration time). + +#### 13.3.5 Hardware counters + +```verilog +reg [31:0] byte_count; // total NFA input bytes since reset +reg [15:0] match_count [0:15]; // per-regex cumulative match events +``` + +Both are synchronously reset by `rst_btn`. `match_count[k]` is incremented on every newline event where `match_bus[k]` is asserted. + +--- + +### 13.4 Serial Protocol + +#### Host → FPGA + +| Byte(s) | Meaning | +| ------------------------------- | -------------------------------------------------------------- | +| Any printable ASCII (0x20–0x7E) | Feed character to NFA engine | +| `\n` (0x0A) or `\r` (0x0D) | Assert `end_of_str`; trigger match + response | +| `?` (0x3F) | Query current counters; response sent without NFA state change | + +#### FPGA → Host + +One ASCII line per `\n` / `?` event: + +``` +MATCH=<N bits, MSB first> BYTES=<8 hex> HITS=<4 hex per regex, comma-sep>\r\n +``` + +--- + +### 13.5 Python TUI (`tui.py`) + +| Aspect | Detail | +| -------------- | ----------------------------------------------------------------------------- | +| Dependencies | `pyserial`, `rich` | +| Connection | Auto-detects first USB-Serial port; overridable with `--port` | +| Display | Live Rich table: regex index, pattern string, MATCH/NO-MATCH, cumulative hits | +| Background I/O | UART reads on a daemon thread; UI never blocks on serial | +| Commands | Plain string → send to FPGA; `?` → counter query; `q` / Ctrl-C → exit | + +--- + +### 13.6 Timing Budget + +At 100 MHz, one complete string-processing cycle from `\n` receipt to TX-complete takes: + +- **NFA end-of-string sequence:** 3 clock cycles (S_EOL_END / S_EOL_MATCH / S_EOL_LATCH) +- **TX packet transmission:** ≈ 95 bytes × 10 bit-times × 868 cycles/bit ≈ 825,000 clock cycles ≈ **8.25 ms** + +This is dominated by the 115200 baud UART and is entirely acceptable for an interactive workload. The NFA engine itself remains line-rate capable; the UART is the limiting factor only at the host interface boundary.