xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine
commit 856f6b7b5f181faa34d3647ba0e1a7c714eba4d1
parent 95ad58a8359cbd2ad7cb090aba50c641410c0418
Author: yash modi <yash3108m@gmail.com>
Date:   Thu, 23 Apr 2026 02:55:42 +0530

Week 6: Verilog NFA generation for example regexes and project specifications (Part 3: Finalizing)

Diffstat:
Moutput-eg/nfa_4.v | 12++++++++----
Moutput-eg/nfa_5.v | 28+++++++++++++++++++---------
Aoutput-eg/nfa_6.v | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aoutput-eg/nfa_7.v | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aoutput-eg/nfa_8.v | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aoutput-eg/nfa_9.v | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Moutput-eg/top_fpga.v | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
7 files changed, 315 insertions(+), 24 deletions(-)

diff --git a/output-eg/nfa_4.v b/output-eg/nfa_4.v @@ -8,7 +8,8 @@ module nfa_4 ( input wire start, input wire end_of_str, input wire [7:0] char_in, - output reg match + output reg match, + output wire active ); // One-hot state register @@ -16,9 +17,9 @@ module nfa_4 ( wire [3:0] next_state; assign next_state[0] = 1'b0; - assign next_state[1] = (state_reg[0] && (char_in == 8'd97)); - assign next_state[2] = (state_reg[1] && (char_in == 8'd98)); - assign next_state[3] = (state_reg[2] && (char_in == 8'd99)); + assign next_state[1] = (state_reg[0] && (char_in == 8'd102)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd108)); + assign next_state[3] = (state_reg[1] && (char_in == 8'd121)) | (state_reg[2] && (char_in == 8'd121)); always @(posedge clk) begin if (rst || start) begin @@ -42,4 +43,7 @@ module nfa_4 ( end end + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[3:1]; + endmodule diff --git a/output-eg/nfa_5.v b/output-eg/nfa_5.v @@ -8,19 +8,26 @@ module nfa_5 ( input wire start, input wire end_of_str, input wire [7:0] char_in, - output reg match + output reg match, + output wire active ); // One-hot state register - reg [5:0] state_reg; - wire [5:0] next_state; + reg [11:0] state_reg; + wire [11:0] next_state; assign next_state[0] = 1'b0; - assign next_state[1] = (state_reg[0] && (char_in == 8'd97)) | (state_reg[1] && (char_in == 8'd97)) | (state_reg[2] && (char_in == 8'd97)); - assign next_state[2] = (state_reg[0] && (char_in == 8'd98)) | (state_reg[1] && (char_in == 8'd98)) | (state_reg[2] && (char_in == 8'd98)); - assign next_state[3] = (state_reg[0] && (char_in == 8'd99)) | (state_reg[1] && (char_in == 8'd99)) | (state_reg[2] && (char_in == 8'd99)); - assign next_state[4] = (state_reg[3] && (char_in == 8'd100)) | (state_reg[4] && (char_in == 8'd100)) | (state_reg[5] && (char_in == 8'd100)); - assign next_state[5] = (state_reg[3] && (char_in == 8'd101)) | (state_reg[4] && (char_in == 8'd101)) | (state_reg[5] && (char_in == 8'd101)); + assign next_state[1] = (state_reg[0] && (char_in == 8'd97)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd112)); + assign next_state[3] = (state_reg[2] && (char_in == 8'd112)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd108)); + assign next_state[5] = (state_reg[4] && (char_in == 8'd101)); + assign next_state[6] = (state_reg[0] && (char_in == 8'd111)); + assign next_state[7] = (state_reg[6] && (char_in == 8'd114)); + assign next_state[8] = (state_reg[7] && (char_in == 8'd97)); + assign next_state[9] = (state_reg[8] && (char_in == 8'd110)); + assign next_state[10] = (state_reg[9] && (char_in == 8'd103)); + assign next_state[11] = (state_reg[10] && (char_in == 8'd101)); always @(posedge clk) begin if (rst || start) begin @@ -37,11 +44,14 @@ module nfa_5 ( match <= 1'b0; end else if (en) begin if (end_of_str) begin - match <= (|{state_reg[4], state_reg[5]}); + match <= (|{state_reg[5], state_reg[11]}); end else begin match <= 1'b0; end end end + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[11:1]; + endmodule diff --git a/output-eg/nfa_6.v b/output-eg/nfa_6.v @@ -0,0 +1,56 @@ +`timescale 1ns / 1ps + +// NFA for regex index 6 +module nfa_6 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output reg match, + output wire active +); + + // One-hot state register + reg [10:0] state_reg; + wire [10:0] next_state; + + assign next_state[0] = 1'b0; + assign next_state[1] = (state_reg[0] && (char_in == 8'd114)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd101)); + assign next_state[3] = (state_reg[2] && (char_in == 8'd100)); + assign next_state[4] = (state_reg[0] && (char_in == 8'd98)); + assign next_state[5] = (state_reg[4] && (char_in == 8'd108)); + assign next_state[6] = (state_reg[5] && (char_in == 8'd117)); + assign next_state[7] = (state_reg[6] && (char_in == 8'd101)); + assign next_state[8] = (state_reg[3] && (char_in == 8'd99)) | (state_reg[7] && (char_in == 8'd99)); + assign next_state[9] = (state_reg[8] && (char_in == 8'd97)); + assign next_state[10] = (state_reg[9] && (char_in == 8'd114)); + + 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 on cycle following end_of_str + always @(posedge clk) begin + if (rst || start) begin + match <= 1'b0; + end else if (en) begin + if (end_of_str) begin + match <= state_reg[10]; + end else begin + match <= 1'b0; + end + end + end + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[10:1]; + +endmodule diff --git a/output-eg/nfa_7.v b/output-eg/nfa_7.v @@ -0,0 +1,50 @@ +`timescale 1ns / 1ps + +// NFA for regex index 7 +module nfa_7 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output reg match, + output wire active +); + + // One-hot state register + reg [4:0] state_reg; + wire [4:0] next_state; + + assign next_state[0] = 1'b0; + assign next_state[1] = (state_reg[0] && (char_in == 8'd108)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd111)); + assign next_state[3] = (state_reg[2] && (char_in == 8'd108)) | (state_reg[4] && (char_in == 8'd108)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd111)); + + 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 on cycle following end_of_str + always @(posedge clk) begin + if (rst || start) begin + match <= 1'b0; + end else if (en) begin + if (end_of_str) begin + match <= (|{state_reg[2], state_reg[4]}); + end else begin + match <= 1'b0; + end + end + end + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[4:1]; + +endmodule diff --git a/output-eg/nfa_8.v b/output-eg/nfa_8.v @@ -0,0 +1,50 @@ +`timescale 1ns / 1ps + +// NFA for regex index 8 +module nfa_8 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output reg match, + output wire active +); + + // One-hot state register + reg [4:0] state_reg; + wire [4:0] next_state; + + assign next_state[0] = 1'b0; + assign next_state[1] = (state_reg[0] && (char_in == 8'd103)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd111)); + assign next_state[3] = (state_reg[2] && (char_in == 8'd111)) | (state_reg[4] && (char_in == 8'd111)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd100)); + + 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 on cycle following end_of_str + always @(posedge clk) begin + if (rst || start) begin + match <= 1'b0; + end else if (en) begin + if (end_of_str) begin + match <= state_reg[4]; + end else begin + match <= 1'b0; + end + end + end + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[4:1]; + +endmodule diff --git a/output-eg/nfa_9.v b/output-eg/nfa_9.v @@ -0,0 +1,51 @@ +`timescale 1ns / 1ps + +// NFA for regex index 9 +module nfa_9 ( + input wire clk, + input wire en, + input wire rst, + input wire start, + input wire end_of_str, + input wire [7:0] char_in, + output reg match, + output wire active +); + + // One-hot state register + reg [5:0] state_reg; + wire [5:0] next_state; + + assign next_state[0] = 1'b0; + assign next_state[1] = (state_reg[0] && (char_in == 8'd104)); + assign next_state[2] = (state_reg[1] && (char_in == 8'd101)); + assign next_state[3] = (state_reg[0] && (char_in == 8'd108)) | (state_reg[2] && (char_in == 8'd108)); + assign next_state[4] = (state_reg[3] && (char_in == 8'd108)); + assign next_state[5] = (state_reg[4] && (char_in == 8'd111)); + + 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 on cycle following end_of_str + always @(posedge clk) begin + if (rst || start) begin + match <= 1'b0; + end else if (en) begin + if (end_of_str) begin + match <= state_reg[5]; + end else begin + match <= 1'b0; + end + end + end + + // Active logic: high if any state other than state 0 is active + assign active = |state_reg[5:1]; + +endmodule diff --git a/output-eg/top_fpga.v b/output-eg/top_fpga.v @@ -1,28 +1,28 @@ `timescale 1ns / 1ps // ============================================================================= -// top_fpga.v — FPGA Top-Level -// Regex count: 6 +// top_fpga.v - FPGA Top-Level +// Regex count: 16 // // Architecture: // uart_rx → uart_rx_fifo → Control FSM → top (NFA engine) // → uart_tx → host PC // // UART response packet (one line per newline received from host): -// "MATCH=<6-bit binary> BYTES=<8 hex> HITS=<4 hex per regex,comma-sep>\r\n" +// "MATCH=<16-bit binary> BYTES=<8 hex> HITS=<4 hex per regex,comma-sep>\r\n" // // Send '?' (0x3F) to query counters without feeding the NFA. // ============================================================================= module top_fpga #( - parameter NUM_REGEX = 6, + parameter NUM_REGEX = 16, parameter CLKS_PER_BIT = 868 // 100 MHz / 115200 baud )( input wire clk, input wire rst_btn, input wire uart_rx_pin, output wire uart_tx_pin, - output reg [5:0] match_leds + output reg [15:0] match_leds ); // UART RX @@ -58,7 +58,7 @@ module top_fpga #( reg nfa_end_of_str = 1'b0; reg [7:0] nfa_char_in = 8'h00; reg nfa_en = 1'b0; - wire [5:0] match_bus; + wire [15:0] match_bus; top regex_engine ( .clk (clk), @@ -144,7 +144,7 @@ module top_fpga #( integer ri; task build_response; - input [5:0] mbits; + input [15:0] mbits; input [31:0] bcount; integer k; begin : build_task @@ -158,6 +158,16 @@ module top_fpga #( tx_buf[p]=8'h48; p=p+1; // H tx_buf[p]=8'h3D; p=p+1; // = // match bits, MSB first + tx_buf[p] = (mbits[15]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[14]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[13]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[12]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[11]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[10]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[9]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[8]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[7]) ? 8'h31 : 8'h30; p=p+1; + tx_buf[p] = (mbits[6]) ? 8'h31 : 8'h30; p=p+1; tx_buf[p] = (mbits[5]) ? 8'h31 : 8'h30; p=p+1; tx_buf[p] = (mbits[4]) ? 8'h31 : 8'h30; p=p+1; tx_buf[p] = (mbits[3]) ? 8'h31 : 8'h30; p=p+1; @@ -222,6 +232,66 @@ module top_fpga #( tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[6]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[7]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[8]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[9]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[10]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[11]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[12]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[13]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[14]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; + tx_buf[p]=8'h2C; p=p+1; // ',' + tmp16 = match_count[15]; + tx_buf[p]=hex_char(tmp16[15:12]); p=p+1; + tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1; + tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1; tx_buf[p]=8'h0D; p=p+1; // CR tx_buf[p]=8'h0A; p=p+1; // LF tx_len = p[6:0]; @@ -244,7 +314,7 @@ module top_fpga #( reg [3:0] state = S_RESET_NFA; - reg [5:0] snap_match = 6'b0; + reg [15:0] snap_match = 16'b0; reg [31:0] snap_bytes = 32'd0; integer k; @@ -256,7 +326,7 @@ module top_fpga #( if (rst_btn) begin state <= S_RESET_NFA; - match_leds <= 6'b0; + match_leds <= 16'b0; byte_count <= 32'd0; for (k = 0; k < 16; k = k + 1) match_count[k] <= 16'd0; @@ -308,7 +378,7 @@ module top_fpga #( snap_match <= match_bus; snap_bytes <= byte_count; match_leds <= match_bus; - for (k = 0; k < 6; k = k + 1) + for (k = 0; k < 16; k = k + 1) if (match_bus[k]) match_count[k] <= match_count[k] + 16'd1; state <= S_TX_ARM; end @@ -329,7 +399,7 @@ module top_fpga #( end S_QUERY_TX: begin - build_response(6'b0, byte_count); + build_response(16'b0, byte_count); tx_send <= 1'b1; state <= S_TX_WAIT; end