xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine

output-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