diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/aiger/Makefile.inc | 3 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.cc | 414 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.h | 51 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 331 | ||||
-rw-r--r-- | frontends/ast/ast.h | 18 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 205 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 286 | ||||
-rw-r--r-- | frontends/blif/blifparse.cc | 4 | ||||
-rw-r--r-- | frontends/ilang/ilang_frontend.cc | 30 | ||||
-rw-r--r-- | frontends/ilang/ilang_frontend.h | 2 | ||||
-rw-r--r-- | frontends/ilang/ilang_parser.y | 42 | ||||
-rw-r--r-- | frontends/liberty/liberty.cc | 15 | ||||
-rw-r--r-- | frontends/verific/README | 31 | ||||
-rw-r--r-- | frontends/verific/verific.cc | 534 | ||||
-rw-r--r-- | frontends/verific/verific.h | 1 | ||||
-rw-r--r-- | frontends/verific/verificsva.cc | 23 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.cc | 52 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.h | 9 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 21 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 387 |
20 files changed, 1919 insertions, 540 deletions
diff --git a/frontends/aiger/Makefile.inc b/frontends/aiger/Makefile.inc new file mode 100644 index 00000000..bc111245 --- /dev/null +++ b/frontends/aiger/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += frontends/aiger/aigerparse.o + diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc new file mode 100644 index 00000000..cf7950c8 --- /dev/null +++ b/frontends/aiger/aigerparse.cc @@ -0,0 +1,414 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] The AIGER And-Inverter Graph (AIG) Format Version 20071012 +// 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> +#endif +#include <array> + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "aigerparse.h" + +YOSYS_NAMESPACE_BEGIN + +#define log_debug log + +AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) + : design(design), f(f), clk_name(clk_name) +{ + module = new RTLIL::Module; + module->name = module_name; + if (design->module(module->name)) + log_error("Duplicate definition of module %s!\n", log_id(module->name)); +} + +void AigerReader::parse_aiger() +{ + 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; + for (auto &i : std::array<std::reference_wrapper<unsigned>,4>{B, C, J, F}) { + if (f.peek() != ' ') break; + if (!(f >> i)) + log_error("Invalid AIGER header\n"); + } + + 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 B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); + + 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; + for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { + 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())); + } + else if (c == 'b' || c == 'j' || c == 'f') { + // TODO + } + else if (c == 'c') { + f.ignore(1); + if (f.peek() == '\n') + break; + // Else constraint (TODO) + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + std::getline(f, line); // Ignore up to start of next line + } + + module->fixup_ports(); + design->add(module); +} + +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::Wire *wire = module->wire(wire_name); + if (wire) return wire; + log_debug("Creating %s\n", wire_name.c_str()); + wire = module->addWire(wire_name); + if (!invert) return wire; + RTLIL::IdString wire_inv_name(stringf("\\n%d", variable)); + RTLIL::Wire *wire_inv = module->wire(wire_inv_name); + if (wire_inv) { + if (module->cell(wire_inv_name)) return wire; + } + else { + log_debug("Creating %s\n", wire_inv_name.c_str()); + wire_inv = module->addWire(wire_inv_name); + } + + 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? + + return wire; +} + +void AigerReader::parse_aiger_ascii() +{ + std::string line; + std::stringstream ss; + + unsigned l1, l2, l3; + + // Parse inputs + for (unsigned i = 0; i < I; ++i, ++line_count) { + 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? + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_input = true; + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + 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; + } + 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? + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0 || l3 == 1) + q_wire->attributes["\\init"] = RTLIL::Const(l3); + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::Const(0); + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + 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); + wire->port_output = true; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse bad state properties + for (unsigned i = 0; i < B; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + for (unsigned i = 0; i < A; ++i) { + if (!(f >> l1 >> l2 >> l3)) + 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? + 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); + } + std::getline(f, line); // Ignore up to start of next line +} + +static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) +{ + unsigned x = 0, i = 0; + unsigned char ch; + while ((ch = f.get()) & 0x80) + x |= (ch & 0x7f) << (7 * i++); + return ref - (x | (ch << (7 * i))); +} + +void AigerReader::parse_aiger_binary() +{ + unsigned l1, l2, l3; + std::string line; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i) { + RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); + wire->port_input = true; + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + 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; + } + l1 = (I+1) * 2; + for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { + if (!(f >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0 || l3 == 1) + q_wire->attributes["\\init"] = RTLIL::Const(l3); + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::Const(0); + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + 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); + wire->port_output = true; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse bad state properties + for (unsigned i = 0; i < B; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + l1 = (I+L+1) << 1; + for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { + l2 = parse_next_delta_literal(f, l1); + 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? + 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); + } +} + +struct AigerFrontend : public Frontend { + AigerFrontend() : Frontend("aiger", "read AIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_aiger [options] [filename]\n"); + log("\n"); + 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("\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"); + } + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing AIGER frontend.\n"); + + RTLIL::IdString clk_name = "\\clk"; + RTLIL::IdString module_name; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-module_name" && argidx+1 < args.size()) { + module_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (arg == "-clk_name" && argidx+1 < args.size()) { + clk_name = RTLIL::escape_id(args[++argidx]); + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + if (module_name.empty()) { +#ifdef _WIN32 + module_name = "top"; // FIXME: basename equivalent on Win32? +#else + char* bn = strdup(filename.c_str()); + module_name = RTLIL::escape_id(bn); + free(bn); +#endif + } + + AigerReader reader(design, *f, module_name, clk_name); + reader.parse_aiger(); + } +} AigerFrontend; + +YOSYS_NAMESPACE_END diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h new file mode 100644 index 00000000..c49cd152 --- /dev/null +++ b/frontends/aiger/aigerparse.h @@ -0,0 +1,51 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef ABC_AIGERPARSE +#define ABC_AIGERPARSE + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +struct AigerReader +{ + RTLIL::Design *design; + std::istream &f; + RTLIL::IdString clk_name; + RTLIL::Module *module; + + unsigned M, I, L, O, A; + unsigned B, C, J, F; // Optional in AIGER 1.9 + unsigned line_count; + + std::vector<RTLIL::Wire*> inputs; + 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); + void parse_aiger(); + void parse_aiger_ascii(); + void parse_aiger_binary(); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index e79be953..d4899616 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.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 @@ -35,16 +36,16 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// instanciate global variables (public API) +// instantiate global variables (public API) namespace AST { std::string current_filename; void (*set_line_num)(int) = NULL; int (*get_line_num)() = NULL; } -// instanciate global variables (private API) +// instantiate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + 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_lib, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; @@ -171,8 +172,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) AstNode *attr = attributes.at(id); if (attr->type != AST_CONSTANT) - log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", - id.c_str()); + log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str()); return attr->integer != 0; } @@ -431,9 +431,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_RANGE: - if (range_valid) - fprintf(f, "[%d:%d]", range_left, range_right); - else { + if (range_valid) { + if (range_swapped) + fprintf(f, "[%d:%d]", range_right, range_left); + else + fprintf(f, "[%d:%d]", range_left, range_right); + } else { for (auto child : children) { fprintf(f, "%c", first ? '[' : ':'); child->dumpVlog(f, ""); @@ -562,7 +565,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_CONCAT: fprintf(f, "{"); - for (auto child : children) { + for (int i = GetSize(children)-1; i >= 0; i--) { + auto child = children[i]; if (!first) fprintf(f, ", "); child->dumpVlog(f, ""); @@ -903,9 +907,9 @@ RTLIL::Const AstNode::realAsConst(int width) } // create a new AstModule from an AST_MODULE AST node -static AstModule* process_module(AstNode *ast, bool defer) +static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL) { - log_assert(ast->type == AST_MODULE); + log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); if (defer) log("Storing AST representation for module `%s'.\n", ast->str.c_str()); @@ -916,28 +920,38 @@ static AstModule* process_module(AstNode *ast, bool defer) current_module->ast = NULL; current_module->name = ast->str; current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); + current_module->set_bool_attribute("\\cells_not_processed"); current_ast_mod = ast; - AstNode *ast_before_simplify = ast->clone(); + AstNode *ast_before_simplify; + if (original_ast != NULL) + ast_before_simplify = original_ast; + else + ast_before_simplify = ast->clone(); if (flag_dump_ast1) { - log("Dumping Verilog AST before simplification:\n"); + log("Dumping AST before simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } + if (flag_dump_vlog1) { + log("Dumping Verilog AST before simplification:\n"); + ast->dumpVlog(NULL, " "); + log("--- END OF AST DUMP ---\n"); + } if (!defer) { while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } if (flag_dump_ast2) { - log("Dumping Verilog AST after simplification:\n"); + log("Dumping AST after simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } - if (flag_dump_vlog) { - log("Dumping Verilog AST (as requested by dump_vlog option):\n"); + if (flag_dump_vlog2) { + log("Dumping Verilog AST after simplification:\n"); ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } @@ -963,8 +977,7 @@ static AstModule* process_module(AstNode *ast, bool defer) for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); current_module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { @@ -989,6 +1002,8 @@ static AstModule* process_module(AstNode *ast, bool defer) ignoreThisSignalsInInitial = RTLIL::SigSpec(); } + if (ast->type == AST_INTERFACE) + current_module->set_bool_attribute("\\is_interface"); current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; current_module->nomeminit = flag_nomeminit; @@ -1010,14 +1025,15 @@ static AstModule* process_module(AstNode *ast, bool defer) } // 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_vlog, bool dump_rtlil, +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 lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_no_dump_ptr = no_dump_ptr; - flag_dump_vlog = dump_vlog; + flag_dump_vlog1 = dump_vlog1; + flag_dump_vlog2 = dump_vlog2; flag_dump_rtlil = dump_rtlil; flag_nolatches = nolatches; flag_nomeminit = nomeminit; @@ -1031,7 +1047,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump log_assert(current_ast->type == AST_DESIGN); for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { - if ((*it)->type == AST_MODULE) + if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) { for (auto n : design->verilog_globals) (*it)->children.push_back(n->clone()); @@ -1053,8 +1069,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump if (design->has((*it)->str)) { RTLIL::Module *existing_mod = design->module((*it)->str); if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { - log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", - (*it)->str.c_str()); + log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str()); } else if (nooverwrite) { log("Ignoring re-definition of module `%s' at %s:%d.\n", (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); @@ -1083,8 +1098,264 @@ AstModule::~AstModule() delete ast; } + +// An interface port with modport is specified like this: +// <interface_name>.<modport_name> +// This function splits the interface_name from the modport_name, and fails if it is not a valid combination +std::pair<std::string,std::string> AST::split_modport_from_type(std::string name_type) +{ + std::string interface_type = ""; + std::string interface_modport = ""; + size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); + // Separate the interface instance name from any modports: + if (ndots == 0) { // Does not have modport + interface_type = name_type; + } + else { + std::stringstream name_type_stream(name_type); + std::string segment; + std::vector<std::string> seglist; + while(std::getline(name_type_stream, segment, '.')) { + seglist.push_back(segment); + } + if (ndots == 1) { // Has modport + interface_type = seglist[0]; + interface_modport = seglist[1]; + } + else { // Erroneous port type + log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); + } + } + return std::pair<std::string,std::string>(interface_type, interface_modport); + +} + +AstNode * AST::find_modport(AstNode *intf, std::string name) +{ + for (auto &ch : intf->children) + if (ch->type == AST_MODPORT) + if (ch->str == name) // Modport found + return ch; + return NULL; +} + +// Iterate over all wires in an interface and add them as wires in the AST module: +void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport) +{ + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string origname = log_id(wire_it.first); + std::string newname = intfname + "." + origname; + wire->str = newname; + if (modport != NULL) { + bool found_in_modport = false; + // Search for the current wire in the wire list for the current modport + for (auto &ch : modport->children) { + if (ch->type == AST_MODPORTMEMBER) { + std::string compare_name = "\\" + origname; + if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output + found_in_modport = true; + wire->is_input = ch->is_input; + wire->is_output = ch->is_output; + break; + } + } + } + if (found_in_modport) { + module_ast->children.push_back(wire); + } + else { // If not found in modport, do not create port + delete wire; + } + } + else { // If no modport, set inout + wire->is_input = true; + wire->is_output = true; + module_ast->children.push_back(wire); + } + } +} + +// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again +// from AST. The interface members are copied into the AST module with the prefix of the interface. +void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) +{ + bool is_top = false; + AstNode *new_ast = ast->clone(); + for (auto &intf : local_interfaces) { + std::string intfname = intf.first.str(); + RTLIL::Module *intfmodule = intf.second; + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string newname = log_id(wire_it.first); + newname = intfname + "." + newname; + wire->str = newname; + new_ast->children.push_back(wire); + } + } + + AstNode *ast_before_replacing_interface_ports = new_ast->clone(); + + // Explode all interface ports. Note this will only have an effect on 'top + // level' modules. Other sub-modules will have their interface ports + // exploded via the derive(..) function + for (size_t i =0; i<new_ast->children.size(); i++) + { + AstNode *ch2 = new_ast->children[i]; + if (ch2->type == AST_INTERFACEPORT) { // Is an interface port + std::string name_port = ch2->str; // Name of the interface port + if (ch2->children.size() > 0) { + for(size_t j=0; j<ch2->children.size();j++) { + AstNode *ch = ch2->children[j]; + if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface + std::pair<std::string,std::string> res = split_modport_from_type(ch->str); + std::string interface_type = res.first; + std::string interface_modport = res.second; // Is "", if no modport + if (design->modules_.count(interface_type) > 0) { + // Add a cell to the module corresponding to the interface port such that + // it can further propagated down if needed: + AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE); + celltype_for_intf->str = interface_type; + AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); + cell_for_intf->str = name_port + "_inst_from_top_dummy"; + new_ast->children.push_back(cell_for_intf); + + // Get all members of this non-overridden dummy interface instance: + RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming + // reprocess_module is called from the hierarchy pass) be + // present in design->modules_ + AstModule *ast_module_of_interface = (AstModule*)intfmodule; + std::string interface_modport_compare_str = "\\" + interface_modport; + AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport + // Iterate over all wires in the interface and add them to the module: + explode_interface_port(new_ast, intfmodule, name_port, modport); + } + break; + } + } + } + } + } + + // The old module will be deleted. Rename and mark for deletion: + std::string original_name = this->name.str(); + std::string changed_name = original_name + "_before_replacing_local_interfaces"; + design->rename(this, changed_name); + this->set_bool_attribute("\\to_delete"); + + // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the + // new module. + if (this->get_bool_attribute("\\initial_top")) { + this->attributes.erase("\\initial_top"); + is_top = true; + } + + // Generate RTLIL from AST for the new module and add to the design: + AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports); + delete(new_ast); + design->add(newmod); + RTLIL::Module* mod = design->module(original_name); + if (is_top) + mod->set_bool_attribute("\\top"); + + // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. + mod->set_bool_attribute("\\interfaces_replaced_in_module"); +} + +// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces +// This method is used to explode the interface when the interface is a port of the module (not instantiated inside) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + // Since interfaces themselves may be instantiated with different parameters, + // "modname" must also take those into account, so that unique modules + // are derived for any variant of interface connections: + std::string interf_info = ""; + + bool has_interfaces = false; + for(auto &intf : interfaces) { + interf_info += log_id(intf.second->name); + has_interfaces = true; + } + + if (has_interfaces) + modname += "$interfaces$" + interf_info; + + + if (!design->has(modname)) { + new_ast->str = modname; + + // Iterate over all interfaces which are ports in this module: + for(auto &intf : interfaces) { + RTLIL::Module * intfmodule = intf.second; + std::string intfname = intf.first.str(); + // Check if a modport applies for the interface port: + AstNode *modport = NULL; + if (modports.count(intfname) > 0) { + std::string interface_modport = modports.at(intfname).str(); + AstModule *ast_module_of_interface = (AstModule*)intfmodule; + AstNode *ast_node_of_interface = ast_module_of_interface->ast; + modport = find_modport(ast_node_of_interface, interface_modport); + } + // Iterate over all wires in the interface and add them to the module: + explode_interface_port(new_ast, intfmodule, intfname, modport); + } + + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + + RTLIL::Module* mod = design->module(modname); + + // Now that the interfaces have been exploded, we can delete the dummy port related to every interface. + for(auto &intf : interfaces) { + if(mod->wires_.count(intf.first)) { + mod->wires_.erase(intf.first); + mod->fixup_ports(); + // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated + // down to sub-sub-modules etc. + RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name); + new_subcell->set_bool_attribute("\\is_interface"); + } + else { + log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); + } + } + + // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module': + if (interfaces.size() > 0) { + mod->set_bool_attribute("\\interfaces_replaced_in_module"); + } + + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + +// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + if (!design->has(modname)) { + new_ast->str = modname; + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + // create a new parametric module (when needed) and return the name of the generated module -RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool) +std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool) { std::string stripped_name = name.str(); @@ -1096,7 +1367,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R current_ast = NULL; flag_dump_ast1 = false; flag_dump_ast2 = false; - flag_dump_vlog = false; + flag_dump_vlog1 = false; + flag_dump_vlog2 = false; flag_nolatches = nolatches; flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; @@ -1156,15 +1428,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R else modname = "$paramod" + stripped_name + para_info; - if (!design->has(modname)) { - new_ast->str = modname; - design->add(process_module(new_ast, false)); - design->module(modname)->check(); - } else { - log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); - } - delete new_ast; + (*new_ast_out) = new_ast; return modname; } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 7e97bdb3..ddd59d4b 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -142,6 +142,11 @@ namespace AST AST_NEGEDGE, AST_EDGE, + AST_INTERFACE, + AST_INTERFACEPORT, + AST_INTERFACEPORTTYPE, + AST_MODPORT, + AST_MODPORTMEMBER, AST_PACKAGE }; @@ -209,6 +214,8 @@ namespace AST MEM2REG_FL_SET_ASYNC = 0x00000800, MEM2REG_FL_EQ2 = 0x00001000, MEM2REG_FL_CMPLX_LHS = 0x00002000, + MEM2REG_FL_CONST_LHS = 0x00004000, + MEM2REG_FL_VAR_LHS = 0x00008000, /* proc flags */ MEM2REG_FL_EQ1 = 0x01000000, @@ -232,6 +239,7 @@ namespace AST bool has_const_only_constructs(bool &recommend_const_eval); void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); AstNode *eval_const_function(AstNode *fcall); + bool is_simple_const_expr(); // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; @@ -274,7 +282,7 @@ 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_vlog, bool dump_rtlil, bool nolatches, bool nomeminit, + 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 lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library @@ -284,6 +292,9 @@ namespace AST bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, 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; + std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail); + void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE; RTLIL::Module *clone() const YS_OVERRIDE; }; @@ -300,6 +311,11 @@ namespace AST // call a DPI function AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args); + + // Helper functions related to handling SystemVerilog interfaces + std::pair<std::string,std::string> split_modport_from_type(std::string name_type); + AstNode * find_modport(AstNode *intf, std::string name); + void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport); } namespace AST_INTERNAL diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 0f7e910f..b3a2a84b 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -55,8 +55,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi if (gen_attributes) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -89,8 +88,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s if (that != NULL) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -117,8 +115,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -152,8 +149,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -480,8 +476,7 @@ struct AST_INTERNAL::ProcessGenerator for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); sw->attributes[attr.first] = attr.second->asAttrConst(); } @@ -530,7 +525,16 @@ struct AST_INTERNAL::ProcessGenerator } if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) { + #if 0 + // this is a valid transformation, but as optimization it is premature. + // better: add a default case that assigns 'x' to everything, and let later + // optimizations take care of the rest last_generated_case->compare.clear(); + #else + default_case = new RTLIL::CaseRule; + addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue))); + sw->cases.push_back(default_case); + #endif } else { if (default_case == NULL) { default_case = new RTLIL::CaseRule; @@ -549,7 +553,11 @@ struct AST_INTERNAL::ProcessGenerator break; case AST_WIRE: - log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n"); + log_file_error(ast->filename, ast->linenum, "Found reg declaration in block without label!\n"); + break; + + case AST_ASSIGN: + log_file_error(ast->filename, ast->linenum, "Found continous assignment in always/initial block!\n"); break; case AST_PARAMETER: @@ -648,9 +656,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", - str.c_str()); - this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; delete left_at_zero_ast; delete right_at_zero_ast; } else @@ -778,7 +785,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { } if (children[0]->type != AST_CONSTANT) log_file_error(filename, linenum, "System function %s called with non-const argument!\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); width_hint = max(width_hint, int(children[0]->asInt(true))); } break; @@ -798,9 +805,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // everything should have been handled above -> print error if not. default: for (auto f : log_files) - current_ast->dumpAst(f, "verilog-ast> "); - log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", - type2str(type).c_str()); + current_ast_mod->dumpAst(f, "verilog-ast> "); + log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); } if (*found_real) @@ -853,6 +859,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: + case AST_MODPORT: + case AST_MODPORTMEMBER: + break; + case AST_INTERFACEPORT: { + // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' + // This is used by the hierarchy pass to know when it can replace interface connection with the individual + // signals. + RTLIL::Wire *wire = current_module->addWire(str, 1); + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->start_offset = 0; + wire->port_id = port_id; + wire->port_input = true; + wire->port_output = true; + wire->set_bool_attribute("\\is_interface"); + if (children.size() > 0) { + for(size_t i=0; i<children.size();i++) { + if(children[i]->type == AST_INTERFACEPORTTYPE) { + std::pair<std::string,std::string> res = AST::split_modport_from_type(children[i]->str); + wire->attributes["\\interface_type"] = res.first; + if (res.second != "") + wire->attributes["\\interface_modport"] = res.second; + break; + } + } + } + wire->upto = 0; + } + break; + case AST_INTERFACEPORTTYPE: break; // remember the parameter, needed for example in techmap @@ -863,11 +898,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Wire for an AST_WIRE node case AST_WIRE: { if (current_module->wires_.count(str) != 0) - log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", - str.c_str()); + log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", str.c_str()); if (!range_valid) - log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", - str.c_str()); + log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str()); log_assert(range_left >= range_right || (range_left == -1 && range_right == 0)); @@ -881,8 +914,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -891,16 +923,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Memory for an AST_MEMORY node case AST_MEMORY: { if (current_module->memories.count(str) != 0) - log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", - str.c_str()); + log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", str.c_str()); log_assert(children.size() >= 2); log_assert(children[0]->type == AST_RANGE); log_assert(children[1]->type == AST_RANGE); if (!children[0]->range_valid || !children[1]->range_valid) - log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", - str.c_str()); + log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); @@ -917,8 +947,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); memory->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -926,19 +955,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node case AST_CONSTANT: + case AST_REALVALUE: { if (width_hint < 0) detectSignWidth(width_hint, sign_hint); - is_signed = sign_hint; - return RTLIL::SigSpec(bitsAsConst()); - } - case AST_REALVALUE: - { + if (type == AST_CONSTANT) + return RTLIL::SigSpec(bitsAsConst()); + RTLIL::SigSpec sig = realAsConst(width_hint); - log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", - realvalue, log_signal(sig)); + log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); return sig; } @@ -949,6 +976,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::Wire *wire = NULL; RTLIL::SigChunk chunk; + bool is_interface = false; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -964,19 +992,42 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) { if (id2ast->children[0]->type != AST_CONSTANT) - log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", - str.c_str()); + log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str()); chunk = RTLIL::Const(id2ast->children[0]->bits); goto use_const_chunk; } - else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE && - id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0) - log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", - str.c_str()); + else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { + RTLIL::Wire *current_wire = current_module->wire(str); + if (current_wire->get_bool_attribute("\\is_interface")) + is_interface = true; + // Ignore + } + // If an identifier is found that is not already known, assume that it is an interface: + else if (1) { // FIXME: Check if sv_mode first? + is_interface = true; + } + else { + log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", str.c_str()); + } if (id2ast->type == AST_MEMORY) - log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", - str.c_str()); + log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); + + // If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface' + // This makes it possible for the hierarchy pass to see what are interface connections and then replace them + // with the individual signals: + if (is_interface) { + RTLIL::Wire *dummy_wire; + std::string dummy_wire_name = "$dummywireforinterface" + str; + if (current_module->wires_.count(dummy_wire_name)) + dummy_wire = current_module->wires_[dummy_wire_name]; + else { + dummy_wire = current_module->addWire(dummy_wire_name); + dummy_wire->set_bool_attribute("\\is_interface"); + } + RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire); + return tmp; + } wire = current_module->wires_[str]; chunk.wire = wire; @@ -985,7 +1036,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) use_const_chunk: if (children.size() != 0) { - log_assert(children[0]->type == AST_RANGE); + if (children[0]->type != AST_RANGE) + log_file_error(filename, linenum, "Single range expected.\n"); int source_width = id2ast->range_left - id2ast->range_right + 1; int source_offset = id2ast->range_right; if (!children[0]->range_valid) { @@ -994,9 +1046,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", - str.c_str()); - int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); fake_ast->children[0]->delete_children(); @@ -1024,10 +1075,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { if (chunk.width == 1) log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", - str.c_str()); + str.c_str()); else - log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting all %d result bits to undef.\n", - str.c_str(), chunk.width); + log_file_warning(filename, linenum, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { if (chunk.width + chunk.offset > source_width) { @@ -1040,11 +1091,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) - log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", - str.c_str(), add_undef_bits_lsb); + log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); if (add_undef_bits_msb) - log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", - str.c_str(), add_undef_bits_msb); + log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); } } } @@ -1371,16 +1422,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (GetSize(en) != 1) en = current_module->ReduceBool(NEW_ID, en); - std::stringstream sstr; - sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++); + IdString cellname; + if (str.empty()) { + std::stringstream sstr; + sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++); + cellname = sstr.str(); + } else { + cellname = str; + } - RTLIL::Cell *cell = current_module->addCell(sstr.str(), celltype); + RTLIL::Cell *cell = current_module->addCell(cellname, celltype); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); 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()); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -1402,9 +1458,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) new_right.append(right[i]); } log_file_warning(filename, linenum, "Ignoring assignment to constant bits:\n" - " old assignment: %s = %s\n new assignment: %s = %s.\n", - log_signal(left), log_signal(right), - log_signal(new_left), log_signal(new_right)); + " old assignment: %s = %s\n new assignment: %s = %s.\n", + log_signal(left), log_signal(right), + log_signal(new_left), log_signal(new_right)); left = new_left; right = new_right; } @@ -1422,6 +1478,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Cell *cell = current_module->addCell(str, ""); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass + cell->set_bool_attribute("\\module_not_derived"); for (auto it = children.begin(); it != children.end(); it++) { AstNode *child = *it; @@ -1435,14 +1493,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; if (child->children[0]->type == AST_REALVALUE) { log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n", - log_id(cell), log_id(paraname), child->children[0]->realvalue); + log_id(cell), log_id(paraname), child->children[0]->realvalue); auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); strnode->cloneInto(child->children[0]); delete strnode; } if (child->children[0]->type != AST_CONSTANT) log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n", - log_id(cell), log_id(paraname)); + log_id(cell), log_id(paraname)); cell->parameters[paraname] = child->children[0]->asParaConst(); continue; } @@ -1463,8 +1521,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } 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()); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1492,18 +1549,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (GetSize(children) > 1) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n", - RTLIL::unescape_id(str).c_str(), GetSize(children)); + RTLIL::unescape_id(str).c_str(), GetSize(children)); if (GetSize(children) == 1) { if (children[0]->type != AST_CONSTANT) log_file_error(filename, linenum, "System function %s called with non-const argument!\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); width = children[0]->asInt(true); } if (width <= 0) - log_file_error(filename, linenum, "Failed to detect width of %s!\n", - RTLIL::unescape_id(str).c_str()); + log_file_error(filename, linenum, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); @@ -1528,10 +1584,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // everything should have been handled above -> print error if not. default: for (auto f : log_files) - current_ast->dumpAst(f, "verilog-ast> "); + current_ast_mod->dumpAst(f, "verilog-ast> "); type_name = type2str(type); - log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", - type_name.c_str()); + log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str()); } return RTLIL::SigSpec(); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index aa3b982d..63b71b80 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -50,7 +50,6 @@ using namespace AST_INTERNAL; bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param) { static int recursion_counter = 0; - static pair<string, int> last_blocking_assignment_warn; static bool deep_recursion_warning = false; if (recursion_counter++ == 1000 && deep_recursion_warning) { @@ -71,8 +70,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (stage == 0) { - log_assert(type == AST_MODULE); - last_blocking_assignment_warn = pair<string, int>(); + log_assert(type == AST_MODULE || type == AST_INTERFACE); deep_recursion_warning = true; while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } @@ -113,6 +111,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) goto verbose_activate; + if ((memflags & AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & AstNode::MEM2REG_FL_VAR_LHS)) + goto verbose_activate; + // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); continue; @@ -137,9 +138,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int mem_width, mem_size, addr_bits; node->meminfo(mem_width, mem_size, addr_bits); + int data_range_left = node->children[0]->range_left; + int data_range_right = node->children[0]->range_right; + + if (node->children[0]->range_swapped) + std::swap(data_range_left, data_range_right); + for (int i = 0; i < mem_size; i++) { AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, - mkconst_int(mem_width-1, true), mkconst_int(0, true))); + mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); reg->str = stringf("%s[%d]", node->str.c_str(), i); reg->is_reg = true; reg->is_signed = node->is_signed; @@ -196,7 +203,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int nargs = GetSize(children); if (nargs < 1) log_file_error(filename, linenum, "System task `%s' got %d arguments, expected >= 1.\n", - str.c_str(), int(children.size())); + str.c_str(), int(children.size())); // First argument is the format string AstNode *node_string = children[0]; @@ -240,7 +247,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case 'X': if (next_arg >= GetSize(children)) log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", - cformat, str.c_str()); + cformat, str.c_str()); node_arg = children[next_arg++]; while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } @@ -325,6 +332,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; if (node->type == AST_WIRE) { + if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + for (auto c : node->children[0]->children) { + if (!c->is_simple_const_expr()) { + if (attributes.count("\\dynports")) + delete attributes.at("\\dynports"); + attributes["\\dynports"] = AstNode::mkconst_int(1, true); + } + } + } if (this_wire_scope.count(node->str) > 0) { AstNode *first_node = this_wire_scope[node->str]; if (first_node->is_input && node->is_reg) @@ -450,8 +466,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg) log_warning("wire '%s' is assigned in a block at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum); - if (type == AST_ASSIGN && children[0]->id2ast->is_reg) - log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum); + if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { + bool is_rand_reg = false; + if (children[1]->type == AST_FCALL) { + if (children[1]->str == "\\$anyconst") + is_rand_reg = true; + if (children[1]->str == "\\$anyseq") + is_rand_reg = true; + if (children[1]->str == "\\$allconst") + is_rand_reg = true; + if (children[1]->str == "\\$allseq") + is_rand_reg = true; + } + if (!is_rand_reg) + log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum); + } children[0]->was_checked = true; } break; @@ -629,6 +658,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { bool did_something_here = true; + bool backup_flag_autowire = flag_autowire; if ((type == AST_GENFOR || type == AST_FOR) && i >= 3) break; if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1) @@ -639,6 +669,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; if (type == AST_PREFIX && i >= 1) break; + if (type == AST_DEFPARAM && i == 0) + flag_autowire = true; while (did_something_here && i < children.size()) { bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; int width_hint_here = width_hint; @@ -673,6 +705,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children.erase(children.begin() + (i--)); did_something = true; } + flag_autowire = backup_flag_autowire; } for (auto &attr : attributes) { while (attr.second->simplify(true, false, false, stage, -1, false, true)) @@ -732,7 +765,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (current_scope.at(modname)->type != AST_CELL) log_file_error(filename, linenum, "Defparam argument `%s . %s` does not match a cell!\n", - RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); + RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL); paraset->str = paramname; @@ -880,7 +913,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (children[0]->type == AST_REALVALUE) { RTLIL::Const constvalue = children[0]->realAsConst(width); log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", - children[0]->realvalue, log_signal(constvalue)); + children[0]->realvalue, log_signal(constvalue)); delete children[0]; children[0] = mkconst_bits(constvalue.bits, sign_hint); did_something = true; @@ -921,12 +954,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } if (current_scope.count(str) == 0) { - // log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); - AstNode *auto_wire = new AstNode(AST_AUTOWIRE); - auto_wire->str = str; - current_ast_mod->children.push_back(auto_wire); - current_scope[str] = auto_wire; - did_something = true; + if (flag_autowire || str == "\\$global_clock") { + AstNode *auto_wire = new AstNode(AST_AUTOWIRE); + auto_wire->str = str; + current_ast_mod->children.push_back(auto_wire); + current_scope[str] = auto_wire; + did_something = true; + } else { + log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); + } } if (id2ast != current_scope[str]) { id2ast = current_scope[str]; @@ -946,6 +982,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int data_range_left = id2ast->children[0]->range_left; int data_range_right = id2ast->children[0]->range_right; + if (id2ast->children[0]->range_swapped) + std::swap(data_range_left, data_range_right); + std::stringstream sstr; sstr << "$mem2bits$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string wire_id = sstr.str(); @@ -1299,8 +1338,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_PRIMITIVE) { if (children.size() < 2) - log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n", - str.c_str()); + log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n", str.c_str()); std::vector<AstNode*> children_list; for (auto child : children) { @@ -1315,8 +1353,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1") { if (children_list.size() != 3) - log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n", - str.c_str()); + log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n", str.c_str()); std::vector<RTLIL::State> z_const(1, RTLIL::State::Sz); @@ -1336,6 +1373,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, str.clear(); type = AST_ASSIGN; children.push_back(children_list.at(0)); + children.back()->was_checked = true; children.push_back(node); did_something = true; } @@ -1372,6 +1410,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, str.clear(); type = AST_ASSIGN; children.push_back(children_list[0]); + children.back()->was_checked = true; children.push_back(node); did_something = true; } @@ -1401,8 +1440,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", - str.c_str()); + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } did_something = true; @@ -1480,6 +1518,7 @@ skip_dynamic_range_lvalue_expansion:; newNode->children.push_back(assign_en); AstNode *assertnode = new AstNode(type); + assertnode->str = str; assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children[0]->str = id_check; @@ -1530,6 +1569,7 @@ skip_dynamic_range_lvalue_expansion:; wire_tmp_id->str = wire_tmp->str; newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone())); + newNode->children.back()->was_checked = true; int cursor = 0; for (auto child : children[0]->children) @@ -1559,14 +1599,6 @@ skip_dynamic_range_lvalue_expansion:; sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; - if (type == AST_ASSIGN_EQ) { - pair<string, int> this_blocking_assignment_warn(filename, linenum); - if (this_blocking_assignment_warn != last_blocking_assignment_warn) - log_warning("Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", - filename.c_str(), linenum); - last_blocking_assignment_warn = this_blocking_assignment_warn; - } - int mem_width, mem_size, addr_bits; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1676,7 +1708,7 @@ skip_dynamic_range_lvalue_expansion:; while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); - int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); @@ -1765,18 +1797,18 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$past") { - if (width_hint <= 0) + if (width_hint < 0) goto replace_fcall_later; int num_steps = 1; if (GetSize(children) != 1 && GetSize(children) != 2) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); if (!current_always_clocked) log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); if (GetSize(children) == 2) { @@ -1797,6 +1829,11 @@ skip_dynamic_range_lvalue_expansion:; log_assert(block != nullptr); + if (num_steps == 0) { + newNode = children[0]->clone(); + goto apply_newNode; + } + int myidx = autoidx++; AstNode *outreg = nullptr; @@ -1815,6 +1852,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *regid = new AstNode(AST_IDENTIFIER); regid->str = reg->str; regid->id2ast = reg; + regid->was_checked = true; AstNode *rhs = nullptr; @@ -1836,15 +1874,15 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell") + if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed") { if (GetSize(children) != 1) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); if (!current_always_clocked) log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); AstNode *present = children.at(0)->clone(); AstNode *past = clone(); @@ -1853,11 +1891,18 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$stable") newNode = new AstNode(AST_EQ, past, present); + else if (str == "\\$changed") + newNode = new AstNode(AST_NE, past, present); + else if (str == "\\$rose") - newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present); + newNode = new AstNode(AST_LOGIC_AND, + new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, past, mkconst_int(1,false))), + new AstNode(AST_BIT_AND, present, mkconst_int(1,false))); else if (str == "\\$fell") - newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present)); + newNode = new AstNode(AST_LOGIC_AND, + new AstNode(AST_BIT_AND, past, mkconst_int(1,false)), + new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, present, mkconst_int(1,false)))); else log_abort(); @@ -1875,7 +1920,7 @@ skip_dynamic_range_lvalue_expansion:; { if (children.size() != 1) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *buf = children[0]->clone(); while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } @@ -1892,7 +1937,7 @@ skip_dynamic_range_lvalue_expansion:; if (arg_value.bits.at(i) == RTLIL::State::S1) result = i + 1; - newNode = mkconst_int(result, false); + newNode = mkconst_int(result, true); goto apply_newNode; } @@ -1900,11 +1945,11 @@ skip_dynamic_range_lvalue_expansion:; { if (str == "\\$bits" && children.size() != 1) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); if (str == "\\$size" && children.size() != 1 && children.size() != 2) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); int dim = 1; if (str == "\\$size" && children.size() == 2) { @@ -1978,18 +2023,18 @@ skip_dynamic_range_lvalue_expansion:; if (func_with_two_arguments) { if (children.size() != 2) log_file_error(filename, linenum, "System function %s got %d arguments, expected 2.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); } else { if (children.size() != 1) log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); } if (children.size() >= 1) { while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (!children[0]->isConst()) log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[0]->detectSignWidth(child_width_hint, child_sign_hint); @@ -2000,7 +2045,7 @@ skip_dynamic_range_lvalue_expansion:; while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (!children[1]->isConst()) log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n", - RTLIL::unescape_id(str).c_str()); + RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[1]->detectSignWidth(child_width_hint, child_sign_hint); @@ -2088,7 +2133,7 @@ skip_dynamic_range_lvalue_expansion:; { if (GetSize(children) < 2 || GetSize(children) > 4) log_file_error(filename, linenum, "System function %s got %d arguments, expected 2-4.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *node_filename = children[0]->clone(); while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } @@ -2136,6 +2181,8 @@ skip_dynamic_range_lvalue_expansion:; } newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); + delete node_filename; + delete node_memory; goto apply_newNode; } @@ -2177,6 +2224,8 @@ skip_dynamic_range_lvalue_expansion:; std::map<std::string, std::string> replace_rules; vector<AstNode*> added_mod_children; dict<std::string, AstNode*> wire_cache; + vector<AstNode*> new_stmts; + vector<AstNode*> output_assignments; if (current_block == NULL) { @@ -2201,6 +2250,8 @@ skip_dynamic_range_lvalue_expansion:; AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); + always->children[0]->children[0]->was_checked = true; + current_ast_mod->children.push_back(always); goto replace_fcall_with_id; @@ -2250,6 +2301,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); + assign->children[0]->was_checked = true; for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) @@ -2298,8 +2350,8 @@ skip_dynamic_range_lvalue_expansion:; wire->port_id = 0; wire->is_input = false; wire->is_output = false; - if (!child->is_output) - wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + wire->is_reg = true; + wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); wire_cache[child->str] = wire; current_ast_mod->children.push_back(wire); @@ -2320,13 +2372,11 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id, arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id); - - for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { - if (*it != current_block_child) - continue; - current_block->children.insert(it, assign); - break; - } + assign->children[0]->was_checked = true; + if (child->is_input) + new_stmts.push_back(assign); + else + output_assignments.push_back(assign); } } @@ -2340,14 +2390,18 @@ skip_dynamic_range_lvalue_expansion:; { AstNode *stmt = child->clone(); stmt->replace_ids(prefix, replace_rules); + new_stmts.push_back(stmt); + } - for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { - if (*it != current_block_child) - continue; - current_block->children.insert(it, stmt); - break; - } + new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); + + for (auto it = current_block->children.begin(); ; it++) { + log_assert(it != current_block->children.end()); + if (*it == current_block_child) { + current_block->children.insert(it, new_stmts.begin(), new_stmts.end()); + break; } + } replace_fcall_with_id: if (type == AST_FCALL) { @@ -2759,6 +2813,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); block->children.back()->children[0]->str = memory->str; block->children.back()->children[0]->id2ast = memory; + block->children.back()->children[0]->was_checked = true; } cursor += increment; @@ -2817,7 +2872,11 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; - if (child->type != AST_FUNCTION && child->type != AST_TASK && child->type != AST_PREFIX) + // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX + // still needs to recursed-into + if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER) + continue; + if (child->type != AST_FUNCTION && child->type != AST_TASK) child->expand_genblock(index_var, prefix, name_map); } @@ -2872,7 +2931,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags) { uint32_t children_flags = 0; - int ignore_children_counter = 0; + int lhs_children_counter = 0; if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) { @@ -2898,6 +2957,16 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } + // for proper (non-init) writes: remember if this is a constant index or not + if ((flags & MEM2REG_FL_INIT) == 0) { + if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) { + if (children[0]->children[0]->children[0]->type == AST_CONSTANT) + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS; + else + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS; + } + } + // remember where this is if (flags & MEM2REG_FL_INIT) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) @@ -2910,7 +2979,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg } } - ignore_children_counter = 1; + lhs_children_counter = 1; } if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY) @@ -2953,12 +3022,23 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg log_assert((flags & ~0x000000ff) == 0); for (auto child : children) - if (ignore_children_counter > 0) - ignore_children_counter--; - else if (proc_flags_p) + { + if (lhs_children_counter > 0) { + lhs_children_counter--; + if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) { + for (auto c : child->children[0]->children) { + if (proc_flags_p) + c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); + else + c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); + } + } + } else + if (proc_flags_p) child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); else child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); + } flags &= ~children_flags | backup_flags; @@ -3010,6 +3090,39 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, if (type == AST_FUNCTION || type == AST_TASK) return false; + if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) + { + log_assert(children[0]->type == AST_CONSTANT); + log_assert(children[1]->type == AST_CONSTANT); + log_assert(children[2]->type == AST_CONSTANT); + + int cursor = children[0]->asInt(false); + Const data = children[1]->bitsAsConst(); + int length = children[2]->asInt(false); + + if (length != 0) + { + AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK)); + mod->children.push_back(block); + block = block->children[0]; + + int wordsz = GetSize(data) / length; + + for (int i = 0; i < length; i++) { + block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false))); + block->children.back()->children[0]->str = str; + block->children.back()->children[0]->id2ast = id2ast; + block->children.back()->children[0]->was_checked = true; + } + } + + AstNode *newNode = new AstNode(AST_NONE); + newNode->cloneInto(this); + delete newNode; + + did_something = true; + } + if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set)) { if (async_block == NULL) { @@ -3019,6 +3132,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *newNode = clone(); newNode->type = AST_ASSIGN_EQ; + newNode->children[0]->was_checked = true; async_block->children[0]->children.push_back(newNode); newNode = new AstNode(AST_NONE); @@ -3064,6 +3178,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; + assign_addr->children[0]->was_checked = true; block->children.insert(block->children.begin()+assign_idx+1, assign_addr); AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); @@ -3087,6 +3202,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, children[0]->id2ast = NULL; children[0]->str = id_data; type = AST_ASSIGN_EQ; + children[0]->was_checked = true; did_something = true; } @@ -3236,6 +3352,16 @@ bool AstNode::has_const_only_constructs(bool &recommend_const_eval) return false; } +bool AstNode::is_simple_const_expr() +{ + if (type == AST_IDENTIFIER) + return false; + for (auto child : children) + if (!child->is_simple_const_expr()) + return false; + return true; +} + // helper function for AstNode::eval_const_function() void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) { @@ -3244,12 +3370,12 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia if (!children.empty()) { if (children.size() != 1 || children.at(0)->type != AST_RANGE) log_file_error(filename, linenum, "Memory access in constant function is not supported\n%s:%d: ...called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); children.at(0)->replace_variables(variables, fcall); while (simplify(true, false, false, 1, -1, false, true)) { } if (!children.at(0)->range_valid) log_file_error(filename, linenum, "Non-constant range\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); offset = min(children.at(0)->range_left, children.at(0)->range_right); width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); } @@ -3289,7 +3415,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) while (child->simplify(true, false, false, 1, -1, false, true)) { } if (!child->range_valid) log_file_error(child->filename, child->linenum, "Can't determine size of variable %s\n%s:%d: ... called from here.\n", - child->str.c_str(), fcall->filename.c_str(), fcall->linenum); + child->str.c_str(), fcall->filename.c_str(), fcall->linenum); variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); variables[child->str].offset = min(child->range_left, child->range_right); variables[child->str].is_signed = child->is_signed; @@ -3333,15 +3459,15 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->children.at(1)->type != AST_CONSTANT) log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here. X\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); if (stmt->children.at(0)->type != AST_IDENTIFIER) log_file_error(stmt->filename, stmt->linenum, "Unsupported composite left hand side in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); if (!variables.count(stmt->children.at(0)->str)) log_file_error(stmt->filename, stmt->linenum, "Assignment to non-local variable in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); if (stmt->children.at(0)->children.empty()) { variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); @@ -3349,7 +3475,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) AstNode *range = stmt->children.at(0)->children.at(0); if (!range->range_valid) log_file_error(range->filename, range->linenum, "Non-constant range\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; @@ -3381,7 +3507,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (cond->type != AST_CONSTANT) log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -3402,7 +3528,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (num->type != AST_CONSTANT) log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -3440,7 +3566,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (cond->type != AST_CONSTANT) log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); found_match = cond->asBool(); delete cond; @@ -3470,7 +3596,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) } log_file_error(stmt->filename, stmt->linenum, "Unsupported language construct in constant function\n%s:%d: ... called from here.\n", - fcall->filename.c_str(), fcall->linenum); + fcall->filename.c_str(), fcall->linenum); log_abort(); } diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 034b3e70..a6a07863 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -276,7 +276,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo if(lastcell == nullptr || module == nullptr) { - err_reason = stringf("No primative object to attach .cname %s.", p); + err_reason = stringf("No primitive object to attach .cname %s.", p); goto error_with_reason; } @@ -584,7 +584,7 @@ struct BlifFrontend : public Frontend { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" read_blif [filename]\n"); + log(" read_blif [options] [filename]\n"); log("\n"); log("Load modules from a BLIF file into the current design.\n"); log("\n"); diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc index d8783ac1..6b302a79 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -44,11 +44,39 @@ struct IlangFrontend : public Frontend { log("Load modules from an ilang file to the current design. (ilang is a text\n"); log("representation of a design in yosys's internal format.)\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 blackbox\n"); + log(" module, and overwrite the existing module if it is a blackbox module.)\n"); + log("\n"); + log(" -overwrite\n"); + log(" overwrite existing modules with the same name\n"); + log("\n"); } void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = false; + log_header(design, "Executing ILANG frontend.\n"); - extra_args(f, filename, args, 1); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nooverwrite") { + ILANG_FRONTEND::flag_nooverwrite = true; + ILANG_FRONTEND::flag_overwrite = false; + continue; + } + if (arg == "-overwrite") { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + log("Input filename: %s\n", filename.c_str()); ILANG_FRONTEND::lexin = f; diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h index ad3ffec9..052dd4cb 100644 --- a/frontends/ilang/ilang_frontend.h +++ b/frontends/ilang/ilang_frontend.h @@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { extern std::istream *lexin; extern RTLIL::Design *current_design; + extern bool flag_nooverwrite; + extern bool flag_overwrite; } YOSYS_NAMESPACE_END diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index bfc062fe..5bcc01f4 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -37,6 +37,8 @@ namespace ILANG_FRONTEND { std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack; std::vector<RTLIL::CaseRule*> case_stack; dict<RTLIL::IdString, RTLIL::Const> attrbuf; + bool flag_nooverwrite, flag_overwrite; + bool delete_current_module; } using namespace ILANG_FRONTEND; YOSYS_NAMESPACE_END @@ -93,18 +95,36 @@ design: module: TOK_MODULE TOK_ID EOL { - if (current_design->has($2)) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + delete_current_module = false; + if (current_design->has($2)) { + RTLIL::Module *existing_mod = current_design->module($2); + if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) { + log("Ignoring blackbox re-definition of module %s.\n", $2); + delete_current_module = true; + } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + } else if (flag_nooverwrite) { + log("Ignoring re-definition of module %s.\n", $2); + delete_current_module = true; + } else { + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2); + current_design->remove(existing_mod); + } + } current_module = new RTLIL::Module; current_module->name = $2; current_module->attributes = attrbuf; - current_design->add(current_module); + if (!delete_current_module) + current_design->add(current_module); attrbuf.clear(); free($2); } module_body TOK_END { if (attrbuf.size() != 0) rtlil_frontend_ilang_yyerror("dangling attribute"); current_module->fixup_ports(); + if (delete_current_module) + delete current_module; + current_module = nullptr; } EOL; module_body: @@ -387,17 +407,13 @@ sigspec: $$ = new RTLIL::SigSpec(current_module->wires_[$1]); free($1); } | - TOK_ID '[' TOK_INT ']' { - if (current_module->wires_.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); - $$ = new RTLIL::SigSpec(current_module->wires_[$1], $3); - free($1); + sigspec '[' TOK_INT ']' { + $$ = new RTLIL::SigSpec($1->extract($3)); + delete $1; } | - TOK_ID '[' TOK_INT ':' TOK_INT ']' { - if (current_module->wires_.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); - $$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1); - free($1); + sigspec '[' TOK_INT ':' TOK_INT ']' { + $$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1)); + delete $1; } | '{' sigspec_list '}' { $$ = $2; diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 0a5bd84d..6e3cffac 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -36,7 +36,8 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *& int id_len = 0; while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') || - ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++; + ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || + expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++; if (id_len == 0) log_error("Expected identifier at `%s'.\n", expr); @@ -615,7 +616,7 @@ struct LibertyFrontend : public Frontend { LibertyAst *bus_type_node = node->find("bus_type"); if (!bus_type_node || !type_map.count(bus_type_node->value)) - log_error("Unkown or unsupported type for bus interface %s on cell %s.\n", + log_error("Unknown or unsupported type for bus interface %s on cell %s.\n", node->args.at(0).c_str(), log_id(cell_name)); int bus_type_width = std::get<0>(type_map.at(bus_type_node->value)); @@ -634,9 +635,12 @@ struct LibertyFrontend : public Frontend { } } - for (auto node : cell->children) + if (!flag_lib) { - if (!flag_lib) { + // some liberty files do not put ff/latch at the beginning of a cell + // try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes + for (auto node : cell->children) + { if (node->id == "ff" && node->args.size() == 2) create_ff(module, node); if (node->id == "latch" && node->args.size() == 2) @@ -645,7 +649,10 @@ struct LibertyFrontend : public Frontend { goto skip_cell; } } + } + for (auto node : cell->children) + { if (node->id == "pin" && node->args.size() == 1) { LibertyAst *dir = node->find("direction"); diff --git a/frontends/verific/README b/frontends/verific/README index b4c436a3..89584f2e 100644 --- a/frontends/verific/README +++ b/frontends/verific/README @@ -4,35 +4,6 @@ This directory contains Verific bindings for Yosys. See http://www.verific.com/ for details. -Building Yosys with the 32 bit Verific eval library on amd64: -============================================================= - -1.) Use a Makefile.conf like the following one: - ---snip-- -CONFIG := gcc -ENABLE_TCL := 0 -ENABLE_PLUGINS := 0 -ENABLE_VERIFIC := 1 -CXXFLAGS += -m32 -LDFLAGS += -m32 -VERIFIC_DIR = /usr/local/src/verific_lib_eval ---snap-- - - -2.) Install the necessary multilib packages - -Hint: On debian/ubuntu the multilib packages have names such as -libreadline-dev:i386 or lib32readline6-dev, depending on the -exact version of debian/ubuntu you are working with. - - -3.) Build and test - -make -j8 -./yosys -p 'verific -sv frontends/verific/example.sv; verific -import top' - - Verific Features that should be enabled in your Verific library =============================================================== @@ -50,7 +21,7 @@ Then run in the following command in this directory: sby -f example.sby -This will generate approximately one page of text outpout. The last lines +This will generate approximately one page of text output. The last lines should be something like this: SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index c5fa5831..ed9727b8 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -118,6 +118,27 @@ RTLIL::SigBit VerificImporter::net_map_at(Net *net) return net_map.at(net); } +bool is_blackbox(Netlist *nl) +{ + if (nl->IsBlackBox()) + return true; + + const char *attr = nl->GetAttValue("blackbox"); + if (attr != nullptr && strcmp(attr, "0")) + return true; + + return false; +} + +RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) +{ + std::string s = stringf("$verific$%s", obj->Name()); + if (obj->Linefile()) + s += stringf("$%s:%d", Verific::LineFile::GetFileName(obj->Linefile()), Verific::LineFile::GetLineNo(obj->Linefile())); + s += stringf("$%d", autoidx++); + return s; +} + void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj) { MapIter mi; @@ -203,7 +224,7 @@ RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*, dummy_wire = NULL; } else { if (dummy_wire == NULL) - dummy_wire = module->addWire(NEW_ID); + dummy_wire = module->addWire(new_verific_id(inst)); else dummy_wire->width++; sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1)); @@ -219,8 +240,8 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr } if (inst->Type() == PRIM_NAND) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addAndGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + module->addAndGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } @@ -231,8 +252,8 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr } if (inst->Type() == PRIM_NOR) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addOrGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + module->addOrGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } @@ -272,16 +293,16 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr if (inst->Type() == PRIM_FADD) { RTLIL::SigSpec a = net_map_at(inst->GetInput1()), b = net_map_at(inst->GetInput2()), c = net_map_at(inst->GetCin()); - RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(NEW_ID); - RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID); - RTLIL::SigSpec tmp1 = module->addWire(NEW_ID); - RTLIL::SigSpec tmp2 = module->addWire(NEW_ID); - RTLIL::SigSpec tmp3 = module->addWire(NEW_ID); - module->addXorGate(NEW_ID, a, b, tmp1); + RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(new_verific_id(inst)); + RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp1 = module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp2 = module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp3 = module->addWire(new_verific_id(inst)); + module->addXorGate(new_verific_id(inst), a, b, tmp1); module->addXorGate(inst_name, tmp1, c, y); - module->addAndGate(NEW_ID, tmp1, c, tmp2); - module->addAndGate(NEW_ID, a, b, tmp3); - module->addOrGate(NEW_ID, tmp2, tmp3, x); + module->addAndGate(new_verific_id(inst), tmp1, c, tmp2); + module->addAndGate(new_verific_id(inst), a, b, tmp3); + module->addOrGate(new_verific_id(inst), tmp2, tmp3, x); return true; } @@ -308,63 +329,78 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdString inst_name) { + RTLIL::Cell *cell = nullptr; + if (inst->Type() == PRIM_AND) { - module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + cell = module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NAND) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addAnd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); - module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + cell = module->addAnd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + import_attributes(cell->attributes, inst); + cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_OR) { - module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + cell = module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NOR) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addOr(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); - module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + cell = module->addOr(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + import_attributes(cell->attributes, inst); + cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XOR) { - module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + cell = module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XNOR) { - module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + cell = module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_INV) { - module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + cell = module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_MUX) { - module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + cell = module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_TRI) { - module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_FADD) { - RTLIL::SigSpec a_plus_b = module->addWire(NEW_ID, 2); - RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID); + RTLIL::SigSpec a_plus_b = module->addWire(new_verific_id(inst), 2); + RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); if (inst->GetCout()) y.append(net_map_at(inst->GetCout())); - module->addAdd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b); - module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y); + cell = module->addAdd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b); + import_attributes(cell->attributes, inst); + cell = module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y); + import_attributes(cell->attributes, inst); return true; } @@ -375,24 +411,26 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr log_assert(clocking.body_net == nullptr); if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) - clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else if (inst->GetSet()->IsGnd()) - clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0); + cell = clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0); else if (inst->GetReset()->IsGnd()) - clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1); + cell = clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1); else - clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + cell = clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_DLATCHRS) { if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) - module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else - module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } @@ -408,37 +446,45 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr if (inst->GetCout() != NULL) out.append(net_map_at(inst->GetCout())); if (inst->GetCin()->IsGnd()) { - module->addAdd(inst_name, IN1, IN2, out, SIGNED); + cell = module->addAdd(inst_name, IN1, IN2, out, SIGNED); + import_attributes(cell->attributes, inst); } else { - RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out)); - module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED); - module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst), GetSize(out)); + cell = module->addAdd(new_verific_id(inst), IN1, IN2, tmp, SIGNED); + import_attributes(cell->attributes, inst); + cell = module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false); + import_attributes(cell->attributes, inst); } return true; } if (inst->Type() == OPER_MULTIPLIER) { - module->addMul(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addMul(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_DIVIDER) { - module->addDiv(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addDiv(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MODULO) { - module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REMAINDER) { - module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SHIFT_LEFT) { - module->addShl(inst_name, IN1, IN2, OUT, false); + cell = module->addShl(inst_name, IN1, IN2, OUT, false); + import_attributes(cell->attributes, inst); return true; } @@ -448,7 +494,8 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr for (unsigned i = 1; i < inst->OutputSize(); i++) { vec.append(RTLIL::State::S0); } - module->addShl(inst_name, vec, IN, OUT, false); + cell = module->addShl(inst_name, vec, IN, OUT, false); + import_attributes(cell->attributes, inst); return true; } @@ -458,7 +505,8 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr for (unsigned i = 1; i < inst->OutputSize(); i++) { vec.append(RTLIL::State::S0); } - module->addShl(inst_name, vec, IN, OUT, false); + cell = module->addShl(inst_name, vec, IN, OUT, false); + import_attributes(cell->attributes, inst); return true; } @@ -466,108 +514,127 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr Net *net_cin = inst->GetCin(); Net *net_a_msb = inst->GetInput1Bit(0); if (net_cin->IsGnd()) - module->addShr(inst_name, IN1, IN2, OUT, false); + cell = module->addShr(inst_name, IN1, IN2, OUT, false); else if (net_cin == net_a_msb) - module->addSshr(inst_name, IN1, IN2, OUT, true); + cell = module->addSshr(inst_name, IN1, IN2, OUT, true); else log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name()); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_AND) { - module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_OR) { - module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XOR) { - module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XNOR) { - module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_NOR) { - SigSpec t = module->ReduceOr(NEW_ID, IN, SIGNED); - module->addNot(inst_name, t, net_map_at(inst->GetOutput())); + SigSpec t = module->ReduceOr(new_verific_id(inst), IN, SIGNED); + cell = module->addNot(inst_name, t, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_LESSTHAN) { Net *net_cin = inst->GetCin(); if (net_cin->IsGnd()) - module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else if (net_cin->IsPwr()) - module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name()); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_AND) { - module->addAnd(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addAnd(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_OR) { - module->addOr(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addOr(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XOR) { - module->addXor(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addXor(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XNOR) { - module->addXnor(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addXnor(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_BUF) { - module->addPos(inst_name, IN, FILTERED_OUT, SIGNED); + cell = module->addPos(inst_name, IN, FILTERED_OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_INV) { - module->addNot(inst_name, IN, OUT, SIGNED); + cell = module->addNot(inst_name, IN, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MINUS) { - module->addSub(inst_name, IN1, IN2, OUT, SIGNED); + cell = module->addSub(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_UMINUS) { - module->addNeg(inst_name, IN, OUT, SIGNED); + cell = module->addNeg(inst_name, IN, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_EQUAL) { - module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_NEQUAL) { - module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + cell = module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_MUX) { - module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT); + cell = module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_NTO1MUX) { - module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); + cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } @@ -587,25 +654,29 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr padded_data.append(d); } - module->addShr(inst_name, padded_data, sel, out); + cell = module->addShr(inst_name, padded_data, sel, out); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SELECTOR) { - module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput())); + cell = module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_SELECTOR) { SigSpec out = OUT; - module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out); + cell = module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_TRI) { - module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT); + cell = module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT); + import_attributes(cell->attributes, inst); return true; } @@ -619,9 +690,10 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr RTLIL::SigSpec sig_reset = operatorInport(inst, "reset"); if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool()) - clocking.addDff(inst_name, IN, OUT); + cell = clocking.addDff(inst_name, IN, OUT); else - clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT); + cell = clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT); + import_attributes(cell->attributes, inst); return true; } @@ -709,7 +781,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se netlist = nl; if (design->has(module_name)) { - if (!nl->IsOperator()) + if (!nl->IsOperator() && !is_blackbox(nl)) log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name()); return; } @@ -718,7 +790,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se module->name = module_name; design->add(module); - if (nl->IsBlackBox()) { + if (is_blackbox(nl)) { log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name)); module->set_bool_attribute("\\blackbox"); } else { @@ -850,7 +922,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se ascii_initdata++; } if (initval_valid) { - RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit"); + RTLIL::Cell *cell = module->addCell(new_verific_id(net), "$meminit"); cell->parameters["\\WORDS"] = 1; if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound()) cell->setPort("\\ADDR", word_idx); @@ -913,7 +985,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (net->Bus()) continue; - RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : NEW_ID); + RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : new_verific_id(net)); if (verific_verbose) log(" importing net %s as %s.\n", net->Name(), log_id(wire_name)); @@ -937,7 +1009,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (found_new_net) { - RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : NEW_ID); + RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : new_verific_id(netbus)); if (verific_verbose) log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name)); @@ -1013,16 +1085,16 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } if (GetSize(anyconst_sig)) - module->connect(anyconst_sig, module->Anyconst(NEW_ID, GetSize(anyconst_sig))); + module->connect(anyconst_sig, module->Anyconst(new_verific_id(netbus), GetSize(anyconst_sig))); if (GetSize(anyseq_sig)) - module->connect(anyseq_sig, module->Anyseq(NEW_ID, GetSize(anyseq_sig))); + module->connect(anyseq_sig, module->Anyseq(new_verific_id(netbus), GetSize(anyseq_sig))); if (GetSize(allconst_sig)) - module->connect(allconst_sig, module->Allconst(NEW_ID, GetSize(allconst_sig))); + module->connect(allconst_sig, module->Allconst(new_verific_id(netbus), GetSize(allconst_sig))); if (GetSize(allseq_sig)) - module->connect(allseq_sig, module->Allseq(NEW_ID, GetSize(allseq_sig))); + module->connect(allseq_sig, module->Allseq(new_verific_id(netbus), GetSize(allseq_sig))); } for (auto it : init_nets) @@ -1046,10 +1118,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } for (auto net : anyconst_nets) - module->connect(net_map_at(net), module->Anyconst(NEW_ID)); + module->connect(net_map_at(net), module->Anyconst(new_verific_id(net))); for (auto net : anyseq_nets) - module->connect(net_map_at(net), module->Anyseq(NEW_ID)); + module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); pool<Instance*, hash_ptr_ops> sva_asserts; pool<Instance*, hash_ptr_ops> sva_assumes; @@ -1060,7 +1132,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) { - RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : NEW_ID); + RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : new_verific_id(inst)); if (verific_verbose) log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name)); @@ -1128,27 +1200,34 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT) { RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name())); - if (memory->width != int(inst->Input2Size())) - log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name()); + int numchunks = int(inst->Input2Size()) / memory->width; + int chunksbits = ceil_log2(numchunks); + + if ((numchunks * memory->width) != int(inst->Input2Size()) || (numchunks & (numchunks - 1)) != 0) + log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name()); + + for (int i = 0; i < numchunks; i++) + { + RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; + RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width); + + RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : + RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$memwr"); + cell->parameters["\\MEMID"] = memory->name.str(); + cell->parameters["\\CLK_ENABLE"] = false; + cell->parameters["\\CLK_POLARITY"] = true; + cell->parameters["\\PRIORITY"] = 0; + cell->parameters["\\ABITS"] = GetSize(addr); + cell->parameters["\\WIDTH"] = GetSize(data); + cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); + cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\ADDR", addr); + cell->setPort("\\DATA", data); - RTLIL::SigSpec addr = operatorInput1(inst); - RTLIL::SigSpec data = operatorInput2(inst); - - RTLIL::Cell *cell = module->addCell(inst_name, "$memwr"); - cell->parameters["\\MEMID"] = memory->name.str(); - cell->parameters["\\CLK_ENABLE"] = false; - cell->parameters["\\CLK_POLARITY"] = true; - cell->parameters["\\PRIORITY"] = 0; - cell->parameters["\\ABITS"] = GetSize(addr); - cell->parameters["\\WIDTH"] = GetSize(data); - cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); - cell->setPort("\\CLK", RTLIL::State::S0); - cell->setPort("\\ADDR", addr); - cell->setPort("\\DATA", data); - - if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { - cell->parameters["\\CLK_ENABLE"] = true; - cell->setPort("\\CLK", net_map_at(inst->GetClock())); + if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { + cell->parameters["\\CLK_ENABLE"] = true; + cell->setPort("\\CLK", net_map_at(inst->GetClock())); + } } continue; } @@ -1184,7 +1263,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se log_assert(inst->Input1Size() == inst->OutputSize()); SigSpec sig_d, sig_q, sig_o; - sig_q = module->addWire(NEW_ID, inst->Input1Size()); + sig_q = module->addWire(new_verific_id(inst), inst->Input1Size()); for (int i = int(inst->Input1Size())-1; i >= 0; i--){ sig_d.append(net_map_at(inst->GetInput1Bit(i))); @@ -1198,8 +1277,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); } - clocking.addDff(NEW_ID, sig_d, sig_q); - module->addXnor(NEW_ID, sig_d, sig_q, sig_o); + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); if (!mode_keep) continue; @@ -1213,7 +1292,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se SigSpec sig_d = net_map_at(inst->GetInput1()); SigSpec sig_o = net_map_at(inst->GetOutput()); - SigSpec sig_q = module->addWire(NEW_ID); + SigSpec sig_q = module->addWire(new_verific_id(inst)); if (verific_verbose) { log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", @@ -1222,8 +1301,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); } - clocking.addDff(NEW_ID, sig_d, sig_q); - module->addXnor(NEW_ID, sig_d, sig_q, sig_o); + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); if (!mode_keep) continue; @@ -1242,7 +1321,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); - past_ffs.insert(clocking.addDff(NEW_ID, sig_d, sig_q)); + past_ffs.insert(clocking.addDff(new_verific_id(inst), sig_d, sig_q)); if (!mode_keep) continue; @@ -1256,14 +1335,14 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se SigBit sig_d = net_map_at(inst->GetInput1()); SigBit sig_o = net_map_at(inst->GetOutput()); - SigBit sig_q = module->addWire(NEW_ID); + SigBit sig_q = module->addWire(new_verific_id(inst)); if (verific_verbose) log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); - clocking.addDff(NEW_ID, sig_d, sig_q); - module->addEq(NEW_ID, {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o); + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addEq(new_verific_id(inst), {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o); if (!mode_keep) continue; @@ -1286,9 +1365,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se Cell *cell = nullptr; if (assume_attr != nullptr && !strcmp(assume_attr, "1")) - cell = module->addAssume(NEW_ID, cond, State::S1); + cell = module->addAssume(new_verific_id(inst), cond, State::S1); else - cell = module->addAssert(NEW_ID, cond, State::S1); + cell = module->addAssert(new_verific_id(inst), cond, State::S1); import_attributes(cell->attributes, inst); continue; @@ -1330,7 +1409,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se IdString port_name_id = RTLIL::escape_id(port_name); auto &sigvec = cell_port_conns[port_name_id]; if (GetSize(sigvec) <= port_offset) { - SigSpec zwires = module->addWire(NEW_ID, port_offset+1-GetSize(sigvec)); + SigSpec zwires = module->addWire(new_verific_id(inst), port_offset+1-GetSize(sigvec)); for (auto bit : zwires) sigvec.push_back(bit); } @@ -1540,30 +1619,35 @@ struct VerificExtNets int portname_cnt = 0; // a map from Net to the same Net one level up in the design hierarchy - std::map<Net*, Net*> net_level_up; + std::map<Net*, Net*> net_level_up_drive_up; + std::map<Net*, Net*> net_level_up_drive_down; - Net *get_net_level_up(Net *net) + Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr) { + auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down; + if (net_level_up.count(net) == 0) { Netlist *nl = net->Owner(); // Simply return if Netlist is not unique - if (nl->NumOfRefs() != 1) - return net; + log_assert(nl->NumOfRefs() == 1); Instance *up_inst = (Instance*)nl->GetReferences()->GetLast(); Netlist *up_nl = up_inst->Owner(); // create new Port string name = stringf("___extnets_%d", portname_cnt++); - Port *new_port = new Port(name.c_str(), DIR_OUT); + Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN); nl->Add(new_port); net->Connect(new_port); // create new Net in up Netlist - Net *new_net = new Net(name.c_str()); - up_nl->Add(new_net); + Net *new_net = final_net; + if (new_net == nullptr || new_net->Owner() != up_nl) { + new_net = new Net(name.c_str()); + up_nl->Add(new_net); + } up_inst->Connect(new_port, new_net); net_level_up[net] = new_net; @@ -1572,6 +1656,39 @@ struct VerificExtNets return net_level_up.at(net); } + Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr) + { + while (net->Owner() != dest) + net = route_up(net, drive_up, final_net); + if (final_net != nullptr) + log_assert(net == final_net); + return net; + } + + Netlist *find_common_ancestor(Netlist *A, Netlist *B) + { + std::set<Netlist*> ancestors_of_A; + + Netlist *cursor = A; + while (1) { + ancestors_of_A.insert(cursor); + if (cursor->NumOfRefs() != 1) + break; + cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); + } + + cursor = B; + while (1) { + if (ancestors_of_A.count(cursor)) + return cursor; + if (cursor->NumOfRefs() != 1) + break; + cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); + } + + log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str()); + } + void run(Netlist *nl) { MapIter mi, mi2; @@ -1595,19 +1712,37 @@ struct VerificExtNets if (verific_verbose) log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name()); - while (net->IsExternalTo(nl)) - { - Net *newnet = get_net_level_up(net); - if (newnet == net) break; + Netlist *ext_nl = net->Owner(); + + if (verific_verbose) + log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str()); + + Netlist *ca_nl = find_common_ancestor(nl, ext_nl); + + if (verific_verbose) + log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str()); + Net *ca_net = route_up(net, !port->IsOutput(), ca_nl); + Net *new_net = ca_net; + + if (ca_nl != nl) + { if (verific_verbose) - log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name()); - net = newnet; + log(" net in common ancestor: %s\n", ca_net->Name()); + + string name = stringf("___extnets_%d", portname_cnt++); + new_net = new Net(name.c_str()); + nl->Add(new_net); + + Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net); + log_assert(n == ca_net); } if (verific_verbose) - log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : ""); - todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, net)); + log(" new local net: %s\n", new_net->Name()); + + log_assert(!new_net->IsExternalTo(nl)); + todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, new_net)); } for (auto it : todo_connect) { @@ -1676,6 +1811,7 @@ YOSYS_NAMESPACE_END PRIVATE_NAMESPACE_BEGIN +#ifdef YOSYS_ENABLE_VERIFIC bool check_noverific_env() { const char *e = getenv("YOSYS_NOVERIFIC"); @@ -1685,6 +1821,7 @@ bool check_noverific_env() return false; return true; } +#endif struct VerificPass : public Pass { VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { } @@ -1774,6 +1911,13 @@ struct VerificPass : public Pass { log(" -autocover\n"); log(" Generate automatic cover statements for all asserts\n"); log("\n"); + log(" -chparam name value \n"); + log(" Elaborate the specified top modules (all modules when -all given) using\n"); + log(" this parameter value. Modules on which this parameter does not exist will\n"); + log(" cause Verific to produce a VERI-1928 or VHDL-1676 message. This option\n"); + log(" can be specified multiple times to override multiple parameters.\n"); + log(" String values must be passed in double quotes (\").\n"); + log("\n"); log(" -v, -vv\n"); log(" Verbose log messages. (-vv is even more verbose than -v.)\n"); log("\n"); @@ -1819,12 +1963,19 @@ struct VerificPass : public Pass { { Message::SetConsoleOutput(0); Message::RegisterCallBackMsg(msg_func); + RuntimeFlags::SetVar("db_preserve_user_nets", 1); RuntimeFlags::SetVar("db_allow_external_nets", 1); - RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); + RuntimeFlags::SetVar("db_infer_wide_operators", 1); + RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); - RuntimeFlags::SetVar("db_infer_wide_operators", 1); + + RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0); + RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1); + + RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); + RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); // Workaround for VIPER #13851 RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); @@ -1832,6 +1983,10 @@ struct VerificPass : public Pass { // WARNING: instantiating unknown module 'XYZ' (VERI-1063) Message::SetMessageType("VERI-1063", VERIFIC_ERROR); +#ifndef DB_PRESERVE_INITIAL_VALUE +# warning Verific was built without DB_PRESERVE_INITIAL_VALUE. +#endif + set_verific_global_flags = false; } @@ -2017,6 +2172,7 @@ struct VerificPass : public Pass { bool mode_autocover = false; bool flatten = false, extnets = false; string dumpfile; + Map parameters(STRING_HASH); for (argidx++; argidx < GetSize(args); argidx++) { if (args[argidx] == "-all") { @@ -2055,6 +2211,15 @@ struct VerificPass : public Pass { mode_autocover = true; continue; } + if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) { + const std::string &key = args[++argidx]; + const std::string &value = args[++argidx]; + unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(), + 1 /* force_overwrite */); + if (!new_insertion) + log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str()); + continue; + } if (args[argidx] == "-V") { mode_verific = true; continue; @@ -2079,42 +2244,6 @@ struct VerificPass : public Pass { if (mode_all) { -#if 0 - log("Running veri_file::ElaborateAll().\n"); - if (!veri_file::ElaborateAll()) - log_cmd_error("Elaboration of Verilog modules failed.\n"); - - log("Running vhdl_file::ElaborateAll().\n"); - if (!vhdl_file::ElaborateAll()) - log_cmd_error("Elaboration of VHDL modules failed.\n"); - - Library *lib = Netlist::PresentDesign()->Owner()->Owner(); - - if (argidx == GetSize(args)) - { - MapIter iter; - char *iter_name; - Verific::Cell *iter_cell; - - FOREACH_MAP_ITEM(lib->GetCells(), iter, &iter_name, &iter_cell) { - if (*iter_name != '$') - nl_todo.insert(iter_cell->GetFirstNetlist()); - } - } - else - { - for (; argidx < GetSize(args); argidx++) - { - Verific::Cell *cell = lib->GetCell(args[argidx].c_str()); - - if (cell == nullptr) - log_cmd_error("Module not found: %s\n", args[argidx].c_str()); - - nl_todo.insert(cell->GetFirstNetlist()); - cell->GetFirstNetlist()->SetPresentDesign(); - } - } -#else log("Running hier_tree::ElaborateAll().\n"); VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); @@ -2124,35 +2253,19 @@ struct VerificPass : public Pass { if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); if (veri_lib) veri_libs.InsertLast(veri_lib); - Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs); + Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, ¶meters); Netlist *nl; int i; FOREACH_ARRAY_ITEM(netlists, i, nl) nl_todo.insert(nl); delete netlists; -#endif } else { if (argidx == GetSize(args)) log_cmd_error("No top module specified.\n"); -#if 0 - for (; argidx < GetSize(args); argidx++) { - if (veri_file::GetModule(args[argidx].c_str())) { - log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str()); - if (!veri_file::Elaborate(args[argidx].c_str())) - log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); - nl_todo.insert(Netlist::PresentDesign()); - } else { - log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str()); - if (!vhdl_file::Elaborate(args[argidx].c_str())) - log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); - nl_todo.insert(Netlist::PresentDesign()); - } - } -#else Array veri_modules, vhdl_units; for (; argidx < GetSize(args); argidx++) { @@ -2177,14 +2290,13 @@ struct VerificPass : public Pass { } log("Running hier_tree::Elaborate().\n"); - Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units); + Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, ¶meters); Netlist *nl; int i; FOREACH_ARRAY_ITEM(netlists, i, nl) nl_todo.insert(nl); delete netlists; -#endif } if (!verific_error_msg.empty()) @@ -2274,21 +2386,43 @@ struct ReadPass : public Pass { log("\n"); log("Add directory to global Verilog/SystemVerilog include directories.\n"); log("\n"); + log("\n"); + log(" read -verific\n"); + log(" read -noverific\n"); + log("\n"); + log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n"); + log("with -verific will result in an error on Yosys binaries that are built without\n"); + log("Verific support. The default is to use Verific if it is available.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - if (args.size() < 2) +#ifdef YOSYS_ENABLE_VERIFIC + static bool verific_available = !check_noverific_env(); +#else + static bool verific_available = false; +#endif + static bool use_verific = verific_available; + + if (args.size() < 2 || args[1][0] != '-') log_cmd_error("Missing mode parameter.\n"); + if (args[1] == "-verific" || args[1] == "-noverific") { + if (args.size() != 2) + log_cmd_error("Additional arguments to -verific/-noverific.\n"); + if (args[1] == "-verific") { + if (!verific_available) + log_cmd_error("This version of Yosys is built without Verific support.\n"); + use_verific = true; + } else { + use_verific = false; + } + return; + } + if (args.size() < 3) log_cmd_error("Missing file name parameter.\n"); -#ifdef YOSYS_ENABLE_VERIFIC - bool use_verific = !check_noverific_env(); -#else - bool use_verific = false; -#endif - if (args[1] == "-vlog95" || args[1] == "-vlog2k") { if (use_verific) { args[0] = "verific"; diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index 334a436a..b331dd4b 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -78,6 +78,7 @@ struct VerificImporter RTLIL::SigBit net_map_at(Verific::Net *net); + RTLIL::IdString new_verific_id(Verific::DesignObj *obj); void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj); RTLIL::SigSpec operatorInput(Verific::Instance *inst); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index cdc9ece8..8ea8372d 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -827,9 +827,9 @@ struct SvaFsm for (auto &it : nodes[i].edges) { if (it.second != State::S1) - log(" egde %s -> %d\n", log_signal(it.second), it.first); + log(" edge %s -> %d\n", log_signal(it.second), it.first); else - log(" egde -> %d\n", it.first); + log(" edge -> %d\n", it.first); } for (auto &it : nodes[i].links) { @@ -856,9 +856,9 @@ struct SvaFsm for (auto &it : unodes[i].edges) { if (!it.second.empty()) - log(" egde %s -> %d\n", log_signal(it.second), it.first); + log(" edge %s -> %d\n", log_signal(it.second), it.first); else - log(" egde -> %d\n", it.first); + log(" edge -> %d\n", it.first); } for (auto &ctrl : unodes[i].accept) { @@ -1666,7 +1666,20 @@ struct VerificSvaImporter log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(), LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile())); - RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID); + bool is_user_declared = root->IsUserDeclared(); + + // FIXME + if (!is_user_declared) { + const char *name = root->Name(); + for (int i = 0; name[i]; i++) { + if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) { + is_user_declared = true; + break; + } + } + } + + RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID); // parse SVA sequence into trigger signal diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 8dcc7c5a..504f8b3f 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -66,12 +66,24 @@ struct VerilogFrontend : public Frontend { log(" enable support for SystemVerilog assertions and some Yosys extensions\n"); log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); log("\n"); + log(" -noassert\n"); + log(" ignore assert() statements\n"); + log("\n"); + log(" -noassume\n"); + log(" ignore assume() statements\n"); + log("\n"); log(" -norestrict\n"); - log(" ignore restrict() assertions\n"); + log(" ignore restrict() statements\n"); log("\n"); log(" -assume-asserts\n"); log(" treat all assert() statements like assume() statements\n"); log("\n"); + log(" -assert-assumes\n"); + log(" treat all assume() statements like assert() statements\n"); + log("\n"); + log(" -debug\n"); + log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n"); + log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); @@ -81,7 +93,10 @@ struct VerilogFrontend : public Frontend { log(" -no_dump_ptr\n"); log(" do not include hex memory addresses in dump (easier to diff dumps)\n"); log("\n"); - log(" -dump_vlog\n"); + log(" -dump_vlog1\n"); + log(" dump ast as Verilog code (before simplification)\n"); + log("\n"); + log(" -dump_vlog2\n"); log(" dump ast as Verilog code (after simplification)\n"); log("\n"); log(" -dump_rtlil\n"); @@ -188,7 +203,8 @@ struct VerilogFrontend : public Frontend { bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; bool flag_no_dump_ptr = false; - bool flag_dump_vlog = false; + bool flag_dump_vlog1 = false; + bool flag_dump_vlog2 = false; bool flag_dump_rtlil = false; bool flag_nolatches = false; bool flag_nomeminit = false; @@ -229,6 +245,14 @@ struct VerilogFrontend : public Frontend { formal_mode = true; continue; } + if (arg == "-noassert") { + noassert_mode = true; + continue; + } + if (arg == "-noassume") { + noassume_mode = true; + continue; + } if (arg == "-norestrict") { norestrict_mode = true; continue; @@ -237,6 +261,18 @@ struct VerilogFrontend : public Frontend { assume_asserts_mode = true; continue; } + if (arg == "-assert-assumes") { + assert_assumes_mode = true; + continue; + } + if (arg == "-debug") { + flag_dump_ast1 = true; + flag_dump_ast2 = true; + flag_dump_vlog1 = true; + flag_dump_vlog2 = true; + frontend_verilog_yydebug = true; + continue; + } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; @@ -249,8 +285,12 @@ struct VerilogFrontend : public Frontend { flag_no_dump_ptr = true; continue; } - if (arg == "-dump_vlog") { - flag_dump_vlog = true; + if (arg == "-dump_vlog1") { + flag_dump_vlog1 = true; + continue; + } + if (arg == "-dump_vlog2") { + flag_dump_vlog2 = true; continue; } if (arg == "-dump_rtlil") { @@ -389,7 +429,7 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + 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, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 16edc798..523bbc89 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -54,12 +54,21 @@ namespace VERILOG_FRONTEND // running in -formal mode extern bool formal_mode; + // running in -noassert mode + extern bool noassert_mode; + + // running in -noassume mode + extern bool noassume_mode; + // running in -norestrict mode extern bool norestrict_mode; // running in -assume-asserts mode extern bool assume_asserts_mode; + // running in -assert-assumes mode + extern bool assert_assumes_mode; + // running in -lib mode extern bool lib_mode; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 83921bf0..6ef38252 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -135,6 +135,9 @@ YOSYS_NAMESPACE_END frontend_verilog_yyerror("Unsupported default nettype: %s", p); } +"`protect"[^\n]* /* ignore `protect*/ +"`endprotect"[^\n]* /* ignore `endprotect*/ + "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } @@ -150,6 +153,9 @@ YOSYS_NAMESPACE_END "specparam" { return TOK_SPECPARAM; } "package" { SV_KEYWORD(TOK_PACKAGE); } "endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } +"interface" { SV_KEYWORD(TOK_INTERFACE); } +"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); } +"modport" { SV_KEYWORD(TOK_MODPORT); } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } @@ -183,6 +189,14 @@ YOSYS_NAMESPACE_END "always_ff" { SV_KEYWORD(TOK_ALWAYS); } "always_latch" { SV_KEYWORD(TOK_ALWAYS); } + /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex + to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some + global state.. its a mess) */ +[a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_SVA_LABEL; +} + "assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } "assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } "cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); } @@ -268,7 +282,7 @@ YOSYS_NAMESPACE_END yystr[j++] = yystr[i++]; } yystr[j] = 0; - frontend_verilog_yylval.string = new std::string(yystr); + frontend_verilog_yylval.string = new std::string(yystr, j); free(yystr); return TOK_STRING; } @@ -295,6 +309,11 @@ supply1 { return TOK_SUPPLY1; } return TOK_ID; } +[a-zA-Z_$][a-zA-Z0-9_$\.]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 2389d7d3..52685f63 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -35,6 +35,7 @@ %{ #include <list> +#include <stack> #include <string.h> #include "frontends/verilog/verilog_frontend.h" #include "kernel/log.h" @@ -47,7 +48,8 @@ YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; std::map<std::string, int> port_stubs; - std::map<std::string, AstNode*> attr_list, default_attr_list; + std::map<std::string, AstNode*> *attr_list, default_attr_list; + std::stack<std::map<std::string, AstNode*> *> attr_list_stack; std::map<std::string, AstNode*> *albuf; std::vector<AstNode*> ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; @@ -58,8 +60,10 @@ namespace VERILOG_FRONTEND { bool do_not_require_port_stubs; bool default_nettype_wire; bool sv_mode, formal_mode, lib_mode; - bool norestrict_mode, assume_asserts_mode; + bool noassert_mode, noassume_mode, norestrict_mode; + bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; + bool current_modport_input, current_modport_output; std::istream *lexin; } YOSYS_NAMESPACE_END @@ -101,10 +105,12 @@ static void free_attr(std::map<std::string, AstNode*> *al) bool boolean; } -%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE +%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE TOK_SVA_LABEL +%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT @@ -114,15 +120,14 @@ static void free_attr(std::map<std::string, AstNode*> *al) %token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED -%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME -%token TOK_RESTRICT TOK_COVER TOK_PROPERTY TOK_ENUM TOK_TYPEDEF +%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF %token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY %token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY %type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list -%type <string> opt_label tok_prim_wrapper hierarchical_id -%type <boolean> opt_signed unique_case_attr +%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id +%type <boolean> opt_signed opt_property unique_case_attr %type <al> attr case_attr // operator precedence from low to high @@ -167,19 +172,23 @@ design: param_decl design | localparam_decl design | package design | + interface design | /* empty */; attr: { - for (auto &it : attr_list) - delete it.second; - attr_list.clear(); + if (attr_list != nullptr) + attr_list_stack.push(attr_list); + attr_list = new std::map<std::string, AstNode*>; for (auto &it : default_attr_list) - attr_list[it.first] = it.second->clone(); + (*attr_list)[it.first] = it.second->clone(); } attr_opt { - std::map<std::string, AstNode*> *al = new std::map<std::string, AstNode*>; - al->swap(attr_list); - $$ = al; + $$ = attr_list; + if (!attr_list_stack.empty()) { + attr_list = attr_list_stack.top(); + attr_list_stack.pop(); + } else + attr_list = nullptr; }; attr_opt: @@ -188,15 +197,20 @@ attr_opt: defattr: DEFATTR_BEGIN { + if (attr_list != nullptr) + attr_list_stack.push(attr_list); + attr_list = new std::map<std::string, AstNode*>; for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); - for (auto &it : attr_list) - delete it.second; - attr_list.clear(); } opt_attr_list { - default_attr_list = attr_list; - attr_list.clear(); + attr_list->swap(default_attr_list); + delete attr_list; + if (!attr_list_stack.empty()) { + attr_list = attr_list_stack.top(); + attr_list_stack.pop(); + } else + attr_list = nullptr; } DEFATTR_END; opt_attr_list: @@ -208,15 +222,15 @@ attr_list: attr_assign: hierarchical_id { - if (attr_list.count(*$1) != 0) - delete attr_list[*$1]; - attr_list[*$1] = AstNode::mkconst_int(1, false); + if (attr_list->count(*$1) != 0) + delete (*attr_list)[*$1]; + (*attr_list)[*$1] = AstNode::mkconst_int(1, false); delete $1; } | hierarchical_id '=' expr { - if (attr_list.count(*$1) != 0) - delete attr_list[*$1]; - attr_list[*$1] = $3; + if (attr_list->count(*$1) != 0) + delete (*attr_list)[*$1]; + (*attr_list)[*$1] = $3; delete $1; }; @@ -301,7 +315,7 @@ module_arg_opt_assignment: else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); } else - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); } | /* empty */; @@ -319,6 +333,21 @@ module_arg: } delete $1; } module_arg_opt_assignment | + TOK_ID { + astbuf1 = new AstNode(AST_INTERFACEPORT); + astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE)); + astbuf1->children[0]->str = *$1; + delete $1; + } TOK_ID { /* SV interfaces */ + if (!sv_mode) + frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); + astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type. + astbuf2->str = *$3; + delete $3; + astbuf2->port_id = ++port_counter; + ast_stack.back()->children.push_back(astbuf2); + delete astbuf1; // really only needed if multiple instances of same type. + } module_arg_opt_assignment | attr wire_type range TOK_ID { AstNode *node = $2; node->str = *$4; @@ -356,6 +385,33 @@ package_body: package_body_stmt: localparam_decl; +interface: + TOK_INTERFACE TOK_ID { + do_not_require_port_stubs = false; + AstNode *intf = new AstNode(AST_INTERFACE); + ast_stack.back()->children.push_back(intf); + ast_stack.push_back(intf); + current_ast_mod = intf; + port_stubs.clear(); + port_counter = 0; + intf->str = *$2; + delete $2; + } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { + if (port_stubs.size() != 0) + frontend_verilog_yyerror("Missing details for module port `%s'.", + port_stubs.begin()->first.c_str()); + ast_stack.pop_back(); + log_assert(ast_stack.size() == 1); + current_ast_mod = NULL; + }; + +interface_body: + interface_body interface_body_stmt |; + +interface_body_stmt: + param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | + modport_stmt; + non_opt_delay: '#' TOK_ID { delete $2; } | '#' TOK_CONSTVAL { delete $2; } | @@ -626,7 +682,7 @@ task_func_port: astbuf2 = $3; if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { if (astbuf2) { - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions (task/function arguments)"); } else { astbuf2 = new AstNode(AST_RANGE); astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); @@ -634,7 +690,7 @@ task_func_port: } } if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("task/function argument range must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); } wire_name | wire_name; task_func_body: @@ -738,7 +794,7 @@ more_path_inputs : list_of_path_outputs : specify_output_terminal_descriptor | list_of_path_outputs ',' specify_output_terminal_descriptor ; - + opt_polarity_operator : '+' | '-' @@ -763,7 +819,7 @@ system_timing_arg : system_timing_args : system_timing_arg | system_timing_args ',' system_timing_arg ; - + /* t_path_delay_expression : path_delay_expression; @@ -825,7 +881,7 @@ constant_mintypmax_expression : // for the time being this is OK, but we may write our own expr here. // as I'm not sure it is legal to use a full expr here (probably not) // On the other hand, other rules requiring constant expressions also use 'expr' -// (such as param assignment), so we may leave this as-is, perhaps assing runtime checks for constant-ness +// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness constant_expression: expr ; @@ -837,7 +893,7 @@ param_signed: param_integer: TOK_INTEGER { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Internal error in param_integer - should not happen?"); astbuf1->children.push_back(new AstNode(AST_RANGE)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); @@ -847,7 +903,7 @@ param_integer: param_real: TOK_REAL { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Parameter already declared as integer, cannot set to real."); astbuf1->children.push_back(new AstNode(AST_REALVALUE)); } | /* empty */; @@ -855,7 +911,7 @@ param_range: range { if ($1 != NULL) { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/real parameters should not have a range."); astbuf1->children.push_back($1); } }; @@ -881,9 +937,15 @@ param_decl_list: single_param_decl: TOK_ID '=' expr { - if (astbuf1 == nullptr) - frontend_verilog_yyerror("syntax error"); - AstNode *node = astbuf1->clone(); + AstNode *node; + if (astbuf1 == nullptr) { + if (!sv_mode) + frontend_verilog_yyerror("In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); + node = new AstNode(AST_PARAMETER); + node->children.push_back(AstNode::mkconst_int(0, true)); + } else { + node = astbuf1->clone(); + } node->str = *$1; delete node->children[0]; node->children[0] = $3; @@ -914,7 +976,7 @@ wire_decl: astbuf2 = $3; if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { if (astbuf2) { - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); } else { astbuf2 = new AstNode(AST_RANGE); astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); @@ -922,7 +984,7 @@ wire_decl: } } if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); } wire_name_list { delete astbuf1; if (astbuf2 != NULL) @@ -1016,7 +1078,7 @@ wire_name_and_opt_assign: wire_name: TOK_ID range_or_multirange { if (astbuf1 == nullptr) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Internal error - should not happen - no AST_WIRE node."); AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -1024,7 +1086,7 @@ wire_name: node->children.push_back(astbuf2->clone()); if ($2 != NULL) { if (node->is_input || node->is_output) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); if (!astbuf2) { AstNode *rng = new AstNode(AST_RANGE); rng->children.push_back(AstNode::mkconst_int(0, true)); @@ -1267,74 +1329,216 @@ opt_label: $$ = NULL; }; +opt_sva_label: + TOK_SVA_LABEL ':' { + $$ = $1; + } | + /* empty */ { + $$ = NULL; + }; + opt_property: - TOK_PROPERTY | /* empty */; + TOK_PROPERTY { + $$ = true; + } | + /* empty */ { + $$ = false; + }; -opt_stmt_label: - TOK_ID ':' | /* empty */; +modport_stmt: + TOK_MODPORT TOK_ID { + AstNode *modport = new AstNode(AST_MODPORT); + ast_stack.back()->children.push_back(modport); + ast_stack.push_back(modport); + modport->str = *$2; + delete $2; + } modport_args_opt { + ast_stack.pop_back(); + log_assert(ast_stack.size() == 2); + } ';' + +modport_args_opt: + '(' ')' | '(' modport_args optional_comma ')'; + +modport_args: + modport_arg | modport_args ',' modport_arg; + +modport_arg: + modport_type_token modport_member | + modport_member + +modport_member: + TOK_ID { + AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); + ast_stack.back()->children.push_back(modport_member); + modport_member->str = *$1; + modport_member->is_input = current_modport_input; + modport_member->is_output = current_modport_output; + delete $1; + } + +modport_type_token: + TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} assert: - opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5)); + opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' { + if (noassert_mode) { + delete $5; + } else { + AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if ($1 != nullptr) + delete $1; } | - opt_stmt_label TOK_ASSUME opt_property '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + opt_sva_label TOK_ASSUME opt_property '(' expr ')' ';' { + if (noassume_mode) { + delete $5; + } else { + AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if ($1 != nullptr) + delete $1; } | - opt_stmt_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6)); + opt_sva_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (noassert_mode) { + delete $6; + } else { + AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if ($1 != nullptr) + delete $1; } | - opt_stmt_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + opt_sva_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (noassume_mode) { + delete $6; + } else { + AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if ($1 != nullptr) + delete $1; } | - opt_stmt_label TOK_COVER opt_property '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5)); + opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { + AstNode *node = new AstNode(AST_COVER, $5); + if ($1 != nullptr) { + node->str = *$1; + delete $1; + } + ast_stack.back()->children.push_back(node); } | - opt_stmt_label TOK_COVER opt_property '(' ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false))); + opt_sva_label TOK_COVER opt_property '(' ')' ';' { + AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + if ($1 != nullptr) { + node->str = *$1; + delete $1; + } + ast_stack.back()->children.push_back(node); } | - opt_stmt_label TOK_COVER ';' { - ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false))); + opt_sva_label TOK_COVER ';' { + AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + if ($1 != nullptr) { + node->str = *$1; + delete $1; + } + ast_stack.back()->children.push_back(node); } | - opt_stmt_label TOK_RESTRICT opt_property '(' expr ')' ';' { - if (norestrict_mode) + opt_sva_label TOK_RESTRICT opt_property '(' expr ')' ';' { + if (norestrict_mode) { delete $5; - else - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + } else { + AstNode *node = new AstNode(AST_ASSUME, $5); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if (!$3) + log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); + if ($1 != nullptr) + delete $1; } | - opt_stmt_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) + opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (norestrict_mode) { delete $6; - else - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + } else { + AstNode *node = new AstNode(AST_FAIR, $6); + if ($1 != nullptr) + node->str = *$1; + ast_stack.back()->children.push_back(node); + } + if (!$3) + log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); + if ($1 != nullptr) + delete $1; }; assert_property: - TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $4)); - } | - TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); + opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } } | - TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $5)); + opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } } | - TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5)); + opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } } | - TOK_COVER TOK_PROPERTY '(' expr ')' ';' { - ast_stack.back()->children.push_back(new AstNode(AST_COVER, $4)); + opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } } | - TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { - if (norestrict_mode) - delete $4; - else - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); + opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } } | - TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) + opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { + if (norestrict_mode) { delete $5; - else - ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5)); + } else { + ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } + } + } | + opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + if (norestrict_mode) { + delete $6; + } else { + ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + if ($1 != nullptr) { + ast_stack.back()->children.back()->str = *$1; + delete $1; + } + } }; simple_behavioral_stmt: @@ -1379,7 +1583,7 @@ behavioral_stmt: node->str = *$3; } behavioral_stmt_list TOK_END opt_label { if ($3 != NULL && $7 != NULL && *$3 != *$7) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1); if ($3 != NULL) delete $3; if ($7 != NULL) @@ -1552,6 +1756,11 @@ case_expr_list: TOK_DEFAULT { ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT)); } | + TOK_SVA_LABEL { + ast_stack.back()->children.push_back(new AstNode(AST_IDENTIFIER)); + ast_stack.back()->children.back()->str = *$1; + delete $1; + } | expr { ast_stack.back()->children.push_back($1); } | @@ -1695,7 +1904,7 @@ basic_expr: } | '(' expr ')' TOK_CONSTVAL { if ($4->substr(0, 1) != "'") - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. (<expr>)<constval> , while %s is not a sized constant.", $4->c_str()); AstNode *bits = $2; AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if (val == NULL) @@ -1705,7 +1914,7 @@ basic_expr: } | hierarchical_id TOK_CONSTVAL { if ($2->substr(0, 1) != "'") - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. <ID>\'d0, while %s is not a sized constant.", $2->c_str()); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); |