diff options
Diffstat (limited to 'frontends/aiger')
-rw-r--r-- | frontends/aiger/aigerparse.cc | 438 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.h | 5 |
2 files changed, 414 insertions, 29 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index cf7950c85..45cacadb6 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -22,21 +22,24 @@ // Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria. // http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf -#ifndef _WIN32 +#ifdef _WIN32 #include <libgen.h> +#include <stdlib.h> #endif #include <array> #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/consteval.h" #include "aigerparse.h" YOSYS_NAMESPACE_BEGIN -#define log_debug log +//#define log_debug log +#define log_debug(...) ; -AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) - : design(design), f(f), clk_name(clk_name) +AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports) + : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports) { module = new RTLIL::Module; module->name = module_name; @@ -113,21 +116,128 @@ void AigerReader::parse_aiger() std::getline(f, line); // Ignore up to start of next line } + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_symbol = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable) < outputs.size()); + RTLIL::Wire* wire = outputs[variable]; + log_assert(wire); + // Ignore direct output -> input connections + if (!wire->port_output) + continue; + log_assert(wire->port_output); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) { + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + if ((port_input && port_output) || (!port_input && !port_output)) + continue; + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (wire->port_input) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + module->fixup_ports(); design->add(module); + + Pass::call(design, "clean"); +} + +static uint32_t parse_xaiger_literal(std::istream &f) +{ + uint32_t l; + f.read(reinterpret_cast<char*>(&l), sizeof(l)); + if (f.gcount() != sizeof(l)) + log_error("Offset %ld: unable to read literal!\n", static_cast<int64_t>(f.tellg())); + // TODO: Don't assume we're on little endian +#ifdef _WIN32 + return _byteswap_ulong(l); +#else + return __builtin_bswap32(l); +#endif } static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal) { const unsigned variable = literal >> 1; const bool invert = literal & 1; - RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix? + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? RTLIL::Wire *wire = module->wire(wire_name); if (wire) return wire; log_debug("Creating %s\n", wire_name.c_str()); wire = module->addWire(wire_name); + wire->port_input = wire->port_output = false; if (!invert) return wire; - RTLIL::IdString wire_inv_name(stringf("\\n%d", variable)); + RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); RTLIL::Wire *wire_inv = module->wire(wire_inv_name); if (wire_inv) { if (module->cell(wire_inv_name)) return wire; @@ -135,14 +245,225 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera else { log_debug("Creating %s\n", wire_inv_name.c_str()); wire_inv = module->addWire(wire_inv_name); + wire_inv->port_input = wire_inv->port_output = false; } log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); - module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix? + module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); // FIXME: is "$not" the right suffix? return wire; } +void AigerReader::parse_xaiger() +{ + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + line_count = 1; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + // Parse footer (symbol table, comments, etc.) + unsigned l1; + std::string s; + bool comment_seen = false; + for (int c = f.peek(); c != EOF; c = f.peek()) { + if (comment_seen || c == 'c') { + if (!comment_seen) { + f.ignore(1); + c = f.peek(); + comment_seen = true; + } + if (c == '\n') + break; + f.ignore(1); + // XAIGER extensions + if (c == 'm') { + uint32_t dataSize = parse_xaiger_literal(f); + uint32_t lutNum = parse_xaiger_literal(f); + uint32_t lutSize = parse_xaiger_literal(f); + log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); + ConstEval ce(module); + for (unsigned i = 0; i < lutNum; ++i) { + uint32_t rootNodeID = parse_xaiger_literal(f); + uint32_t cutLeavesM = parse_xaiger_literal(f); + log_debug("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); + RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID)); + uint32_t nodeID; + RTLIL::SigSpec input_sig; + for (unsigned j = 0; j < cutLeavesM; ++j) { + nodeID = parse_xaiger_literal(f); + log_debug("\t%u\n", nodeID); + RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID)); + log_assert(wire); + input_sig.append(wire); + } + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); + for (int j = 0; j < (1 << cutLeavesM); ++j) { + ce.push(); + ce.set(input_sig, RTLIL::Const{j, static_cast<int>(cutLeavesM)}); + RTLIL::SigSpec o(output_sig); + ce.eval(o); + lut_mask[j] = o.as_const()[0]; + ce.pop(); + } + RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID)); + log_assert(output_cell); + module->remove(output_cell); + module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask)); + } + } + else if (c == 'n') { + parse_xaiger_literal(f); + f >> s; + log_debug("n: '%s'\n", s.c_str()); + } + } + else if (c == 'i' || c == 'l' || c == 'o') { + f.ignore(1); + if (!(f >> l1 >> s)) + log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); + + if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) + log_error("Line %u has invalid symbol position!\n", line_count); + + RTLIL::Wire* wire; + if (c == 'i') wire = inputs[l1]; + else if (c == 'l') wire = latches[l1]; + else if (c == 'o') wire = outputs[l1]; + else log_abort(); + + module->rename(wire, stringf("\\%s", s.c_str())); + + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + + std::getline(f, line); // Ignore up to start of next line + ++line_count; + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + } + + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_symbol = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable) < outputs.size()); + RTLIL::Wire* wire = outputs[variable]; + log_assert(wire); + log_assert(wire->port_output); + + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) { + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + if ((port_input && port_output) || (!port_input && !port_output)) + continue; + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (wire->port_input) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + + module->fixup_ports(); + design->add(module); + + Pass::call(design, "clean"); +} + void AigerReader::parse_aiger_ascii() { std::string line; @@ -169,6 +490,7 @@ void AigerReader::parse_aiger_ascii() log_debug("Creating %s\n", clk_name.c_str()); clk_wire = module->addWire(clk_name); clk_wire->port_input = true; + clk_wire->port_output = false; } for (unsigned i = 0; i < L; ++i, ++line_count) { if (!(f >> l1 >> l2)) @@ -205,8 +527,32 @@ void AigerReader::parse_aiger_ascii() if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); + RTLIL::Wire *wire; + if (l1 == 0 || l1 == 1) { + wire = module->addWire(NEW_ID); + if (l1 == 0) + module->connect(wire, RTLIL::State::S0); + else if (l1 == 1) + module->connect(wire, RTLIL::State::S1); + else + log_abort(); + } + else { + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? + wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else { + if ((wire->port_input || wire->port_output)) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + } + } wire->port_output = true; outputs.push_back(wire); } @@ -234,11 +580,11 @@ void AigerReader::parse_aiger_ascii() log_error("Line %u cannot be interpreted as an AND!\n", line_count); log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? + log_assert(!(l1 & 1)); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); } std::getline(f, line); // Ignore up to start of next line } @@ -259,8 +605,10 @@ void AigerReader::parse_aiger_binary() // Parse inputs for (unsigned i = 1; i <= I; ++i) { + log_debug("%d is an input\n", i); RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); wire->port_input = true; + log_assert(!wire->port_output); inputs.push_back(wire); } @@ -272,6 +620,7 @@ void AigerReader::parse_aiger_binary() log_debug("Creating %s\n", clk_name.c_str()); clk_wire = module->addWire(clk_name); clk_wire->port_input = true; + clk_wire->port_output = false; } l1 = (I+1) * 2; for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { @@ -308,8 +657,32 @@ void AigerReader::parse_aiger_binary() if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); + RTLIL::Wire *wire; + if (l1 == 0 || l1 == 1) { + wire = module->addWire(NEW_ID); + if (l1 == 0) + module->connect(wire, RTLIL::State::S0); + else if (l1 == 1) + module->connect(wire, RTLIL::State::S1); + else + log_abort(); + } + else { + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_inv" the right suffix? + wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else { + if ((wire->port_input || wire->port_output)) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + } + } wire->port_output = true; outputs.push_back(wire); } @@ -338,15 +711,11 @@ void AigerReader::parse_aiger_binary() l3 = parse_next_delta_literal(f, l2); log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? + log_assert(!(l1 & 1)); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - - RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_"); - and_cell->setPort("\\A", i1_wire); - and_cell->setPort("\\B", i2_wire); - and_cell->setPort("\\Y", o_wire); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); } } @@ -361,18 +730,19 @@ struct AigerFrontend : public Frontend { log("Load module from an AIGER file into the current design.\n"); log("\n"); log(" -module_name <module_name>\n"); - log(" Name of module to be created (default: <filename>)" -#ifdef _WIN32 - "top" // FIXME -#else - "<filename>" -#endif - ")\n"); + log(" Name of module to be created (default: <filename>)\n"); log("\n"); log(" -clk_name <wire_name>\n"); log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); log(" this name (default: clk)\n"); log("\n"); + log(" -map <filename>\n"); + log(" read file with port and latch symbols\n"); + log("\n"); + log(" -wideports\n"); + log(" Merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'.\n"); + log("\n"); } void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -380,6 +750,8 @@ struct AigerFrontend : public Frontend { RTLIL::IdString clk_name = "\\clk"; RTLIL::IdString module_name; + std::string map_filename; + bool wideports = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -392,13 +764,23 @@ struct AigerFrontend : public Frontend { clk_name = RTLIL::escape_id(args[++argidx]); continue; } + if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (arg == "-wideports") { + wideports = true; + continue; + } break; } extra_args(f, filename, args, argidx); if (module_name.empty()) { #ifdef _WIN32 - module_name = "top"; // FIXME: basename equivalent on Win32? + char fname[_MAX_FNAME]; + _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */) + module_name = fname; #else char* bn = strdup(filename.c_str()); module_name = RTLIL::escape_id(bn); @@ -406,7 +788,7 @@ struct AigerFrontend : public Frontend { #endif } - AigerReader reader(design, *f, module_name, clk_name); + AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); reader.parse_aiger(); } } AigerFrontend; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 39a77bd93..42c87a298 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -30,6 +30,8 @@ struct AigerReader std::istream &f; RTLIL::IdString clk_name; RTLIL::Module *module; + std::string map_filename; + bool wideports; unsigned M, I, L, O, A; unsigned B, C, J, F; // Optional in AIGER 1.9 @@ -39,8 +41,9 @@ struct AigerReader std::vector<RTLIL::Wire*> latches; std::vector<RTLIL::Wire*> outputs; - AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name); + AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); + void parse_xaiger(); void parse_aiger_ascii(); void parse_aiger_binary(); }; |