xiiregexbuilder
FPGA-Accelerated Regular Expression Matching Engineoutput-eg/top_fpga.v (15025B)
1`timescale 1ns / 1ps
2
3// =============================================================================
4// top_fpga.v - FPGA Top-Level
5// Regex count: 16
6//
7// Architecture:
8// uart_rx → uart_rx_fifo → Control FSM → top (NFA engine)
9// → uart_tx → host PC
10//
11// UART response packet (one line per newline received from host):
12// "MATCH=<16-bit binary> BYTES=<8 hex> HITS=<4 hex per regex,comma-sep>\r\n"
13//
14// Send '?' (0x3F) to query counters without feeding the NFA.
15// =============================================================================
16
17module top_fpga #(
18 parameter NUM_REGEX = 16,
19 parameter CLKS_PER_BIT = 868 // 100 MHz / 115200 baud
20)(
21 input wire clk,
22 input wire rst_btn,
23 input wire uart_rx_pin,
24 output wire uart_tx_pin,
25 output reg [15:0] match_leds
26);
27
28 // UART RX
29 wire [7:0] rx_data;
30 wire rx_ready;
31
32 uart_rx #(.CLKS_PER_BIT(CLKS_PER_BIT)) uart_rx_inst (
33 .clk (clk),
34 .rx (uart_rx_pin),
35 .rx_data (rx_data),
36 .rx_ready(rx_ready)
37 );
38
39 // Input FIFO
40 wire [7:0] fifo_rd_data;
41 wire fifo_empty;
42 wire fifo_full;
43 reg fifo_rd_en = 1'b0;
44
45 uart_rx_fifo #(.DEPTH_LOG2(4)) rx_fifo (
46 .clk (clk),
47 .rst (rst_btn),
48 .wr_data(rx_data),
49 .wr_en (rx_ready && !fifo_full),
50 .full (fifo_full),
51 .rd_data(fifo_rd_data),
52 .rd_en (fifo_rd_en),
53 .empty (fifo_empty)
54 );
55
56 // NFA Engine
57 reg nfa_start = 1'b1;
58 reg nfa_end_of_str = 1'b0;
59 reg [7:0] nfa_char_in = 8'h00;
60 reg nfa_en = 1'b0;
61 wire [15:0] match_bus;
62
63 top regex_engine (
64 .clk (clk),
65 .en (nfa_en),
66 .rst (rst_btn),
67 .start (nfa_start),
68 .end_of_str (nfa_end_of_str),
69 .char_in (nfa_char_in),
70 .match_bus (match_bus)
71 );
72
73 // Hardware Counters
74 reg [31:0] byte_count = 32'd0;
75 // One 16-bit hit counter per regex
76 reg [15:0] match_count [0:15];
77 integer ci;
78 initial begin
79 for (ci = 0; ci < 16; ci = ci + 1)
80 match_count[ci] = 16'd0;
81 end
82
83 // UART TX & Drain Sub-FSM
84 localparam TX_BUF_LEN = 128;
85 reg [7:0] tx_buf [0:TX_BUF_LEN-1];
86 reg [6:0] tx_len = 7'd0;
87 reg [6:0] tx_ptr = 7'd0;
88 reg tx_send = 1'b0; // pulse: start draining tx_buf
89
90 wire tx_busy;
91 reg tx_start_r = 1'b0;
92 reg [7:0] tx_data_r = 8'd0;
93
94 uart_tx #(.CLKS_PER_BIT(CLKS_PER_BIT)) uart_tx_inst (
95 .clk (clk),
96 .rst (rst_btn),
97 .tx_data (tx_data_r),
98 .tx_start(tx_start_r),
99 .tx_busy (tx_busy),
100 .tx (uart_tx_pin)
101 );
102
103 // hex nibble → ASCII character
104 function [7:0] hex_char;
105 input [3:0] nibble;
106 begin
107 hex_char = (nibble < 4'd10) ? (8'd48 + nibble) : (8'd55 + nibble);
108 end
109 endfunction
110
111 localparam TX_IDLE = 2'd0, TX_LOAD = 2'd1, TX_WAIT = 2'd2, TX_NEXT = 2'd3;
112 reg [1:0] tx_state = TX_IDLE;
113
114 always @(posedge clk) begin
115 tx_start_r <= 1'b0;
116 if (rst_btn) begin
117 tx_state <= TX_IDLE;
118 tx_ptr <= 7'd0;
119 end else begin
120 case (tx_state)
121 TX_IDLE: if (tx_send) begin tx_ptr <= 7'd0; tx_state <= TX_LOAD; end
122 TX_LOAD: if (!tx_busy) begin
123 tx_data_r <= tx_buf[tx_ptr];
124 tx_start_r <= 1'b1;
125 tx_state <= TX_WAIT;
126 end
127 TX_WAIT: if (tx_busy) tx_state <= TX_NEXT;
128 TX_NEXT: if (!tx_busy) begin
129 if (tx_ptr + 1 < tx_len) begin
130 tx_ptr <= tx_ptr + 1;
131 tx_state <= TX_LOAD;
132 end else
133 tx_state <= TX_IDLE;
134 end
135 endcase
136 end
137 end
138
139 // build_response Task
140 // Fills tx_buf with the ASCII response packet in one clock cycle.
141 // Format: "MATCH=<bits> BYTES=<8hex> HITS=<4hex per regex,csv>\r\n"
142 reg [6:0] bp; // build pointer (local to task scope)
143 reg [15:0] tmp16;
144 integer ri;
145
146 task build_response;
147 input [15:0] mbits;
148 input [31:0] bcount;
149 integer k;
150 begin : build_task
151 integer p;
152 p = 0;
153 // "MATCH="
154 tx_buf[p]=8'h4D; p=p+1; // M
155 tx_buf[p]=8'h41; p=p+1; // A
156 tx_buf[p]=8'h54; p=p+1; // T
157 tx_buf[p]=8'h43; p=p+1; // C
158 tx_buf[p]=8'h48; p=p+1; // H
159 tx_buf[p]=8'h3D; p=p+1; // =
160 // match bits, MSB first
161 tx_buf[p] = (mbits[15]) ? 8'h31 : 8'h30; p=p+1;
162 tx_buf[p] = (mbits[14]) ? 8'h31 : 8'h30; p=p+1;
163 tx_buf[p] = (mbits[13]) ? 8'h31 : 8'h30; p=p+1;
164 tx_buf[p] = (mbits[12]) ? 8'h31 : 8'h30; p=p+1;
165 tx_buf[p] = (mbits[11]) ? 8'h31 : 8'h30; p=p+1;
166 tx_buf[p] = (mbits[10]) ? 8'h31 : 8'h30; p=p+1;
167 tx_buf[p] = (mbits[9]) ? 8'h31 : 8'h30; p=p+1;
168 tx_buf[p] = (mbits[8]) ? 8'h31 : 8'h30; p=p+1;
169 tx_buf[p] = (mbits[7]) ? 8'h31 : 8'h30; p=p+1;
170 tx_buf[p] = (mbits[6]) ? 8'h31 : 8'h30; p=p+1;
171 tx_buf[p] = (mbits[5]) ? 8'h31 : 8'h30; p=p+1;
172 tx_buf[p] = (mbits[4]) ? 8'h31 : 8'h30; p=p+1;
173 tx_buf[p] = (mbits[3]) ? 8'h31 : 8'h30; p=p+1;
174 tx_buf[p] = (mbits[2]) ? 8'h31 : 8'h30; p=p+1;
175 tx_buf[p] = (mbits[1]) ? 8'h31 : 8'h30; p=p+1;
176 tx_buf[p] = (mbits[0]) ? 8'h31 : 8'h30; p=p+1;
177 // " BYTES="
178 tx_buf[p]=8'h20; p=p+1;
179 tx_buf[p]=8'h42; p=p+1; // B
180 tx_buf[p]=8'h59; p=p+1; // Y
181 tx_buf[p]=8'h54; p=p+1; // T
182 tx_buf[p]=8'h45; p=p+1; // E
183 tx_buf[p]=8'h53; p=p+1; // S
184 tx_buf[p]=8'h3D; p=p+1; // =
185 tx_buf[p]=hex_char(bcount[31:28]); p=p+1;
186 tx_buf[p]=hex_char(bcount[27:24]); p=p+1;
187 tx_buf[p]=hex_char(bcount[23:20]); p=p+1;
188 tx_buf[p]=hex_char(bcount[19:16]); p=p+1;
189 tx_buf[p]=hex_char(bcount[15:12]); p=p+1;
190 tx_buf[p]=hex_char(bcount[11: 8]); p=p+1;
191 tx_buf[p]=hex_char(bcount[ 7: 4]); p=p+1;
192 tx_buf[p]=hex_char(bcount[ 3: 0]); p=p+1;
193 // " HITS="
194 tx_buf[p]=8'h20; p=p+1;
195 tx_buf[p]=8'h48; p=p+1; // H
196 tx_buf[p]=8'h49; p=p+1; // I
197 tx_buf[p]=8'h54; p=p+1; // T
198 tx_buf[p]=8'h53; p=p+1; // S
199 tx_buf[p]=8'h3D; p=p+1; // =
200 tmp16 = match_count[0];
201 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
202 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
203 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
204 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
205 tx_buf[p]=8'h2C; p=p+1; // ','
206 tmp16 = match_count[1];
207 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
208 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
209 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
210 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
211 tx_buf[p]=8'h2C; p=p+1; // ','
212 tmp16 = match_count[2];
213 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
214 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
215 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
216 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
217 tx_buf[p]=8'h2C; p=p+1; // ','
218 tmp16 = match_count[3];
219 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
220 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
221 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
222 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
223 tx_buf[p]=8'h2C; p=p+1; // ','
224 tmp16 = match_count[4];
225 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
226 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
227 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
228 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
229 tx_buf[p]=8'h2C; p=p+1; // ','
230 tmp16 = match_count[5];
231 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
232 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
233 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
234 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
235 tx_buf[p]=8'h2C; p=p+1; // ','
236 tmp16 = match_count[6];
237 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
238 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
239 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
240 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
241 tx_buf[p]=8'h2C; p=p+1; // ','
242 tmp16 = match_count[7];
243 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
244 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
245 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
246 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
247 tx_buf[p]=8'h2C; p=p+1; // ','
248 tmp16 = match_count[8];
249 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
250 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
251 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
252 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
253 tx_buf[p]=8'h2C; p=p+1; // ','
254 tmp16 = match_count[9];
255 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
256 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
257 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
258 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
259 tx_buf[p]=8'h2C; p=p+1; // ','
260 tmp16 = match_count[10];
261 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
262 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
263 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
264 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
265 tx_buf[p]=8'h2C; p=p+1; // ','
266 tmp16 = match_count[11];
267 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
268 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
269 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
270 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
271 tx_buf[p]=8'h2C; p=p+1; // ','
272 tmp16 = match_count[12];
273 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
274 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
275 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
276 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
277 tx_buf[p]=8'h2C; p=p+1; // ','
278 tmp16 = match_count[13];
279 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
280 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
281 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
282 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
283 tx_buf[p]=8'h2C; p=p+1; // ','
284 tmp16 = match_count[14];
285 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
286 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
287 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
288 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
289 tx_buf[p]=8'h2C; p=p+1; // ','
290 tmp16 = match_count[15];
291 tx_buf[p]=hex_char(tmp16[15:12]); p=p+1;
292 tx_buf[p]=hex_char(tmp16[11: 8]); p=p+1;
293 tx_buf[p]=hex_char(tmp16[ 7: 4]); p=p+1;
294 tx_buf[p]=hex_char(tmp16[ 3: 0]); p=p+1;
295 tx_buf[p]=8'h0D; p=p+1; // CR
296 tx_buf[p]=8'h0A; p=p+1; // LF
297 tx_len = p[6:0];
298 end
299 endtask
300
301 // Main Control FSM
302 localparam S_IDLE = 4'd0;
303 localparam S_FETCH = 4'd1;
304 localparam S_DECODE = 4'd2;
305 localparam S_CHAR_LOAD = 4'd3;
306 localparam S_CHAR_STEP = 4'd4;
307 localparam S_EOL_END = 4'd5;
308 localparam S_EOL_MATCH = 4'd6;
309 localparam S_EOL_LATCH = 4'd7;
310 localparam S_TX_ARM = 4'd8;
311 localparam S_TX_WAIT = 4'd9;
312 localparam S_RESET_NFA = 4'd10;
313 localparam S_QUERY_TX = 4'd11;
314
315 reg [3:0] state = S_RESET_NFA;
316
317 reg [15:0] snap_match = 16'b0;
318 reg [31:0] snap_bytes = 32'd0;
319 integer k;
320
321 always @(posedge clk) begin
322 tx_send <= 1'b0;
323 fifo_rd_en <= 1'b0;
324 nfa_en <= 1'b0;
325 nfa_start <= 1'b0;
326
327 if (rst_btn) begin
328 state <= S_RESET_NFA;
329 match_leds <= 16'b0;
330 byte_count <= 32'd0;
331 for (k = 0; k < 16; k = k + 1)
332 match_count[k] <= 16'd0;
333 end else begin
334 case (state)
335
336 S_IDLE: begin
337 nfa_end_of_str <= 1'b0;
338 if (!fifo_empty) state <= S_FETCH;
339 end
340
341 S_FETCH: begin
342 fifo_rd_en <= 1'b1;
343 state <= S_DECODE;
344 end
345
346 S_DECODE: begin
347 if (fifo_rd_data == 8'h0A || fifo_rd_data == 8'h0D) begin
348 nfa_end_of_str <= 1'b1;
349 state <= S_EOL_END;
350 end else if (fifo_rd_data == 8'h3F) begin // '?'
351 state <= S_QUERY_TX;
352 end else begin
353 nfa_char_in <= fifo_rd_data;
354 state <= S_CHAR_LOAD;
355 end
356 end
357
358 S_CHAR_LOAD: state <= S_CHAR_STEP;
359
360 S_CHAR_STEP: begin
361 nfa_en <= 1'b1;
362 byte_count <= byte_count + 1;
363 state <= S_IDLE;
364 end
365
366 S_EOL_END: begin
367 nfa_en <= 1'b1;
368 state <= S_EOL_MATCH;
369 end
370
371 S_EOL_MATCH: begin
372 nfa_en <= 1'b1;
373 nfa_end_of_str <= 1'b0;
374 state <= S_EOL_LATCH;
375 end
376
377 S_EOL_LATCH: begin
378 snap_match <= match_bus;
379 snap_bytes <= byte_count;
380 match_leds <= match_bus;
381 for (k = 0; k < 16; k = k + 1)
382 if (match_bus[k]) match_count[k] <= match_count[k] + 16'd1;
383 state <= S_TX_ARM;
384 end
385
386 S_TX_ARM: begin
387 build_response(snap_match, snap_bytes);
388 tx_send <= 1'b1;
389 state <= S_TX_WAIT;
390 end
391
392 S_TX_WAIT:
393 if (tx_state == TX_IDLE && !tx_send) state <= S_RESET_NFA;
394
395 S_RESET_NFA: begin
396 nfa_start <= 1'b1;
397 nfa_en <= 1'b1;
398 state <= S_IDLE;
399 end
400
401 S_QUERY_TX: begin
402 build_response(16'b0, byte_count);
403 tx_send <= 1'b1;
404 state <= S_TX_WAIT;
405 end
406
407 default: state <= S_IDLE;
408 endcase
409 end
410 end
411
412endmodule