xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine
commit 0619ab7bb78d77d88c6f56969f6e2f187b137383
parent 33f6a42c6705eda20ec587eaf00c520288d4c08c
Author: RahulSannapureddy <rahul.sannapureddy@gmail.com>
Date:   Mon, 20 Apr 2026 17:12:51 +0530

Week 6: PII guard documentation, TUI enhancements, and legacy cleanup (Part 1: Initial definitions)

Diffstat:
Ddocs/details.md | 269-------------------------------------------------------------------------------
Adocs/pii_guard.md | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddump.vcd | 2057-------------------------------------------------------------------------------
Aoutput-pii/nfa_0.v | 41+++++++++++++++++++++++++++++++++++++++++
Aoutput-pii/nfa_1.v | 46++++++++++++++++++++++++++++++++++++++++++++++
Aoutput-pii/nfa_2.v | 48++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 241 insertions(+), 2326 deletions(-)

diff --git a/docs/details.md b/docs/details.md @@ -1,269 +0,0 @@ -# Stage 1 — C++ Regex Parser - -This stage is responsible for transforming a raw regular expression string into an Abstract Syntax Tree (AST) that can be processed by the NFA construction stage. - ---- - -## 1. Lexer Details - -### Overview - -The lexer is the first component. Its primary responsibility is to read a raw regular expression string character-by-character and transform it into a sequence of typed tokens. This abstraction simplifies the downstream parsing process by handling character-level concerns like literal character validation and metacharacter identification. - -### Token Types - -The lexer identifies the following token types: - -| Token Type | Representation | Description | -| -------------- | ---------------------------- | --------------------------------------------------- | -| `LITERAL` | Any printable ASCII (32–126) | Represents a literal character match. | -| `DOT` | `.` | Matches any single character. | -| `STAR` | `*` | Kleene star: zero or more repetitions. | -| `PLUS` | `+` | One or more repetitions. | -| `QUESTION` | `?` | Optional: zero or one occurrence. | -| `PIPE` | `\|` | Union / Alternation operator. | -| `LPAREN` | `(` | Start of a subexpression group. | -| `RPAREN` | `)` | End of a subexpression group. | -| `END_OF_INPUT` | `\0` | Sentinel token marking the end of the regex string. | - -### Input Processing Rules - -1. **Character Filtering:** Only printable ASCII characters (codes 32–126) are allowed. Non-printable characters are reported by their numeric code in error messages. -2. **Coordinate Tracking:** Each token records its `line` and `column` number for precise error reporting. -3. **Implicit Concatenation:** The lexer treats characters as discrete units. The parser handles the logic of implicit concatenation. -4. **Metacharacter Recognition:** Characters like `*`, `+`, `?`, `|`, `(`, `)`, and `.` are immediately recognised as their respective operator tokens. - ---- - -## 2. Parser Details - -### Overview - -The parser consumes the token stream produced by the lexer and constructs an Abstract Syntax Tree (AST). It uses a **recursive descent** approach and strictly enforces operator precedence. - -### Grammar and Precedence - -The parser implements the following grammar (highest to lowest precedence): - -1. **Atom:** The basic building blocks (Literal, Dot, or a Parenthesised Expression). -2. **Factor:** An Atom optionally followed by a quantifier (`*`, `+`, or `?`). -3. **Term:** One or more concatenated Factors. -4. **Expression:** One or more Terms separated by the union operator (`|`). - -### AST Node Structure - -The parser produces a tree composed of various node types (Literal, Dot, Concatenation, Union, Star, Plus, Optional). - -**Important Design Note:** While the base `ASTNode` class contains fields for `nullable`, `firstpos`, and `lastpos`, these are **not** populated by the parser. They are reserved for and populated by the **Stage 2 — NFA Builder**. - -### Error Handling - -The parser detects and reports the following errors with meaningful messages including line and column numbers: - -- **Unmatched Parentheses:** e.g., `(a|b`. -- **Quantifier applied to nothing:** e.g., `*abc` or `(+a)`. -- **Empty alternation branch:** Both left-side (e.g., `|abc`) and right-side (e.g., `abc|`) are explicitly detected. -- **Unexpected Tokens:** Tokens that don't fit the expected grammar at the current position. - ---- - -## Stage 2 — NFA Construction - -This stage converts the Abstract Syntax Tree (AST) into an ε-free Non-deterministic Finite Automaton (NFA) using Glushkov's algorithm. - ---- - -### 1. Glushkov's Algorithm - -The algorithm produces an NFA with exactly $n+1$ states, where $n$ is the number of symbol occurrences (literals or dots) in the regex. - -#### Construction Steps: - -1. **Linearization:** Each symbol is assigned a unique integer position (1 to $n$). -2. **Nullable:** Computes if a sub-expression can match the empty string. -3. **Firstpos:** The set of positions that can match the first character of a string. -4. **Lastpos:** The set of positions that can match the last character of a string. -5. **Followpos:** A map where for each position $p$, it stores the set of positions that can immediately follow it. -6. **Transitions:** - - State 0 is the start state. - - Transitions from state 0 go to all positions in `firstpos` of the root. - - Transitions from state $p$ go to all positions in `followpos(p)`. - -### 2. Global State Numbering - -To facilitate Stage 3 (Verilog Emitter), every NFA state across all input regular expressions is assigned a **globally unique ID**. This ensures that multiple FSM modules can be instantiated in a single Verilog project without identifier collisions. - -### 3. Dot Operator Handling - -The dot (`.`) matches any character. In the NFA, a dot position generates 256 individual transition arcs for every possible byte value (0–255). This ensures the hardware matcher correctly identifies any character in that position. - ---- - -## Validation and Simulation - -To ensure the integrity of the regex-to-NFA conversion before hardware generation, the builder includes an optional simulation engine. - -### 1. NFA Simulation - -The `NFA::simulate` method implements a software-based FSM runner: - -- It tracks the set of active states simultaneously (handling non-determinism). -- It consumes the input string character-by-character. -- It returns `true` if any final active state is an acceptance state. - -## Stage 3 — Verilog Emitter - -This stage transforms the internal NFA structures into synthesisable Verilog HDL code. - -### 1. Per-NFA Modules - -For each regular expression, the emitter generates a self-contained Verilog module (`nfa_N.v`): - -- **File I/O:** All file and directory operations use the C++17 `<filesystem>` library for cross-platform compatibility and improved error handling. File streams are configured to throw exceptions on failure, providing detailed system-level error messages (e.g., "Permission denied"). -- **Robustness:** The emitter validates its inputs before generating code. It will skip emission if no valid NFAs are provided and will throw an error if the golden reference data does not match the number of NFAs, preventing the generation of invalid Verilog. -- **One-Hot Encoding:** The state register uses one-hot encoding (one flip-flop per NFA state). This is ideal for FPGA implementation as it results in high-speed, shallow combinational logic. -- **Optimised Next-State Logic:** Transitions are implemented as pure combinational logic. To improve generation speed, the logic is built by iterating over destination states rather than source states. -- **Deterministic Output:** Global state IDs are sorted during emission to ensure consistent and deterministic Verilog code generation. -- **Match Logic:** The `match` output is registered. The logic uses a Verilog OR-reduction (`|{...}`) for a concise and efficient way to check if any of the final accept states are active. - -### 2. Top-Level Wrapper - -A `top.v` module is generated to instantiate all NFA modules in parallel. - -- All modules share the same clock, reset, and input character stream. -- The results are aggregated into a `match_bus` where each bit corresponds to one regular expression (Regex $k$ maps to `match_bus[k]`). - -### 3. Simulation Testbench - -A fully functional testbench (`tb_top.v`) is generated: - -- It automatically loads test cases from `test_strings.txt`. -- It integrates **Golden Reference** matches generated via `std::regex`. -- For each test string, it drives the `start`, `char_in`, and `end_of_str` signals with correct, deterministic timing. The testbench samples the registered `match` output on the exact clock cycle it becomes valid (the cycle immediately following the assertion of `end_of_str`), explicitly avoiding race conditions and double-sampling bugs. -- It reports `PASS` or `FAIL` for each test case directly in the simulation console and generates a `dump.vcd` for waveform analysis. - ---- - -## Stage 4 — FPGA Integration (Hardware I/O) - -This stage connects the Verilog regex engine to the physical FPGA I/O: a USB-UART serial link for bidirectional communication with a host PC. Three new hardware modules are added. - -### 4.1 UART Transmitter (`uart_tx.v`) - -A standard 8-N-1 UART transmitter with a 4-state FSM: - -| State | Action | -| ------------- | ------------------------------------------------- | -| `S_IDLE` | Line held high; waits for `tx_start` pulse | -| `S_START_BIT` | Drives line low for exactly `CLKS_PER_BIT` cycles | -| `S_DATA_BITS` | Clocks out 8 data bits, LSB first | -| `S_STOP_BIT` | Drives line high for `CLKS_PER_BIT` cycles | - -The `tx_busy` output is held high for the entire duration of a transmission. The caller (the TX serializer in `top_fpga.v`) must not assert `tx_start` while `tx_busy` is high. - -Default baud rate: 115200 at 100 MHz clock (`CLKS_PER_BIT = 868`), configurable via a Verilog parameter. - ---- - -### 4.2 Input FIFO Buffer (`uart_rx_fifo.v`) - -A 16-entry × 8-bit circular FIFO sits between `uart_rx` and the NFA control FSM. This decouples the UART receiver from the NFA pipeline so that incoming bytes are never dropped during the multi-cycle end-of-string and TX response sequence. - -- **Architecture:** Power-of-two depth (configurable via `DEPTH_LOG2` parameter, default 4 → 16 entries). Inferred as distributed RAM (Xilinx SRL16/LUTRAM) on a 7-series device. -- **Write side:** Driven by `uart_rx.rx_ready`; silently discards bytes when full (overflow protection). -- **Read side:** Consumed one byte per cycle by the control FSM. -- **Status flags:** `full` and `empty` are combinationally derived from a `count` register to avoid the grey-code synchronisation complexity that arises with dual-clock designs (this FIFO is single-clock). - ---- - -### 4.3 Updated `top_fpga.v` — Control FSM and TX Serializer - -`top_fpga.v` replaces the previous single-byte latch (`rx_latched_data` / `rx_pending`) with the FIFO-backed architecture and adds two new FSMs. - -#### Main Control FSM (12 states) - -``` -S_IDLE ──► S_FETCH ──► S_DECODE ──► S_CHAR_LOAD ──► S_CHAR_STEP ──► S_IDLE - │ - ├── (newline) ──► S_EOL_END ──► S_EOL_MATCH - │ ──► S_EOL_LATCH ──► S_TX_ARM - │ ──► S_TX_WAIT ──► S_RESET_NFA - │ - └── ('?') ──► S_QUERY_TX ──► S_TX_WAIT -``` - -- `S_IDLE` / `S_FETCH` / `S_DECODE`: pop the FIFO and classify the byte. -- `S_CHAR_LOAD` / `S_CHAR_STEP`: feed one character into the NFA; increment `byte_count`. -- `S_EOL_END` / `S_EOL_MATCH` / `S_EOL_LATCH`: assert `end_of_str`, clock the match flip-flops, capture `match_bus`, update `match_count[k]` for every matched regex, and latch the result to `match_leds`. -- `S_TX_ARM`: call the `build_response` task to serialise the ASCII response into the TX buffer; pulse `tx_send`. -- `S_TX_WAIT`: wait for the TX drain sub-FSM to finish before resetting the NFA. -- `S_RESET_NFA`: assert `nfa_start + nfa_en` for one cycle to re-initialise all NFA FSMs. -- `S_QUERY_TX`: handle the `?` command — build a counter snapshot and transmit without feeding any character to the NFA. - -#### TX Drain Sub-FSM (4 states) - -A separate small FSM drains the ASCII TX buffer byte-by-byte through `uart_tx`: - -``` -TX_IDLE ──► TX_LOAD ──► TX_WAIT ──► TX_NEXT ──► (TX_LOAD if more bytes, else TX_IDLE) -``` - -This sub-FSM runs concurrently with the control FSM, which simply waits in `S_TX_WAIT` until `tx_state == TX_IDLE`. - -#### Hardware Counters - -| Register | Width | Description | -| ---------------- | ------------ | -------------------------------------------------------------------- | -| `byte_count` | 32 bits | Total bytes fed to the NFA engine since the last hardware reset | -| `match_count[k]` | 16 bits each | Cumulative match events for regex _k_; up to 16 independent counters | - -Both are cleared by asserting `rst_btn`. Values are transmitted as part of every response packet. - ---- - -## Stage 5 — Processor-based Regex Engine - -The processor-based engine (`processor/` directory) provides a dynamic alternative to the static Verilog FSMs. Instead of synthesising a new circuit for each regex, we use a custom Soft-Processor that executes "Regex Instructions" loaded into memory. - -### 5.1 Regex CPU Architecture - -The Regex CPU is a specialized processor optimized for NFA simulation: - -- **Instruction Set:** Custom 32-bit instructions (CHAR, SPLIT, JMP, MATCH, ANY). -- **Instruction Memory:** 256-word × 32-bit memory (BRAM/LUTRAM inferred). -- **State Representation:** A 256-bit wide `active_candidates` register represents the set of currently active NFA states. -- **Execution Model:** - - **Character Match Phase:** Iterates through all active states and checks for a character match. - - **Epsilon Expansion Phase:** Iterates through active states to follow SPLIT and JMP transitions until only character-matching or terminal states remain active. - - **Terminal Phase:** Checks if any active state is a MATCH state at the end of the input string. - -### 5.2 Glushkov Assembler Toolchain - -A Python-based compiler converts standard regular expressions into the processor's native machine code: - -1. **`compile_regex.py`**: - - Parses regex into an AST. - - Computes Glushkov `first`, `last`, and `follow` sets. - - Generates a `SPLIT` chain to allow multiple regexes to run in parallel. - - Outputs an assembly file (`.rasm`). -2. **`asm.py`**: - - Parses the `.rasm` file. - - Packs instruction fields into 32-bit binary words. - - Outputs a hex file (`imem.hex`) for FPGA memory initialization or runtime programming. - -### 5.3 Instruction Format - -| Field | Bits | Description | -| ------- | ------- | ------------------------------------------------ | -| `char` | [31:24] | ASCII character to match (or 0 for epsilon/any). | -| `next1` | [23:16] | Primary target PC for jump/split. | -| `next2` | [15:8] | Secondary target PC for split. | -| `mid` | [7:4] | Match ID (Regex index 0–15). | -| `term` | [3] | Terminal bit (1 if this is a MATCH state). | -| `any` | [0] | Wildcard bit (1 if this matches any character). | - -### 5.4 Advantages of the Processor Approach - -- **Runtime Flexibility:** Regexes can be updated by simply writing to the instruction memory over UART. -- **Resource Efficiency:** Supports up to 16 complex regexes with a fixed amount of FPGA logic, regardless of regex complexity (up to 256 instructions). -- **Deterministic Latency:** Fixed scan time of 256 cycles per character ensures predictable performance. diff --git a/docs/pii_guard.md b/docs/pii_guard.md @@ -0,0 +1,106 @@ +# Real-Time PII Guard + +This document provides the complete set of instructions to build, program, and demonstrate the **Hardware-Accelerated PII Scrubber** on an FPGA, along with deep technical details about the underlying regex engine architecture. + +--- + +## 1. Prerequisites & Setup + +### Hardware + +- **FPGA Board:** Xilinx Nexys A7 100T (Artix-7). +- **Cable:** Micro-USB to USB-A (connected to the `USB-PROG` port). +- **Switch:** Power switch in the `ON` position. + +### Software + +- **Vivado Design Suite:** (2019.1 or newer recommended). +- **Terminal/Shell:** Bash (Linux) or Git Bash (Windows). +- **Python 3.x:** With dependencies listed in `tui/requirements.txt` (`pip install -r tui/requirements.txt`). +- **PuTTY:** Serial terminal client. + +--- + +## 2. Build & Hardware Generation + +Before opening Vivado, we must use the C++ compiler to generate the specific Verilog modules for our PII patterns. + +1. **Open your terminal** in the project directory. +2. **Generate the Streaming Verilog Modules:** + This command reads `inputs/regexes.txt` and outputs the Verilog to the `output/` folder with PII-specific hardware (including the 128-byte delay buffer). + ```bash + make pii_build + ``` +3. **Synthesize and Program the FPGA:** + We have automated Vivado TCL scripts for this. From the terminal, simply run: + + ```bash + make pii_synth + make program + ``` + + _Note: If you prefer the GUI, you can open Vivado, create a new project for the `xc7a100tcsg324-1` part, add all `.v` files and the `.xdc` file from the `output/` folder, set `top_fpga.v` as the top module, and generate the bitstream manually._ + + **Important:** Press the **Center Button (BTNC)** on the FPGA after programming to reset the internal state machines and clear the BRAM buffers. + +--- + +## 3. The Live Demo (Using PuTTY) + +This mode allows for a dramatic "live-typing" demonstration where characters are redacted as you type. + +1. **Open Device Manager** on your PC to find the **COM Port** (Eg., `COM3`). +2. **Launch PuTTY** and configure exactly as follows: + - **Connection Type:** Serial + - **Serial Line:** `COMx` (your port) + - **Speed:** `115200` +3. **Configure PuTTY Terminal Behavior:** + - Go to **Category: Terminal** in the left sidebar. + - Check: **Implicit LF on every CR** + - Check: **Implicit CR on every LF** + - **Local Echo:** Set to **Force Off** (Characters should only appear if the FPGA sends them back). + - **Local Line Editing:** Set to **Force Off**. +4. **Open the Connection.** +5. **Press the Reset Button (BTNC)** on the FPGA to ensure the engine is ready. + +--- + +## 4. The High-Speed Streaming Script + +To demonstrate the system running with a simulated data stream (perfect for testing high-throughput): + +1. Navigate to the `tui/` directory and ensure dependencies are installed: + ```bash + pip install -r requirements.txt + ``` +2. Open `tui/pii_demo.py` and ensure the `PORT` variable matches your setup. +3. Run the script: + ```bash + python pii_demo.py + ``` +4. The script will stream a block of text containing credit cards and emails, simulating network line-rate, and print the perfectly redacted string (Eg., `XXXXXXXXXXXXXXXX`) back to your terminal as it flows out of the FPGA's BRAM buffer. + +--- + +## 5. Engine Architecture Details + +### Stage 1 - C++ Regex Parser + +- **Lexer:** Reads raw regex strings and transforms them into typed tokens (Eg., `LITERAL`, `STAR`, `LBRACKET`). It filters for printable ASCII and implements literal escaping (`\`). +- **Parser:** Constructs an Abstract Syntax Tree (AST) using recursive descent, cleanly supporting custom character classes (`[a-z]`) alongside standard operators. + +### Stage 2 - NFA Construction + +Converts the AST into an ε-free NFA using Glushkov's algorithm. + +1. **Linearization:** Symbols and character classes are assigned unique integer positions. +2. **Nullable/Firstpos/Lastpos/Followpos:** Evaluates the AST to determine precise state transitions. +3. **Global State Numbering:** Every state across all input regular expressions gets a globally unique ID to avoid Verilog identifier collisions. + +### Stage 3 - Verilog Emitter + +Transforms the NFA structures into synthesizable Verilog HDL. + +- **One-Hot Encoding:** State registers use one-hot encoding for high-speed, shallow combinational logic. +- **Top-Level Wrapper:** `top_fpga.v` orchestrates the NFA modules in parallel. +- **PII Embellishments:** When compiled with the `--pii` flag, the emitter automatically generates a 128-byte latency buffer utilizing BRAM to support look-behind redaction of matched sequences. diff --git a/dump.vcd b/dump.vcd @@ -1,2057 +0,0 @@ -$date - Tue Mar 31 10:02:33 2026 -$end - -$version - 2025.2 - $dumpfile ("dump.vcd") -$end - -$timescale - 1ps -$end - -$scope module tb_top $end -$var reg 1 ! clk $end -$var reg 1 " en $end -$var reg 1 # rst $end -$var reg 1 $ start $end -$var reg 1 % end_of_str $end -$var reg 8 & char_in [7:0] $end -$var wire 6 ' match_bus [5:0] $end -$scope module uut $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var wire 6 ' match_bus [5:0] $end -$scope module inst_0 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 . match $end -$var reg 3 / state_reg [2:0] $end -$var wire 3 0 next_state [2:0] $end -$upscope $end -$scope module inst_1 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 1 match $end -$var reg 4 2 state_reg [3:0] $end -$var wire 4 3 next_state [3:0] $end -$upscope $end -$scope module inst_2 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 4 match $end -$var reg 4 5 state_reg [3:0] $end -$var wire 4 6 next_state [3:0] $end -$upscope $end -$scope module inst_3 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 7 match $end -$var reg 4 8 state_reg [3:0] $end -$var wire 4 9 next_state [3:0] $end -$upscope $end -$scope module inst_4 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 : match $end -$var reg 4 ; state_reg [3:0] $end -$var wire 4 < next_state [3:0] $end -$upscope $end -$scope module inst_5 $end -$var wire 1 ( clk $end -$var wire 1 ) en $end -$var wire 1 * rst $end -$var wire 1 + start $end -$var wire 1 , end_of_str $end -$var wire 8 - char_in [7:0] $end -$var reg 1 = match $end -$var reg 6 > state_reg [5:0] $end -$var wire 6 ? next_state [5:0] $end -$upscope $end -$upscope $end -$upscope $end -$enddefinitions $end - -#0 -$dumpvars -0! -1" -1# -0$ -0% -b0 & -bx ' -0( -1) -1* -0+ -0, -b0 - -x. -bx / -b0 0 -x1 -bx 2 -b0 3 -x4 -bx 5 -b0 6 -x7 -bx 8 -b0 9 -x: -bx ; -b0 < -x= -bx > -b0 ? -$end - -#5000 -1! -b0 ' -1( -0. -b1 / -01 -b1 2 -04 -b1 5 -07 -b1 8 -0: -b1 ; -0= -b1 > - -#10000 -0! -0( - -#15000 -1! -1( - -#20000 -0! -0# -0( -0* - -#25000 -1! -1( -b0 / -b0 2 -b0 5 -b0 8 -b0 ; -b0 > - -#30000 -0! -1$ -0( -1+ - -#35000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > - -#40000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#45000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#50000 -0! -1% -0( -1, - -#55000 -1! -b101 ' -1( -1. -b0 / -b0 2 -14 -b100 8 -b0 9 -b0 ; - -#60000 -0! -0% -0( -0, - -#65000 -1! -b0 ' -1( -0. -04 -b0 8 - -#70000 -0! -1$ -0( -1+ - -#75000 -1! -1( -b1 / -b10 0 -b1 2 -b10 3 -b1 5 -b1 8 -b10 9 -b1 ; -b10 < -b1 > - -#80000 -0! -0$ -b1100010 & -0( -0+ -b1100010 - -b100 0 -b0 3 -b100 6 -b0 9 -b0 < -b100 ? - -#85000 -1! -1( -b100 / -b0 0 -b0 2 -b100 5 -b0 8 -b0 ; -b100 > - -#90000 -0! -1% -0( -1, - -#95000 -1! -b101 ' -1( -1. -b0 / -14 - -#100000 -0! -0% -0( -0, - -#105000 -1! -b0 ' -1( -0. -04 - -#110000 -0! -1$ -0( -1+ - -#115000 -1! -1( -b1 / -b100 0 -b1 2 -b1 5 -b1 8 -b1 ; -b1 > - -#120000 -0! -0$ -b1100011 & -0( -0+ -b1100011 - -b0 0 -b0 6 -b1000 ? - -#125000 -1! -1( -b0 / -b0 2 -b0 5 -b0 8 -b0 ; -b1000 > -b0 ? - -#130000 -0! -1% -0( -1, - -#135000 -1! -1( -b0 > - -#140000 -0! -0% -0( -0, - -#145000 -1! -1( - -#150000 -0! -1$ -0( -1+ - -#155000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#160000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#165000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#170000 -0! -0( - -#175000 -1! -1( -b0 / -b0 2 -b100 8 -b0 9 -b0 ; - -#180000 -0! -1% -0( -1, - -#185000 -1! -b100 ' -1( -14 -b0 8 - -#190000 -0! -0% -0( -0, - -#195000 -1! -b0 ' -1( -04 - -#200000 -0! -1$ -0( -1+ - -#205000 -1! -1( -b1 / -b10 0 -b1 2 -b10 3 -b1 5 -b1 8 -b10 9 -b1 ; -b10 < -b1 > - -#210000 -0! -0$ -0( -0+ - -#215000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#220000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#225000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#230000 -0! -1% -0( -1, - -#235000 -1! -b100 ' -1( -14 -b1000 8 -b0 9 -b0 ; - -#240000 -0! -0% -0( -0, - -#245000 -1! -b0 ' -1( -04 -b0 8 - -#250000 -0! -1$ -0( -1+ - -#255000 -1! -1( -b1 / -b100 0 -b1 2 -b0 3 -b1 5 -b1 8 -b1 ; -b1 > - -#260000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#265000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#270000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b1000 ? - -#275000 -1! -1( -b0 / -b1000 2 -b0 3 -b1000 5 -b0 6 -b100 8 -b0 9 -b0 ; -b1000 > -b0 ? - -#280000 -0! -1% -0( -1, - -#285000 -1! -b110 ' -1( -11 -b0 2 -14 -b0 5 -b0 8 -b0 > - -#290000 -0! -0% -0( -0, - -#295000 -1! -b0 ' -1( -01 -04 - -#300000 -0! -1$ -0( -1+ - -#305000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#310000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#315000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#320000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#325000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#330000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b0 9 -b1000 < -b1000 ? - -#335000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b0 8 -b1000 ; -b0 < -b1000 > -b0 ? - -#340000 -0! -1% -0( -1, - -#345000 -1! -b10110 ' -1( -11 -b0 2 -14 -b0 5 -1: -b0 ; -b0 > - -#350000 -0! -0% -0( -0, - -#355000 -1! -b0 ' -1( -01 -04 -0: - -#360000 -0! -1$ -0( -1+ - -#365000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#370000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#375000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#380000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#385000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#390000 -0! -0( - -#395000 -1! -1( -b1000 8 -b0 9 -b0 ; - -#400000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b1000 ? - -#405000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b0 8 -b1000 > -b0 ? - -#410000 -0! -1% -0( -1, - -#415000 -1! -b110 ' -1( -11 -b0 2 -14 -b0 5 -b0 > - -#420000 -0! -0% -0( -0, - -#425000 -1! -b0 ' -1( -01 -04 - -#430000 -0! -1$ -0( -1+ - -#435000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#440000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#445000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#450000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#455000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#460000 -0! -0( - -#465000 -1! -1( -b1000 8 -b0 9 -b0 ; - -#470000 -0! -0( - -#475000 -1! -1( -b0 8 - -#480000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b1000 ? - -#485000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b1000 > -b0 ? - -#490000 -0! -1% -0( -1, - -#495000 -1! -b110 ' -1( -11 -b0 2 -14 -b0 5 -b0 > - -#500000 -0! -0% -0( -0, - -#505000 -1! -b0 ' -1( -01 -04 - -#510000 -0! -1$ -0( -1+ - -#515000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#520000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#525000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#530000 -0! -0( - -#535000 -1! -1( -b0 / -b0 2 -b100 8 -b0 9 -b0 ; - -#540000 -0! -0( - -#545000 -1! -1( -b0 8 - -#550000 -0! -0( - -#555000 -1! -1( - -#560000 -0! -1% -0( -1, - -#565000 -1! -b100 ' -1( -14 - -#570000 -0! -0% -0( -0, - -#575000 -1! -b0 ' -1( -04 - -#580000 -0! -1$ -0( -1+ - -#585000 -1! -1( -b1 / -b10 0 -b1 2 -b10 3 -b1 5 -b1 8 -b10 9 -b1 ; -b10 < -b1 > - -#590000 -0! -0$ -b1100010 & -0( -0+ -b1100010 - -b100 0 -b0 3 -b100 6 -b0 9 -b0 < -b100 ? - -#595000 -1! -1( -b100 / -b0 0 -b0 2 -b100 5 -b0 8 -b0 ; -b100 > - -#600000 -0! -0( - -#605000 -1! -1( -b0 / - -#610000 -0! -0( - -#615000 -1! -1( - -#620000 -0! -0( - -#625000 -1! -1( - -#630000 -0! -1% -0( -1, - -#635000 -1! -b100 ' -1( -14 - -#640000 -0! -0% -0( -0, - -#645000 -1! -b0 ' -1( -04 - -#650000 -0! -1$ -0( -1+ - -#655000 -1! -1( -b1 / -b100 0 -b1 2 -b1 5 -b1 8 -b1 ; -b1 > - -#660000 -0! -0$ -b1111000 & -0( -0+ -b1111000 - -b0 0 -b0 6 -b0 ? - -#665000 -1! -1( -b0 / -b0 2 -b0 5 -b0 8 -b0 ; -b0 > - -#670000 -0! -1% -0( -1, - -#675000 -1! -1( - -#680000 -0! -0% -0( -0, - -#685000 -1! -1( - -#690000 -0! -1$ -0( -1+ - -#695000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > - -#700000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#705000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#710000 -0! -b1111000 & -0( -b1111000 - -b0 6 -b0 ? - -#715000 -1! -1( -b0 / -b0 2 -b0 5 -b100 8 -b0 9 -b0 ; -b0 > - -#720000 -0! -b1100010 & -0( -b1100010 - -b1000 9 - -#725000 -1! -1( -b1000 8 -b0 9 - -#730000 -0! -1% -0( -1, - -#735000 -1! -b1000 ' -1( -17 -b0 8 - -#740000 -0! -0% -0( -0, - -#745000 -1! -b0 ' -1( -07 - -#750000 -0! -1$ -0( -1+ - -#755000 -1! -1( -b1 / -b100 0 -b1 2 -b1 5 -b100 6 -b1 8 -b1 ; -b1 > -b100 ? - -#760000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#765000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#770000 -0! -b1111001 & -0( -b1111001 - -b0 6 -b0 ? - -#775000 -1! -1( -b0 / -b0 2 -b0 5 -b100 8 -b0 9 -b0 ; -b0 > - -#780000 -0! -b1100010 & -0( -b1100010 - -b1000 9 - -#785000 -1! -1( -b1000 8 -b0 9 - -#790000 -0! -1% -0( -1, - -#795000 -1! -b1000 ' -1( -17 -b0 8 - -#800000 -0! -0% -0( -0, - -#805000 -1! -b0 ' -1( -07 - -#810000 -0! -1$ -0( -1+ - -#815000 -1! -1( -b1 / -b100 0 -b1 2 -b1 5 -b100 6 -b1 8 -b1 ; -b1 > -b100 ? - -#820000 -0! -0$ -b101000 & -0( -0+ -b101000 - -b0 0 -b0 6 -b0 ? - -#825000 -1! -1( -b0 / -b0 2 -b0 5 -b0 8 -b0 ; -b0 > - -#830000 -0! -b1100001 & -0( -b1100001 - - -#835000 -1! -1( - -#840000 -0! -b1111100 & -0( -b1111100 - - -#845000 -1! -1( - -#850000 -0! -b1100010 & -0( -b1100010 - - -#855000 -1! -1( - -#860000 -0! -b101001 & -0( -b101001 - - -#865000 -1! -1( - -#870000 -0! -1% -0( -1, - -#875000 -1! -1( - -#880000 -0! -0% -0( -0, - -#885000 -1! -1( - -#890000 -0! -1$ -0( -1+ - -#895000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > - -#900000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#905000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#910000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#915000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#920000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b0 9 -b1000 < -b1000 ? - -#925000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b0 8 -b1000 ; -b0 < -b1000 > -b0 ? - -#930000 -0! -1% -0( -1, - -#935000 -1! -b10110 ' -1( -11 -b0 2 -14 -b0 5 -1: -b0 ; -b0 > - -#940000 -0! -0% -0( -0, - -#945000 -1! -b0 ' -1( -01 -04 -0: - -#950000 -0! -1$ -0( -1+ - -#955000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b1000 ? - -#960000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#965000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#970000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#975000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#980000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b0 9 -b1000 < -b1000 ? - -#985000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b0 8 -b1000 ; -b0 < -b1000 > -b0 ? - -#990000 -0! -b1100100 & -0( -b1100100 - -b10000 ? - -#995000 -1! -1( -b0 2 -b0 5 -b0 ; -b10000 > - -#1000000 -0! -1% -0( -1, - -#1005000 -1! -b100000 ' -1( -1= - -#1010000 -0! -0% -0( -0, - -#1015000 -1! -b0 ' -1( -0= - -#1020000 -0! -1$ -0( -1+ - -#1025000 -1! -1( -b1 / -b1 2 -b1 5 -b1 8 -b1 ; -b1 > -b0 ? - -#1030000 -0! -0$ -b1100001 & -0( -0+ -b1100001 - -b10 0 -b10 3 -b10 6 -b10 9 -b10 < -b10 ? - -#1035000 -1! -1( -b10 / -b0 0 -b10 2 -b0 3 -b10 5 -b10 8 -b100 9 -b10 ; -b0 < -b10 > - -#1040000 -0! -b1100010 & -0( -b1100010 - -b100 3 -b100 6 -b100 < -b100 ? - -#1045000 -1! -1( -b0 / -b100 2 -b100 5 -b100 8 -b1000 9 -b100 ; -b0 < -b100 > - -#1050000 -0! -b1100011 & -0( -b1100011 - -b1000 3 -b1000 6 -b0 9 -b1000 < -b1000 ? - -#1055000 -1! -1( -b1000 2 -b0 3 -b1000 5 -b0 6 -b0 8 -b1000 ; -b0 < -b1000 > -b0 ? - -#1060000 -0! -b1100101 & -0( -b1100101 - -b100000 ? - -#1065000 -1! -1( -b0 2 -b0 5 -b0 ; -b100000 > - -#1070000 -0! -1% -0( -1, - -#1075000 -1! -b100000 ' -1( -1= - -#1080000 -0! -0% -0( -0, - -#1085000 -1! -b0 ' -1( -0= - -#1090000 -0! -0( - -#1095000 -1! -1( - -#1100000 -0! -0( - -#1105000 -1! -1( - -#1110000 -0! -0( - -#1115000 -1! -1( - -#1120000 -0! -0( - -#1125000 -1! -1( - -#1130000 -0! -0( - -#1135000 -1! -1( - -#1140000 -0! -0( - -#1145000 -1! -1( - -#1150000 -0! -0( - -#1155000 -1! -1( - -#1160000 -0! -0( - -#1165000 -1! -1( - -#1170000 -0! -0( - -#1175000 -1! -1( - -#1180000 -0! -0( - -#1185000 -1! -1( diff --git a/output-pii/nfa_0.v b/output-pii/nfa_0.v @@ -0,0 +1,41 @@ +`timescale 1ns / 1ps + +// NFA for regex index 0 +module nfa_0 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output wire match, + output wire active +); + + // One-hot state register + reg [5:0] state_reg; + wire [5:0] next_state; + + assign next_state[0] = 1'b1; + assign next_state[1] = (state_reg[0] && (char_in == 8'd46)) | (state_reg[0] && (char_in >= 8'd48) && (char_in <= 8'd57)) | (state_reg[0] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[0] && (char_in >= 8'd97) && (char_in <= 8'd122)) | (state_reg[1] && (char_in == 8'd46)) | (state_reg[1] && (char_in >= 8'd48) && (char_in <= 8'd57)) | (state_reg[1] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[1] && (char_in >= 8'd97) && (char_in <= 8'd122)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd64)); + assign next_state[3] = (state_reg[2] && (char_in == 8'd46)) | (state_reg[2] && (char_in >= 8'd48) && (char_in <= 8'd57)) | (state_reg[2] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[2] && (char_in >= 8'd97) && (char_in <= 8'd122)) | (state_reg[3] && (char_in == 8'd46)) | (state_reg[3] && (char_in >= 8'd48) && (char_in <= 8'd57)) | (state_reg[3] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[3] && (char_in >= 8'd97) && (char_in <= 8'd122)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd46)); + assign next_state[5] = (state_reg[4] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[4] && (char_in >= 8'd97) && (char_in <= 8'd122)) | (state_reg[5] && (char_in >= 8'd65) && (char_in <= 8'd90)) | (state_reg[5] && (char_in >= 8'd97) && (char_in <= 8'd122)); + + always @(posedge clk) begin + if (rst || start) begin + // Reset to start state (one-hot) + state_reg <= 1 << 0; + end else if (en) begin + state_reg <= next_state; + end + end + + // Match logic: asserted immediately on accept state (combinational) + assign match = state_reg[5]; + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[5:1]; + +endmodule diff --git a/output-pii/nfa_1.v b/output-pii/nfa_1.v @@ -0,0 +1,46 @@ +`timescale 1ns / 1ps + +// NFA for regex index 1 +module nfa_1 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output wire match, + output wire active +); + + // One-hot state register + reg [10:0] state_reg; + wire [10:0] next_state; + + assign next_state[0] = 1'b1; + assign next_state[1] = (state_reg[0] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[2] = (state_reg[1] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[3] = (state_reg[2] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[4] = (state_reg[3] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[5] = (state_reg[4] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[6] = (state_reg[5] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[7] = (state_reg[6] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[8] = (state_reg[7] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[9] = (state_reg[8] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[10] = (state_reg[9] && (char_in >= 8'd48) && (char_in <= 8'd57)); + + always @(posedge clk) begin + if (rst || start) begin + // Reset to start state (one-hot) + state_reg <= 1 << 0; + end else if (en) begin + state_reg <= next_state; + end + end + + // Match logic: asserted immediately on accept state (combinational) + assign match = state_reg[10]; + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[10:1]; + +endmodule diff --git a/output-pii/nfa_2.v b/output-pii/nfa_2.v @@ -0,0 +1,48 @@ +`timescale 1ns / 1ps + +// NFA for regex index 2 +module nfa_2 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output wire match, + output wire active +); + + // One-hot state register + reg [12:0] state_reg; + wire [12:0] next_state; + + assign next_state[0] = 1'b1; + assign next_state[1] = (state_reg[0] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[2] = (state_reg[1] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[3] = (state_reg[2] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd45)); + assign next_state[5] = (state_reg[4] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[6] = (state_reg[5] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[7] = (state_reg[6] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[8] = (state_reg[7] && (char_in == 8'd45)); + assign next_state[9] = (state_reg[8] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[10] = (state_reg[9] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[11] = (state_reg[10] && (char_in >= 8'd48) && (char_in <= 8'd57)); + assign next_state[12] = (state_reg[11] && (char_in >= 8'd48) && (char_in <= 8'd57)); + + always @(posedge clk) begin + if (rst || start) begin + // Reset to start state (one-hot) + state_reg <= 1 << 0; + end else if (en) begin + state_reg <= next_state; + end + end + + // Match logic: asserted immediately on accept state (combinational) + assign match = state_reg[12]; + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[12:1]; + +endmodule