xiiregexbuilder

FPGA-Accelerated Regular Expression Matching Engine
commit b98cdd2ed1a02ffcd879c0abad800b27a10d02bd
parent 9639da9d4aebf625f01e8b3987447850881bf3c4
Author: yash modi <yash3108m@gmail.com>
Date:   Thu, 26 Mar 2026 23:50:46 +0530

Week 2: Begin Verilog Emitter Implementation (Part 1: Initial definitions)

Diffstat:
Asrc/emitter.cpp | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 132 insertions(+), 0 deletions(-)

diff --git a/src/emitter.cpp b/src/emitter.cpp @@ -0,0 +1,132 @@ +#include "emitter.h" +#include <fstream> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <algorithm> +#include <set> +#include <map> +#include <system_error> +#include <filesystem> + +// ============================================================================= +// Emitter::emit — orchestration +// ============================================================================= +void Emitter::emit(const std::vector<std::unique_ptr<NFA>> &nfas, + const std::string &outputDirStr, + const std::vector<std::string> &testStrings, + const std::vector<std::string> &expectedMatches) +{ + if (nfas.empty()) + { + std::cout << "No valid NFAs were provided; skipping Verilog emission." << std::endl; + return; + } + + std::filesystem::path outputDir(outputDirStr); + try + { + std::filesystem::create_directories(outputDir); + } + catch (const std::filesystem::filesystem_error &e) + { + throw std::system_error(e.code(), "Failed to create output directory: " + outputDir.string()); + } + + for (const auto &nfa : nfas) + { + emitNFAModule(*nfa, outputDir); + } + + emitTopModule(nfas, outputDir); + emitUARTRX(outputDir); // uart_rx.v + emitUARTTX(outputDir); // uart_tx.v + emitFIFO(outputDir); // uart_rx_fifo.v + emitTopFPGA(nfas, outputDir); // top_fpga.v + emitConstraints(nfas, outputDir); + emitTestbench(nfas, outputDir, testStrings, expectedMatches); +} + +// ============================================================================= +// emitNFAModule — one file per regex +// ============================================================================= +void Emitter::emitNFAModule(const NFA &nfa, const std::filesystem::path &outputDir) +{ + auto filePath = outputDir / ("nfa_" + std::to_string(nfa.regexIndex) + ".v"); + std::ofstream out; + out.exceptions(std::ofstream::failbit | std::ofstream::badbit); + + try + { + out.open(filePath); + } + catch (const std::ios_base::failure &e) + { + throw std::system_error(errno, std::generic_category(), "Could not open " + filePath.string() + " for writing"); + } + + int numStates = static_cast<int>(nfa.states.size()); + if (numStates == 0) + return; + + std::map<int, int> globalToLocal; + int localIdx = 0; + + // Ensure start state is always local state 0 + globalToLocal[nfa.startStateId] = localIdx++; + + // Sort other state IDs for deterministic output + std::set<int> otherIds; + for (const auto &pair : nfa.states) + { + if (pair.first != nfa.startStateId) + otherIds.insert(pair.first); + } + for (int id : otherIds) + { + globalToLocal[id] = localIdx++; + } + + out << "`timescale 1ns / 1ps\n\n"; + out << "// NFA for regex index " << nfa.regexIndex << "\n"; + out << "module nfa_" << nfa.regexIndex << " (\n" + << " input wire clk,\n" + << " input wire en,\n" + << " input wire rst,\n" + << " input wire start,\n" + << " input wire end_of_str,\n" + << " input wire [7:0] char_in,\n" + << " output reg match\n" + << ");\n\n"; + + out << " // One-hot state register\n" + << " reg [" << numStates - 1 << ":0] state_reg;\n" + << " wire [" << numStates - 1 << ":0] next_state;\n\n"; + + std::map<int, std::vector<std::pair<int, unsigned char>>> invertedTransitions; + for (const auto &[srcGlobalId, srcState] : nfa.states) + { + for (const auto &[c, dstIds] : srcState.transitions) + { + for (int dstGlobalId : dstIds) + { + invertedTransitions[dstGlobalId].push_back({srcGlobalId, c}); + } + } + } + + for (const auto &[globalId, localId] : globalToLocal) + { + out << " assign next_state[" << localId << "] = "; + + std::vector<std::string> terms; + + if (auto it = invertedTransitions.find(globalId); it != invertedTransitions.end()) + { + std::map<int, std::vector<unsigned char>> charsBySrc; + for (const auto &[srcGlobalId, c] : it->second) + { + charsBySrc[globalToLocal.at(srcGlobalId)].push_back(c); + } + + // Magnitude comparators for contiguous wildcard ranges