diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/aiger/aigerparse.cc | 705 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.h | 10 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 8 | ||||
-rw-r--r-- | frontends/ast/ast.h | 6 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 21 | ||||
-rw-r--r-- | frontends/verilog/const2ast.cc | 12 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.cc | 10 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 46 |
9 files changed, 752 insertions, 68 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 68552fd06..5f236992d 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Eddie Hung <eddie@fpgeh.com> + * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,19 +22,187 @@ // 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 -#include <libgen.h> +// https://stackoverflow.com/a/46137633 +#ifdef _MSC_VER +#include <stdlib.h> +#define __builtin_bswap32 _byteswap_ulong +#elif defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#define __builtin_bswap32 OSSwapInt32 #endif -#include <array> +#include <inttypes.h> #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/celltypes.h" #include "aigerparse.h" YOSYS_NAMESPACE_BEGIN -AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) - : design(design), f(f), clk_name(clk_name) +inline int32_t from_big_endian(int32_t i32) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return __builtin_bswap32(i32); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return i32; +#else +#error "Unknown endianness" +#endif +} + +struct ConstEvalAig +{ + RTLIL::Module *module; + dict<RTLIL::SigBit, RTLIL::State> values_map; + dict<RTLIL::SigBit, RTLIL::Cell*> sig2driver; + dict<SigBit, pool<RTLIL::SigBit>> sig2deps; + + ConstEvalAig(RTLIL::Module *module) : module(module) + { + for (auto &it : module->cells_) { + if (!yosys_celltypes.cell_known(it.second->type)) + continue; + for (auto &it2 : it.second->connections()) + if (yosys_celltypes.cell_output(it.second->type, it2.first)) { + auto r = sig2driver.insert(std::make_pair(it2.second, it.second)); + log_assert(r.second); + } + } + } + + void clear() + { + values_map.clear(); + sig2deps.clear(); + } + + void set(RTLIL::SigBit sig, RTLIL::State value) + { + auto it = values_map.find(sig); +#ifndef NDEBUG + if (it != values_map.end()) { + RTLIL::State current_val = it->second; + log_assert(current_val == value); + } +#endif + if (it != values_map.end()) + it->second = value; + else + values_map[sig] = value; + } + + void set_incremental(RTLIL::SigSpec sig, RTLIL::Const value) + { + log_assert(GetSize(sig) == GetSize(value)); + + for (int i = 0; i < GetSize(sig); i++) { + auto it = values_map.find(sig[i]); + if (it != values_map.end()) { + RTLIL::State current_val = it->second; + if (current_val != value[i]) + for (auto dep : sig2deps[sig[i]]) + values_map.erase(dep); + it->second = value[i]; + } + else + values_map[sig[i]] = value[i]; + } + } + + void compute_deps(RTLIL::SigBit output, const pool<RTLIL::SigBit> &inputs) + { + sig2deps[output].insert(output); + + RTLIL::Cell *cell = sig2driver.at(output); + RTLIL::SigBit sig_a = cell->getPort("\\A"); + sig2deps[sig_a].reserve(sig2deps[sig_a].size() + sig2deps[output].size()); // Reserve so that any invalidation + // that may occur does so here, and + // not mid insertion (below) + sig2deps[sig_a].insert(sig2deps[output].begin(), sig2deps[output].end()); + if (!inputs.count(sig_a)) + compute_deps(sig_a, inputs); + + if (cell->type == "$_AND_") { + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + sig2deps[sig_b].reserve(sig2deps[sig_b].size() + sig2deps[output].size()); // Reserve so that any invalidation + // that may occur does so here, and + // not mid insertion (below) + sig2deps[sig_b].insert(sig2deps[output].begin(), sig2deps[output].end()); + + if (!inputs.count(sig_b)) + compute_deps(sig_b, inputs); + } + else if (cell->type == "$_NOT_") { + } + else log_abort(); + } + + bool eval(RTLIL::Cell *cell) + { + RTLIL::SigBit sig_y = cell->getPort("\\Y"); + if (values_map.count(sig_y)) + return true; + + RTLIL::SigBit sig_a = cell->getPort("\\A"); + if (!eval(sig_a)) + return false; + + RTLIL::State eval_ret = RTLIL::Sx; + if (cell->type == "$_NOT_") { + if (sig_a == RTLIL::S0) eval_ret = RTLIL::S1; + else if (sig_a == RTLIL::S1) eval_ret = RTLIL::S0; + } + else if (cell->type == "$_AND_") { + if (sig_a == RTLIL::S0) { + eval_ret = RTLIL::S0; + goto eval_end; + } + + { + RTLIL::SigBit sig_b = cell->getPort("\\B"); + if (!eval(sig_b)) + return false; + if (sig_b == RTLIL::S0) { + eval_ret = RTLIL::S0; + goto eval_end; + } + + if (sig_a != RTLIL::S1 || sig_b != RTLIL::S1) + goto eval_end; + + eval_ret = RTLIL::S1; + } + } + else log_abort(); + +eval_end: + set(sig_y, eval_ret); + return true; + } + + bool eval(RTLIL::SigBit &sig) + { + auto it = values_map.find(sig); + if (it != values_map.end()) { + sig = it->second; + return true; + } + + RTLIL::Cell *cell = sig2driver.at(sig); + if (!eval(cell)) + return false; + + it = values_map.find(sig); + if (it != values_map.end()) { + sig = it->second; + return true; + } + + return false; + } +}; + +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; @@ -73,6 +241,8 @@ end_of_header: log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); line_count = 1; + piNum = 0; + flopNum = 0; if (header == "aag") parse_aiger_ascii(); @@ -81,21 +251,10 @@ end_of_header: else log_abort(); - RTLIL::Wire* n0 = module->wire("\\n0"); + RTLIL::Wire* n0 = module->wire("\\__0__"); if (n0) module->connect(n0, RTLIL::S0); - for (unsigned i = 0; i < outputs.size(); ++i) { - RTLIL::Wire *wire = outputs[i]; - if (wire->port_input) { - RTLIL::Wire *o_wire = module->addWire(wire->name.str() + "_o"); - o_wire->port_output = true; - wire->port_output = false; - module->connect(o_wire, wire); - outputs[i] = o_wire; - } - } - // Parse footer (symbol table, comments, etc.) unsigned l1; std::string s; @@ -131,21 +290,30 @@ end_of_header: std::getline(f, line); // Ignore up to start of next line } - module->fixup_ports(); - design->add(module); + post_process(); +} + +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 %" PRId64 ": unable to read literal!\n", static_cast<int64_t>(f.tellg())); + return from_big_endian(l); } 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" : "")); 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; @@ -153,14 +321,164 @@ 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); 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; + piNum = 0; + flopNum = 0; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + RTLIL::Wire* n0 = module->wire("\\__0__"); + if (n0) + module->connect(n0, RTLIL::S0); + + dict<int,IdString> box_lookup; + for (auto m : design->modules()) { + auto it = m->attributes.find("\\abc_box_id"); + if (it == m->attributes.end()) + continue; + if (m->name.begins_with("$paramod")) + continue; + auto r = box_lookup.insert(std::make_pair(it->second.as_int(), m->name)); + log_assert(r.second); + } + + // Parse footer (symbol table, comments, etc.) + 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); + ConstEvalAig 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); + } + // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size()) + ce.clear(); + ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); + for (int j = 0; j < (1 << cutLeavesM); ++j) { + int gray = j ^ (j >> 1); + ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)}); + RTLIL::SigBit o(output_sig); + bool success = ce.eval(o); + log_assert(success); + log_assert(o.wire == nullptr); + lut_mask[gray] = o.data; + } + 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 == 'r') { + uint32_t dataSize = parse_xaiger_literal(f); + flopNum = parse_xaiger_literal(f); + log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); + f.ignore(flopNum * sizeof(uint32_t)); + } + else if (c == 'n') { + parse_xaiger_literal(f); + f >> s; + log_debug("n: '%s'\n", s.c_str()); + } + else if (c == 'h') { + f.ignore(sizeof(uint32_t)); + uint32_t version = parse_xaiger_literal(f); + log_assert(version == 1); + uint32_t ciNum = parse_xaiger_literal(f); + log_debug("ciNum = %u\n", ciNum); + uint32_t coNum = parse_xaiger_literal(f); + log_debug("coNum = %u\n", coNum); + piNum = parse_xaiger_literal(f); + log_debug("piNum = %u\n", piNum); + uint32_t poNum = parse_xaiger_literal(f); + log_debug("poNum = %u\n", poNum); + uint32_t boxNum = parse_xaiger_literal(f); + log_debug("boxNum = %u\n", poNum); + for (unsigned i = 0; i < boxNum; i++) { + f.ignore(2*sizeof(uint32_t)); + uint32_t boxUniqueId = parse_xaiger_literal(f); + log_assert(boxUniqueId > 0); + uint32_t oldBoxNum = parse_xaiger_literal(f); + RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId)); + boxes.emplace_back(cell); + } + } + else if (c == 'a' || c == 'i' || c == 'o') { + uint32_t dataSize = parse_xaiger_literal(f); + f.ignore(dataSize); + } + else { + break; + } + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + } + + post_process(); +} + void AigerReader::parse_aiger_ascii() { std::string line; @@ -173,7 +491,7 @@ void AigerReader::parse_aiger_ascii() if (!(f >> l1)) log_error("Line %u cannot be interpreted as an input!\n", line_count); log_debug("%d is an input\n", l1); - log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted? + log_assert(!(l1 & 1)); // Inputs can't be inverted RTLIL::Wire *wire = createWireIfNotExists(module, l1); wire->port_input = true; inputs.push_back(wire); @@ -182,17 +500,19 @@ void AigerReader::parse_aiger_ascii() // Parse latches RTLIL::Wire *clk_wire = nullptr; if (L > 0) { + log_assert(clk_name != ""); clk_wire = module->wire(clk_name); log_assert(!clk_wire); 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)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_debug("%d %d is a latch\n", l1, l2); - log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? + log_assert(!(l1 & 1)); RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); @@ -208,7 +528,7 @@ void AigerReader::parse_aiger_ascii() else if (l3 == 1) q_wire->attributes["\\init"] = RTLIL::S1; else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + //q_wire->attributes["\\init"] = RTLIL::Sx; } else log_error("Line %u has invalid reset literal for latch!\n", line_count); @@ -226,7 +546,17 @@ void AigerReader::parse_aiger_ascii() 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); + 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? + RTLIL::Wire *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); } @@ -260,11 +590,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 } @@ -285,19 +615,23 @@ 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); } // Parse latches RTLIL::Wire *clk_wire = nullptr; if (L > 0) { + log_assert(clk_name != ""); clk_wire = module->wire(clk_name); log_assert(!clk_wire); 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) { @@ -319,7 +653,7 @@ void AigerReader::parse_aiger_binary() else if (l3 == 1) q_wire->attributes["\\init"] = RTLIL::S1; else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + //q_wire->attributes["\\init"] = RTLIL::Sx; } else log_error("Line %u has invalid reset literal for latch!\n", line_count); @@ -337,7 +671,17 @@ void AigerReader::parse_aiger_binary() 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); + 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? + RTLIL::Wire *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); } @@ -375,15 +719,273 @@ 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); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } +} + +void AigerReader::post_process() +{ + pool<RTLIL::Module*> abc_carry_modules; + unsigned ci_count = 0, co_count = 0; + for (auto cell : boxes) { + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + if (box_module->attributes.count("\\abc_carry") && !abc_carry_modules.count(box_module)) { + RTLIL::Wire* carry_in = nullptr, *carry_out = nullptr; + RTLIL::Wire* last_in = nullptr, *last_out = nullptr; + for (const auto &port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + if (w->port_input) { + if (w->attributes.count("\\abc_carry_in")) { + log_assert(!carry_in); + carry_in = w; + } + log_assert(!last_in || last_in->port_id < w->port_id); + last_in = w; + } + if (w->port_output) { + if (w->attributes.count("\\abc_carry_out")) { + log_assert(!carry_out); + carry_out = w; + } + log_assert(!last_out || last_out->port_id < w->port_id); + last_out = w; + } + } + + if (carry_in != last_in) { + std::swap(box_module->ports[carry_in->port_id], box_module->ports[last_in->port_id]); + std::swap(carry_in->port_id, last_in->port_id); + } + if (carry_out != last_out) { + log_assert(last_out); + std::swap(box_module->ports[carry_out->port_id], box_module->ports[last_out->port_id]); + std::swap(carry_out->port_id, last_out->port_id); + } + } - 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); + // NB: Assume box_module->ports are sorted alphabetically + // (as RTLIL::Module::fixup_ports() would do) + for (auto port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + RTLIL::SigSpec rhs; + RTLIL::Wire* wire = nullptr; + for (int i = 0; i < GetSize(w); i++) { + if (w->port_input) { + log_assert(co_count < outputs.size()); + wire = outputs[co_count++]; + log_assert(wire); + log_assert(wire->port_output); + wire->port_output = false; + } + if (w->port_output) { + log_assert((piNum + ci_count) < inputs.size()); + wire = inputs[piNum + ci_count++]; + log_assert(wire); + log_assert(wire->port_input); + wire->port_input = false; + } + rhs.append(wire); + } + cell->setPort(port_name, rhs); + } + } + + 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_s = 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) { + // Cope with the fact that a CI might be identical + // to a PI (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) + module->rename(wire, escaped_s); + else { + wire->port_input = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + else { + module->connect(wire, existing); + wire->port_input = false; + } + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); + RTLIL::Wire* wire = outputs[variable + co_count]; + log_assert(wire); + log_assert(wire->port_output); + if (escaped_s == "$__dummy__") { + wire->port_output = false; + continue; + } + + if (index == 0) { + // Cope with the fact that a CO might be identical + // to a PO (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else + module->rename(wire, escaped_s); + } + else { + wire->port_output = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + } + else { + module->connect(wire, existing); + wire->port_output = false; + } + } + } + else if (type == "box") { + RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); + if (cell) { // ABC could have optimised this box away + module->rename(cell, escaped_s); + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + for (const auto &i : cell->connections()) { + RTLIL::IdString port_name = i.first; + RTLIL::SigSpec rhs = i.second; + int index = 0; + for (auto bit : rhs.bits()) { + RTLIL::Wire* wire = bit.wire; + RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name))); + if (index == 0) + module->rename(wire, escaped_s); + else if (index > 0) { + module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index)); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + 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) + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + // 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; + } + } + + 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) { + if (other_wire) + module->connect(other_wire, SigSpec(wire, i)); + } + else { + // Since we skip POs that are connected to Sx, + // re-connect them here + module->connect(SigSpec(wire, i), other_wire ? other_wire : SigSpec(RTLIL::Sx)); + } + } + } + + module->fixup_ports(); + design->add(module); + + design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = design->selection_stack.back(); + sel.select(module); + + Pass::call(design, "clean"); + + design->selection_stack.pop_back(); + + for (auto cell : module->cells().to_vector()) { + if (cell->type != "$lut") continue; + auto y_port = cell->getPort("\\Y").as_bit(); + if (y_port.wire->width == 1) + module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str())); + else + module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset)); } } @@ -398,18 +1000,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: " -#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 { @@ -417,6 +1020,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++) { @@ -429,13 +1034,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); @@ -443,7 +1058,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 0e3719cc4..de3c3efbc 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Eddie Hung <eddie@fpgeh.com> + * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,20 +31,26 @@ 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 unsigned line_count; + uint32_t piNum, flopNum; std::vector<RTLIL::Wire*> inputs; std::vector<RTLIL::Wire*> latches; std::vector<RTLIL::Wire*> outputs; std::vector<RTLIL::Wire*> bad_properties; + std::vector<RTLIL::Cell*> boxes; - 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(); + void post_process(); }; YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index b5b968e9e..3d066af53 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -46,7 +46,7 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_autowire; + bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; @@ -1112,6 +1112,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module->nowb = flag_nowb; current_module->noopt = flag_noopt; current_module->icells = flag_icells; + current_module->pwires = flag_pwires; current_module->autowire = flag_autowire; current_module->fixup_ports(); @@ -1126,7 +1127,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast // create AstModule instances for all modules in the AST tree and add them to 'design' void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, - bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) + bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; @@ -1144,6 +1145,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; + flag_pwires = pwires; flag_autowire = autowire; log_assert(current_ast->type == AST_DESIGN); @@ -1480,6 +1482,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; + flag_pwires = pwires; flag_autowire = autowire; use_internal_line_num(); @@ -1551,6 +1554,7 @@ RTLIL::Module *AstModule::clone() const new_mod->lib = lib; new_mod->noopt = noopt; new_mod->icells = icells; + new_mod->pwires = pwires; new_mod->autowire = autowire; return new_mod; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index b8cde060e..54b2fb319 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -286,13 +286,13 @@ namespace AST // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, - bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); + bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire; ~AstModule() YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; @@ -325,7 +325,7 @@ namespace AST_INTERNAL { // internal state variables extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 32ed401eb..079fc11e5 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -853,7 +853,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_FUNCTION: case AST_DPI_FUNCTION: case AST_AUTOWIRE: - case AST_LOCALPARAM: case AST_DEFPARAM: case AST_GENVAR: case AST_GENFOR: @@ -895,6 +894,26 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // remember the parameter, needed for example in techmap case AST_PARAMETER: current_module->avail_parameters.insert(str); + /* fall through */ + case AST_LOCALPARAM: + if (flag_pwires) + { + if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT) + log_file_error(filename, linenum, "Parameter `%s' with non-constant value!\n", str.c_str()); + + RTLIL::Const val = children[0]->bitsAsConst(); + RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); + current_module->connect(wire, val); + + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->attributes[type == AST_PARAMETER ? "\\parameter" : "\\localparam"] = 1; + + for (auto &attr : attributes) { + if (attr.second->type != AST_CONSTANT) + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + wire->attributes[attr.first] = attr.second->asAttrConst(); + } + } break; // create an RTLIL::Wire for an AST_WIRE node diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 57d366dbf..3a3634d34 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -204,7 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { std::vector<RTLIL::State> data; bool is_signed = false; - bool is_unsized = false; + bool is_unsized = len_in_bits < 0; if (*(endptr+1) == 's') { is_signed = true; endptr++; @@ -213,25 +213,25 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { case 'b': case 'B': - my_strtobin(data, endptr+2, len_in_bits, 2, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized); break; case 'o': case 'O': - my_strtobin(data, endptr+2, len_in_bits, 8, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized); break; case 'd': case 'D': - my_strtobin(data, endptr+2, len_in_bits, 10, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized); break; case 'h': case 'H': - my_strtobin(data, endptr+2, len_in_bits, 16, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized); break; default: char next_char = char(tolower(*(endptr+1))); if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { - my_strtobin(data, endptr+1, 1, 2, case_type, true); is_unsized = true; + my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized); } else { return NULL; } diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 01e589efb..0e2bead6f 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -168,6 +168,9 @@ struct VerilogFrontend : public Frontend { log(" -icells\n"); log(" interpret cell types starting with '$' as internal cell types\n"); log("\n"); + log(" -pwires\n"); + log(" add a wire for each module parameter\n"); + log("\n"); log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message if the existing module is not a black box\n"); @@ -228,6 +231,7 @@ struct VerilogFrontend : public Frontend { bool flag_nodpi = false; bool flag_noopt = false; bool flag_icells = false; + bool flag_pwires = false; bool flag_nooverwrite = false; bool flag_overwrite = false; bool flag_defer = false; @@ -368,6 +372,10 @@ struct VerilogFrontend : public Frontend { flag_icells = true; continue; } + if (arg == "-pwires") { + flag_pwires = true; + continue; + } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; @@ -458,7 +466,7 @@ struct VerilogFrontend : public Frontend { error_on_dpi_function(current_ast); AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, - flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 3c612472d..d3fd91473 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -313,7 +313,7 @@ supply1 { return TOK_SUPPLY1; } "$"(info|warning|error|fatal) { frontend_verilog_yylval.string = new std::string(yytext); - return TOK_ELAB_TASK; + return TOK_MSG_TASKS; } "$signed" { return TOK_TO_SIGNED; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index a034f9601..d89b2dc88 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -133,7 +133,7 @@ struct specify_rise_fall { } %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE -%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_ELAB_TASK +%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM @@ -319,15 +319,17 @@ module_para_list: single_module_para: /* empty */ | - TOK_PARAMETER { + attr TOK_PARAMETER { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_range single_param_decl | - TOK_LOCALPARAM { + attr TOK_LOCALPARAM { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_range single_param_decl | single_param_decl; @@ -345,7 +347,13 @@ module_arg_opt_assignment: if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; - if (ast_stack.back()->children.back()->is_reg) + if (ast_stack.back()->children.back()->is_input) { + AstNode *n = ast_stack.back()->children.back(); + if (n->attributes.count("\\defaultvalue")) + delete n->attributes.at("\\defaultvalue"); + n->attributes["\\defaultvalue"] = $2; + } else + if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); @@ -509,6 +517,7 @@ wire_type_token: TOK_GENVAR { astbuf3->type = AST_GENVAR; astbuf3->is_reg = true; + astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; } | @@ -1211,6 +1220,7 @@ param_decl: attr TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -1219,6 +1229,7 @@ localparam_decl: attr TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -1360,7 +1371,12 @@ wire_name_and_opt_assign: wire_name '=' expr { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; - if (astbuf1->is_reg) + if (astbuf1->is_input) { + if (astbuf1->attributes.count("\\defaultvalue")) + delete astbuf1->attributes.at("\\defaultvalue"); + astbuf1->attributes["\\defaultvalue"] = $3; + } else + if (astbuf1->is_reg || astbuf1->is_logic) ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $3)))); else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3)); @@ -1385,7 +1401,13 @@ wire_name: node->children.push_back(rng); } node->type = AST_MEMORY; - node->children.push_back($2); + auto *rangeNode = $2; + if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { + // SV array size [n], rewrite as [n-1:0] + rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); + rangeNode->children.push_back(AstNode::mkconst_int(0, false)); + } + node->children.push_back(rangeNode); } if (current_function_or_task == NULL) { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { @@ -1881,6 +1903,16 @@ behavioral_stmt: } opt_arg_list ';'{ ast_stack.pop_back(); } | + TOK_MSG_TASKS attr { + AstNode *node = new AstNode(AST_TCALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $2); + } opt_arg_list ';'{ + ast_stack.pop_back(); + } | attr TOK_BEGIN opt_label { AstNode *node = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(node); @@ -2177,7 +2209,7 @@ gen_stmt: delete $6; ast_stack.pop_back(); } | - TOK_ELAB_TASK { + TOK_MSG_TASKS { AstNode *node = new AstNode(AST_TECALL); node->str = *$1; delete $1; |