diff options
129 files changed, 3856 insertions, 693 deletions
diff --git a/.github/issue_template.md b/.github/issue_template.md index 5a0723c3e..c72daae3e 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -13,7 +13,7 @@ create a Minimal, Complete, and Verifiable example (MCVE). Please do not waste our time with issues that lack sufficient information to reproduce the issue easily. We will simply close those issues. -Contact https://www.symbioticeda.com/ if you need commercial support for Yosys. +Contact https://www.yosyshq.com/ if you need commercial support for Yosys. ## Expected behavior @@ -9,3 +9,4 @@ brew "python3" brew "tcl-tk" brew "xdot" brew "bash" +brew 'boost-python3' @@ -126,7 +126,7 @@ LDFLAGS += -rdynamic LDLIBS += -lrt endif -YOSYS_VER := 0.9+3830 +YOSYS_VER := 0.9+3962 GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o @@ -810,6 +810,7 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT) +cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT) +cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT) + +cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT) +cd tests/arch/efinix && bash run-test.sh $(SEEDOPT) +cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) +cd tests/arch/gowin && bash run-test.sh $(SEEDOPT) diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 639c6f129..692452aad 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -860,7 +860,20 @@ struct BtorWorker goto okay; } - log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + log_error("Unsupported cell type %s for cell %s.%s.\n", + log_id(cell->type), log_id(module), log_id(cell)); okay: btorf_pop(log_id(cell)); diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index 0a6bcb849..0e55c46c2 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -1217,49 +1217,49 @@ value<BitsY> xnor_ss(const value<BitsA> &a, const value<BitsB> &b) { template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> shl_uu(const value<BitsA> &a, const value<BitsB> &b) { - return a.template zcast<BitsY>().template shl(b); + return a.template zcast<BitsY>().shl(b); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> shl_su(const value<BitsA> &a, const value<BitsB> &b) { - return a.template scast<BitsY>().template shl(b); + return a.template scast<BitsY>().shl(b); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> sshl_uu(const value<BitsA> &a, const value<BitsB> &b) { - return a.template zcast<BitsY>().template shl(b); + return a.template zcast<BitsY>().shl(b); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> sshl_su(const value<BitsA> &a, const value<BitsB> &b) { - return a.template scast<BitsY>().template shl(b); + return a.template scast<BitsY>().shl(b); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> shr_uu(const value<BitsA> &a, const value<BitsB> &b) { - return a.template shr(b).template zcast<BitsY>(); + return a.shr(b).template zcast<BitsY>(); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) { - return a.template shr(b).template scast<BitsY>(); + return a.shr(b).template scast<BitsY>(); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> sshr_uu(const value<BitsA> &a, const value<BitsB> &b) { - return a.template shr(b).template zcast<BitsY>(); + return a.shr(b).template zcast<BitsY>(); } template<size_t BitsY, size_t BitsA, size_t BitsB> CXXRTL_ALWAYS_INLINE value<BitsY> sshr_su(const value<BitsA> &a, const value<BitsB> &b) { - return a.template sshr(b).template scast<BitsY>(); + return a.sshr(b).template scast<BitsY>(); } template<size_t BitsY, size_t BitsA, size_t BitsB> diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 861b484f4..39046bd78 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -601,6 +601,12 @@ struct WireType { bool is_exact() const { return type == ALIAS || type == CONST; } }; +// Tests for a SigSpec that is a valid clock input, clocks have to have a backing wire and be a single bit +// using this instead of sig.is_wire() solves issues when the clock is a slice instead of a full wire +bool is_valid_clock(const RTLIL::SigSpec& sig) { + return sig.is_chunk() && sig.is_bit() && sig[0].wire; +} + struct CxxrtlWorker { bool split_intf = false; std::string intf_filename; @@ -1110,7 +1116,8 @@ struct CxxrtlWorker { // Flip-flops } else if (is_ff_cell(cell->type)) { log_assert(!for_debug); - if (cell->hasPort(ID::CLK) && cell->getPort(ID::CLK).is_wire()) { + // Clocks might be slices of larger signals but should only ever be single bit + if (cell->hasPort(ID::CLK) && is_valid_clock(cell->getPort(ID::CLK))) { // Edge-sensitive logic RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); @@ -2266,7 +2273,7 @@ struct CxxrtlWorker { void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type) { signal = sigmap(signal); - log_assert(signal.is_wire() && signal.is_bit()); + log_assert(is_valid_clock(signal)); log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe); RTLIL::SigBit sigbit = signal[0]; @@ -2274,7 +2281,8 @@ struct CxxrtlWorker { edge_types[sigbit] = type; else if (edge_types[sigbit] != type) edge_types[sigbit] = RTLIL::STe; - edge_wires.insert(signal.as_wire()); + // Cannot use as_wire because signal might not be a full wire, instead extract the wire from the sigbit + edge_wires.insert(sigbit.wire); } void analyze_design(RTLIL::Design *design) @@ -2355,14 +2363,14 @@ struct CxxrtlWorker { // Various DFF cells are treated like posedge/negedge processes, see above for details. if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { - if (sigmap(cell->getPort(ID::CLK)).is_wire()) + if (is_valid_clock(cell->getPort(ID::CLK))) register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); } // Similar for memory port cells. if (cell->type.in(ID($memrd), ID($memwr))) { if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - if (sigmap(cell->getPort(ID::CLK)).is_wire()) + if (is_valid_clock(cell->getPort(ID::CLK))) register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); } @@ -2372,7 +2380,7 @@ struct CxxrtlWorker { if (cell->type == ID($memwr)) writable_memories.insert(module->memories[cell->getParam(ID::MEMID).decode_string()]); // Collect groups of memory write ports in the same domain. - if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire()) { + if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK))) { RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; memwr_per_domain[{clk_bit, memory}].insert(cell); @@ -2384,7 +2392,7 @@ struct CxxrtlWorker { } for (auto cell : module->cells()) { // Collect groups of memory write ports read by every transparent read port. - if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire() && + if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK)) && cell->getParam(ID::TRANSPARENT).as_bool()) { RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h index 6ee98b428..3f40a8d12 100644 --- a/backends/cxxrtl/cxxrtl_vcd.h +++ b/backends/cxxrtl/cxxrtl_vcd.h @@ -228,13 +228,13 @@ public: } void add(const debug_items &items) { - this->template add(items, [](const std::string &, const debug_item &) { + this->add(items, [](const std::string &, const debug_item &) { return true; }); } void add_without_memories(const debug_items &items) { - this->template add(items, [](const std::string &, const debug_item &item) { + this->add(items, [](const std::string &, const debug_item &item) { return item.type != debug_item::MEMORY; }); } diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index a185fdd74..8be9e05f1 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -855,6 +855,18 @@ struct Smt2Worker return; } + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } log_error("Unsupported cell type %s for cell %s.%s.\n", log_id(cell->type), log_id(module), log_id(cell)); } diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 4e5c6050d..e41582fea 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -573,8 +573,22 @@ struct SmvWorker continue; } - if (cell->type[0] == '$') - log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell)); + if (cell->type[0] == '$') { + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + log_error("Unsupported cell type %s for cell %s.%s.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } // f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index aa20f106a..ca5c680c9 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -64,7 +64,7 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, } } -static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian, bool use_inames) +static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &buf, std::string &ncpf, bool big_endian, bool use_inames) { SigMap sigmap(module); idict<IdString, 1> inums; @@ -121,10 +121,10 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) { - f << stringf("V%d", conn_counter++); - print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); + f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++)); print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); - f << stringf(" DC 0\n"); + print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); + f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str())); } } @@ -148,6 +148,10 @@ struct SpiceBackend : public Backend { log(" -pos net_name\n"); log(" set the net name for constant 1 (default: Vdd)\n"); log("\n"); + log(" -buf DC|subckt_name\n"); + log(" set the name for jumper element (default: DC)\n"); + log(" (used to connect different nets)\n"); + log("\n"); log(" -nc_prefix\n"); log(" prefix for not-connected nets (default: _NC)\n"); log("\n"); @@ -164,7 +168,7 @@ struct SpiceBackend : public Backend { std::string top_module_name; RTLIL::Module *top_module = NULL; bool big_endian = false, use_inames = false; - std::string neg = "Vss", pos = "Vdd", ncpf = "_NC"; + std::string neg = "Vss", pos = "Vdd", ncpf = "_NC", buf = "DC"; log_header(design, "Executing SPICE backend.\n"); @@ -187,6 +191,10 @@ struct SpiceBackend : public Backend { pos = args[++argidx]; continue; } + if (args[argidx] == "-buf" && argidx+1 < args.size()) { + buf = args[++argidx]; + continue; + } if (args[argidx] == "-nc_prefix" && argidx+1 < args.size()) { ncpf = args[++argidx]; continue; @@ -241,14 +249,14 @@ struct SpiceBackend : public Backend { *f << stringf(" %s", spice_id2str(wire->name).c_str()); } *f << stringf("\n"); - print_spice_module(*f, module, design, neg, pos, ncpf, big_endian, use_inames); + print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames); *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); } if (!top_module_name.empty()) { if (top_module == NULL) log_error("Can't find top module `%s'!\n", top_module_name.c_str()); - print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian, use_inames); + print_spice_module(*f, top_module, design, neg, pos, buf, ncpf, big_endian, use_inames); *f << stringf("\n"); } diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 07e3cd6e0..463c5965b 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -55,6 +55,17 @@ inline int32_t from_big_endian(int32_t i32) { #define log_debug2(...) ; //#define log_debug2(...) log_debug(__VA_ARGS__) +static int decimal_digits(uint32_t n) { + static uint32_t digit_cutoff[9] = { + 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000 + }; + for (int i = 0; i < 9; ++i) { + if (n < digit_cutoff[i]) return i + 1; + } + return 10; +} + struct ConstEvalAig { RTLIL::Module *module; @@ -515,7 +526,7 @@ void AigerReader::parse_aiger_ascii() unsigned l1, l2, l3; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an input!\n", line_count); @@ -537,7 +548,7 @@ void AigerReader::parse_aiger_ascii() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); 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); @@ -575,7 +586,7 @@ void AigerReader::parse_aiger_ascii() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); 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); @@ -643,7 +654,7 @@ void AigerReader::parse_aiger_binary() std::string line; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i) { log_debug2("%d is an input\n", i); RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i)); @@ -662,7 +673,7 @@ void AigerReader::parse_aiger_binary() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); l1 = (I+1) * 2; for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { if (!(f >> l2)) @@ -700,7 +711,7 @@ void AigerReader::parse_aiger_binary() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); 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); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 1c0a8b34d..57552d86c 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -287,8 +287,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) const } std::string type_name = type2str(type); - fprintf(f, "%s%s <%s:%d.%d-%d.%d>", indent.c_str(), type_name.c_str(), filename.c_str(), location.first_line, - location.first_column, location.last_line, location.last_column); + fprintf(f, "%s%s <%s>", indent.c_str(), type_name.c_str(), loc_string().c_str()); if (!flag_no_dump_ptr) { if (id2ast) @@ -548,9 +547,9 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_CASE: - if (!children.empty() && children[0]->type == AST_CONDX) + if (children.size() > 1 && children[1]->type == AST_CONDX) fprintf(f, "%s" "casex (", indent.c_str()); - else if (!children.empty() && children[0]->type == AST_CONDZ) + else if (children.size() > 1 && children[1]->type == AST_CONDZ) fprintf(f, "%s" "casez (", indent.c_str()); else fprintf(f, "%s" "case (", indent.c_str()); @@ -959,6 +958,16 @@ RTLIL::Const AstNode::realAsConst(int width) return result; } +std::string AstNode::loc_string() const +{ + return stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +} + +void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) +{ + obj->attributes[ID::src] = ast->loc_string(); +} + // create a new AstModule from an AST_MODULE AST node static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) { @@ -974,8 +983,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module = new AstModule; current_module->ast = NULL; current_module->name = ast->str; - current_module->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, - ast->location.first_column, ast->location.last_line, ast->location.last_column); + set_src_attr(current_module, ast); current_module->set_bool_attribute(ID::cells_not_processed); current_ast_mod = ast; @@ -1229,13 +1237,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str()); } else if (nooverwrite) { - log("Ignoring re-definition of module `%s' at %s:%d.%d-%d.%d.\n", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + log("Ignoring re-definition of module `%s' at %s.\n", + (*it)->str.c_str(), (*it)->loc_string().c_str()); continue; } else { - log("Replacing existing%s module `%s' at %s:%d.%d-%d.%d.\n", + log("Replacing existing%s module `%s' at %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + (*it)->str.c_str(), (*it)->loc_string().c_str()); design->remove(existing_mod); } } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 907392166..1c9a6ee47 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -252,8 +252,8 @@ namespace AST bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); void replace_result_wire_name_in_function(const std::string &from, const std::string &to); AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); - void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope = true); - void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); + void expand_genblock(const std::string &prefix); + void label_genblks(std::set<std::string>& existing, int &counter); void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); @@ -263,14 +263,22 @@ namespace AST bool detect_latch(const std::string &var); // additional functionality for evaluating constant functions - struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; - bool has_const_only_constructs(bool &recommend_const_eval); - bool has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval); - void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); - AstNode *eval_const_function(AstNode *fcall); + struct varinfo_t { + RTLIL::Const val; + int offset; + bool is_signed; + AstNode *arg = nullptr; + bool explicitly_sized; + }; + bool has_const_only_constructs(); + bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); + AstNode *eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); + bool is_recursive_function() const; + std::pair<AstNode*, AstNode*> get_tern_choice(); + // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; void dumpVlog(FILE *f, std::string indent) const; @@ -315,6 +323,9 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); void annotateTypedEnums(AstNode *template_node); + + // helpers for locations + std::string loc_string() const; }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -353,6 +364,9 @@ namespace AST 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); + + // Helper for setting the src attribute. + void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast); } namespace AST_INTERNAL diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d3..948c9083c 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -67,7 +67,7 @@ static ffi_fptr resolve_fn (std::string symbol_name) AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args) { AST::AstNode *newNode = nullptr; - union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; + union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1]; ffi_type *types [args.size() + 1]; void *values [args.size() + 1]; ffi_cif cif; @@ -92,6 +92,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, value_store[i].i32 = args[i]->asInt(args[i]->is_signed); values[i] = &value_store[i].i32; types[i] = &ffi_type_sint32; + } else if (argtypes[i] == "chandle") { + log(" arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false)); + value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed); + values[i] = &value_store[i].ptr; + types[i] = &ffi_type_pointer; } else { log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i); } @@ -106,6 +111,9 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, } else if (rtype == "real") { types[args.size()] = &ffi_type_double; values[args.size()] = &value_store[args.size()].f64; + } else if (rtype == "chandle") { + types[args.size()] = &ffi_type_pointer; + values[args.size()] = &value_store[args.size()].ptr; } else { log_error("invalid rtype '%s'.\n", rtype.c_str()); } @@ -123,6 +131,13 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, newNode = new AstNode(AST_REALVALUE); newNode->realvalue = value_store[args.size()].f32; log(" return realvalue: %g\n", newNode->asReal(true)); + } else if (rtype == "chandle") { + uint64_t rawval = (uint64_t)value_store[args.size()].ptr; + std::vector<RTLIL::State> bits(64); + for (int i = 0; i < 64; i++) + bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; + newNode = AstNode::mkconst_bits(bits, false); + log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false)); } else { newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); log(" return integer: %lld\n", (long long)newNode->asInt(true)); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b8bfdf65e..d4299bf69 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,10 +45,11 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width { IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (gen_attributes) for (auto &attr : that->attributes) { @@ -76,10 +77,11 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (that != NULL) for (auto &attr : that->attributes) { @@ -102,10 +104,10 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width { IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { @@ -137,10 +139,10 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { @@ -318,7 +320,7 @@ struct AST_INTERNAL::ProcessGenerator // generate process and simple root case proc = new RTLIL::Process; - proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + set_src_attr(proc, always); proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) @@ -354,7 +356,7 @@ struct AST_INTERNAL::ProcessGenerator if (found_anyedge_syncs) { if (found_global_syncs) log_file_error(always->filename, always->location.first_line, "Found non-synthesizable event list!\n"); - log("Note: Assuming pure combinatorial block at %s:%d.%d-%d.%d in\n", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string().c_str()); log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); } @@ -454,7 +456,7 @@ struct AST_INTERNAL::ProcessGenerator } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + set_src_attr(wire, always); chunk.wire = wire; chunk.offset = 0; @@ -589,7 +591,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: { RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; - sw->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); + set_src_attr(sw, ast); sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap()); current_case->switches.push_back(sw); @@ -623,7 +625,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; - current_case->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", child->filename.c_str(), child->location.first_line, child->location.first_column, child->location.last_line, child->location.last_column); + set_src_attr(current_case, child); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); for (auto node : child->children) { @@ -942,6 +944,41 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } break; } + if (current_scope.count(str)) + { + // This width detection is needed for function calls which are + // unelaborated, which currently only applies to calls to recursive + // functions reached by unevaluated ternary branches. + const AstNode *func = current_scope.at(str); + if (func->type != AST_FUNCTION) + log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); + const AstNode *wire = nullptr; + for (const AstNode *child : func->children) + if (child->str == func->str) { + wire = child; + break; + } + log_assert(wire && wire->type == AST_WIRE); + sign_hint = wire->is_signed; + width_hint = 1; + if (!wire->children.empty()) + { + log_assert(wire->children.size() == 1); + const AstNode *range = wire->children.at(0); + log_assert(range->type == AST_RANGE && range->children.size() == 2); + AstNode *left = range->children.at(0)->clone(); + AstNode *right = range->children.at(1)->clone(); + while (left->simplify(true, false, false, 1, -1, false, true)) { } + while (right->simplify(true, false, false, 1, -1, false, true)) { } + if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Function %s has non-constant width!", + RTLIL::unescape_id(str).c_str()); + width_hint = abs(int(left->asInt(true) - right->asInt(true))); + delete left; + delete right; + } + break; + } YS_FALLTHROUGH // everything should have been handled above -> print error if not. @@ -965,6 +1002,29 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real detectSignWidthWorker(width_hint, sign_hint, found_real); } +static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id, + const AstNode *node, const char *to_add_kind) +{ + auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) { + std::string src = existing->get_string_attribute(ID::src); + std::string location_str = "earlier"; + if (!src.empty()) + location_str = "at " + src; + log_file_error(node->filename, node->location.first_line, + "Cannot add %s `%s' because a %s with the same name was already created %s!\n", + to_add_kind, id.c_str(), existing_kind, location_str.c_str()); + }; + + if (const RTLIL::Wire *wire = module->wire(id)) + already_exists(wire, "signal"); + if (const RTLIL::Cell *cell = module->cell(id)) + already_exists(cell, "cell"); + if (module->processes.count(id)) + already_exists(module->processes.at(id), "process"); + if (module->memories.count(id)) + already_exists(module->memories.at(id), "memory"); +} + // create RTLIL from an AST node // all generated cells, wires and processes are added to the module pointed to by 'current_module' // when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned. @@ -1010,8 +1070,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // 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[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "interface port"); + RTLIL::Wire *wire = current_module->addWire(id, 1); + set_src_attr(wire, this); wire->start_offset = 0; wire->port_id = port_id; wire->port_input = true; @@ -1048,10 +1110,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Parameter `%s' with non-constant value!\n", str.c_str()); RTLIL::Const val = children[0]->bitsAsConst(); - RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "pwire"); + RTLIL::Wire *wire = current_module->addWire(id, GetSize(val)); current_module->connect(wire, val); + wire->is_signed = children[0]->is_signed; - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1; for (auto &attr : attributes) { @@ -1064,16 +1129,16 @@ 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, location.first_line, "Re-definition of signal `%s'!\n", str.c_str()); if (!range_valid) log_file_error(filename, location.first_line, "Signal `%s' with non-constant width!\n", str.c_str()); if (!(range_left + 1 >= range_right)) log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); - RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "signal"); + RTLIL::Wire *wire = current_module->addWire(id, range_left - range_right + 1); + set_src_attr(wire, this); wire->start_offset = range_right; wire->port_id = port_id; wire->port_input = is_input; @@ -1094,9 +1159,6 @@ 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, location.first_line, "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); @@ -1105,7 +1167,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; - memory->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(memory, this); memory->name = str; memory->width = children[0]->range_left - children[0]->range_right + 1; if (children[1]->range_right < children[1]->range_left) { @@ -1115,6 +1177,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) memory->start_offset = children[1]->range_left; memory->size = children[1]->range_right - children[1]->range_left + 1; } + check_unique_id(current_module, memory->name, this, "memory"); current_module->memories[memory->name] = memory; for (auto &attr : attributes) { @@ -1162,7 +1225,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { RTLIL::Wire *wire = current_module->addWire(str); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->name = str; if (flag_autowire) log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); @@ -1544,13 +1607,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); int mem_width, mem_size, addr_bits; is_signed = id2ast->is_signed; + wire->is_signed = is_signed; id2ast->meminfo(mem_width, mem_size, addr_bits); RTLIL::SigSpec addr_sig = children[0]->genRTLIL(); @@ -1582,7 +1646,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1645,8 +1709,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) else cellname = str; + check_unique_id(current_module, cellname, this, "procedural assertion"); RTLIL::Cell *cell = current_module->addCell(cellname, celltype); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -1687,11 +1752,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { int port_counter = 0, para_counter = 0; - if (current_module->count_id(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of cell `%s'!\n", str.c_str()); - - RTLIL::Cell *cell = current_module->addCell(str, ""); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "cell"); + RTLIL::Cell *cell = current_module->addCell(id, ""); + set_src_attr(cell, this); // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass cell->set_bool_attribute(ID::module_not_derived); @@ -1740,7 +1804,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // non-trivial signed nodes are indirected through // signed wires to enable sign extension RTLIL::IdString wire_name = NEW_ID; - RTLIL::Wire *wire = current_module->addWire(wire_name, arg->bits.size()); + RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig)); wire->is_signed = true; current_module->connect(wire, sig); sig = wire; @@ -1855,7 +1919,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, location.first_line, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); cell->parameters[ID::WIDTH] = width; if (attributes.count(ID::reg)) { @@ -1866,7 +1930,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } Wire *wire = current_module->addWire(myid + "_wire", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); cell->setPort(ID::Y, wire); is_signed = sign_hint; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index d4242f1e7..5c4dd290f 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) return true; } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ + log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); + log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); + log_assert(prefix.back() == '.'); + if (str.front() == '\\') + return prefix + str.substr(1); + return prefix + str; +} + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -561,10 +571,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, static bool deep_recursion_warning = false; if (recursion_counter++ == 1000 && deep_recursion_warning) { - log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n"); + log_warning("Deep recursion in AST simplifier.\nDoes this design contain overly long or deeply nested expressions, or excessive recursion?\n"); deep_recursion_warning = false; } + static bool unevaluated_tern_branch = false; + AstNode *newNode = NULL; bool did_something = false; @@ -748,6 +760,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") if (type == AST_MODULE) { current_scope.clear(); + std::set<std::string> existing; + int counter = 0; + label_genblks(existing, counter); std::map<std::string, AstNode*> this_wire_scope; for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; @@ -928,7 +943,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic) 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.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str.c_str(), loc_string().c_str()); if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { bool is_rand_reg = false; if (children[1]->type == AST_FCALL) { @@ -942,7 +957,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, is_rand_reg = true; } if (!is_rand_reg) - log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str.c_str(), loc_string().c_str()); } children[0]->was_checked = true; } @@ -1078,7 +1093,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; case AST_TERNARY: - detect_width_simple = true; child_0_is_self_determined = true; break; @@ -1111,6 +1125,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detectSignWidth(width_hint, sign_hint); if (type == AST_TERNARY) { + if (width_hint < 0) { + while (!children[0]->basic_prep && children[0]->simplify(true, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; + AstNode *chosen = get_tern_choice().first; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; + while (!children[1]->basic_prep && children[1]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1]; + while (!children[2]->basic_prep && children[2]->simplify(false, false, in_lvalue, stage, -1, false, in_param)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch; + detectSignWidth(width_hint, sign_hint); + } int width_hint_left, width_hint_right; bool sign_hint_left, sign_hint_right; bool found_real_left, found_real_right; @@ -1174,6 +1206,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (size_t i = 0; i < children.size(); i++) { bool did_something_here = true; bool backup_flag_autowire = flag_autowire; + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; if ((type == AST_GENFOR || type == AST_FOR) && i >= 3) break; if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1) @@ -1186,6 +1219,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; if (type == AST_DEFPARAM && i == 0) flag_autowire = true; + if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) { + AstNode *chosen = get_tern_choice().first; + unevaluated_tern_branch = chosen && chosen != children[i]; + } while (did_something_here && i < children.size()) { bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; int width_hint_here = width_hint; @@ -1205,11 +1242,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_block = this; current_block_child = children[i]; } - if (!in_param_here && type == AST_FCALL) { - bool recommend_const_eval = false; - bool require_const_eval = has_const_only_constructs(recommend_const_eval); - in_param_here = recommend_const_eval || require_const_eval; - } if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) current_top_block = children[i]; if (i == 0 && child_0_is_self_determined) @@ -1230,6 +1262,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; } flag_autowire = backup_flag_autowire; + unevaluated_tern_branch = backup_unevaluated_tern_branch; } for (auto &attr : attributes) { while (attr.second->simplify(true, false, false, stage, -1, false, true)) @@ -1330,6 +1363,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) { // replace with wire representing the packed structure newNode = make_packed_struct(template_node, str); + // add original input/output attribute to resolved wire + newNode->is_input = this->is_input; + newNode->is_output = this->is_output; current_scope[str] = this; goto apply_newNode; } @@ -1852,19 +1888,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // expand body int index = varbuf->children[0]->integer; - if (body_ast->type == AST_GENBLOCK) - buf = body_ast->clone(); - else - buf = new AstNode(AST_GENBLOCK, body_ast->clone()); - if (buf->str.empty()) { - std::stringstream sstr; - sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); - buf->str = sstr.str(); - } - std::map<std::string, std::string> name_map; + log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); + log_assert(!body_ast->str.empty()); + buf = body_ast->clone(); + std::stringstream sstr; sstr << buf->str << "[" << index << "]."; - buf->expand_genblock(varbuf->str, sstr.str(), name_map); + std::string prefix = sstr.str(); + + // create a scoped localparam for the current value of the loop variable + AstNode *local_index = varbuf->clone(); + size_t pos = local_index->str.rfind('.'); + if (pos != std::string::npos) // remove outer prefix + local_index->str = "\\" + local_index->str.substr(pos + 1); + local_index->str = prefix_id(prefix, local_index->str); + current_scope[local_index->str] = local_index; + current_ast_mod->children.push_back(local_index); + + buf->expand_genblock(prefix); if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { @@ -1912,14 +1953,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) - log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); + { + log_assert(!VERILOG_FRONTEND::sv_mode); + log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); + } } // transform block with name if (type == AST_BLOCK && !str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) @@ -1939,8 +1982,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENBLOCK && children.size() != 0) { if (!str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); } for (size_t i = 0; i < children.size(); i++) { @@ -1976,8 +2018,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = new AstNode(AST_GENBLOCK, buf); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -2055,8 +2096,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = selected_case->clone(); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -3049,6 +3089,93 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (str == "\\$countbits") { + if (children.size() < 2) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected at least 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + std::vector<RTLIL::State> control_bits; + + // Determine which bits to count + for (size_t i = 1; i < children.size(); i++) { + AstNode *node = children[i]; + while (node->simplify(true, false, false, stage, -1, false, false)) { } + if (node->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant control bit argument.\n", str.c_str()); + if (node->bits.size() != 1) + log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with control bit width != 1.\n", str.c_str()); + control_bits.push_back(node->bits[0]); + } + + // Detect width of exp (first argument of $countbits) + int exp_width = -1; + bool exp_sign = false; + AstNode *exp = children[0]; + exp->detectSignWidth(exp_width, exp_sign, NULL); + + newNode = mkconst_int(0, false); + + for (int i = 0; i < exp_width; i++) { + // Generate nodes for: exp << i >> ($size(exp) - 1) + // ^^ ^^ + AstNode *lsh_node = new AstNode(AST_SHIFT_LEFT, exp->clone(), mkconst_int(i, false)); + AstNode *rsh_node = new AstNode(AST_SHIFT_RIGHT, lsh_node, mkconst_int(exp_width - 1, false)); + + AstNode *or_node = nullptr; + + for (RTLIL::State control_bit : control_bits) { + // Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit + // ^^^ + AstNode *eq_node = new AstNode(AST_EQX, rsh_node->clone(), mkconst_bits({control_bit}, false)); + + // Or the result for each checked bit value + if (or_node) + or_node = new AstNode(AST_LOGIC_OR, or_node, eq_node); + else + or_node = eq_node; + } + + // We should have at least one element in control_bits, + // because we checked for the number of arguments above + log_assert(or_node != nullptr); + + delete rsh_node; + + // Generate node for adding with result of previous bit + newNode = new AstNode(AST_ADD, newNode, or_node); + } + + goto apply_newNode; + } + + if (str == "\\$countones" || str == "\\$isunknown" || str == "\\$onehot" || str == "\\$onehot0") { + if (children.size() != 1) + log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + AstNode *countbits = clone(); + countbits->str = "\\$countbits"; + + if (str == "\\$countones") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = countbits; + } else if (str == "\\$isunknown") { + countbits->children.push_back(mkconst_bits({RTLIL::Sx}, false)); + countbits->children.push_back(mkconst_bits({RTLIL::Sz}, false)); + newNode = new AstNode(AST_GT, countbits, mkconst_int(0, false)); + } else if (str == "\\$onehot") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_EQ, countbits, mkconst_int(1, false)); + } else if (str == "\\$onehot0") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_LE, countbits, mkconst_int(1, false)); + } else { + log_abort(); + } + + goto apply_newNode; + } + if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) { AstNode *dpi_decl = current_scope[str]; @@ -3156,16 +3283,21 @@ skip_dynamic_range_lvalue_expansion:; log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str()); } - AstNode *decl = current_scope[str]; std::stringstream sstr; - sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; + sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); - bool recommend_const_eval = false; - bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); - if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) + AstNode *decl = current_scope[str]; + if (unevaluated_tern_branch && decl->is_recursive_function()) + goto replace_fcall_later; + decl = decl->clone(); + decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion + decl->expand_genblock(prefix); + + if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype)) { + bool require_const_eval = decl->has_const_only_constructs(); bool all_args_const = true; for (auto child : children) { while (child->simplify(true, false, false, 1, -1, false, true)) { } @@ -3174,12 +3306,14 @@ skip_dynamic_range_lvalue_expansion:; } if (all_args_const) { - AstNode *func_workspace = current_scope[str]->clone(); - func_workspace->str = NEW_ID.str(); - func_workspace->replace_result_wire_name_in_function(str, func_workspace->str); - newNode = func_workspace->eval_const_function(this); + AstNode *func_workspace = decl->clone(); + func_workspace->str = prefix_id(prefix, "$result"); + newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); delete func_workspace; - goto apply_newNode; + if (newNode) { + delete decl; + goto apply_newNode; + } } if (in_param) @@ -3189,8 +3323,6 @@ skip_dynamic_range_lvalue_expansion:; } size_t arg_count = 0; - 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; @@ -3200,16 +3332,17 @@ skip_dynamic_range_lvalue_expansion:; log_assert(type == AST_FCALL); AstNode *wire = NULL; + std::string res_name = prefix_id(prefix, "$result"); for (auto child : decl->children) - if (child->type == AST_WIRE && child->str == str) + if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); log_assert(wire != NULL); - wire->str = prefix + str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); while (wire->simplify(true, false, false, 1, -1, false, false)) { } @@ -3253,7 +3386,6 @@ skip_dynamic_range_lvalue_expansion:; if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { AstNode *wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3315,7 +3447,6 @@ skip_dynamic_range_lvalue_expansion:; else { wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3326,15 +3457,11 @@ skip_dynamic_range_lvalue_expansion:; wire_cache[child->str] = wire; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); - added_mod_children.push_back(wire); } - if (child->type == AST_WIRE) - while (wire->simplify(true, false, false, 1, -1, false, false)) { } - - replace_rules[child->str] = wire->str; - current_scope[wire->str] = wire; + while (wire->simplify(true, false, false, 1, -1, false, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -3363,6 +3490,8 @@ skip_dynamic_range_lvalue_expansion:; range->children.push_back(mkconst_int(0, true)); } } + // updates the sizing + while (wire->simplify(true, false, false, 1, -1, false, false)) { } continue; } AstNode *wire_id = new AstNode(AST_IDENTIFIER); @@ -3378,18 +3507,9 @@ skip_dynamic_range_lvalue_expansion:; } } - for (auto child : added_mod_children) { - child->replace_ids(prefix, replace_rules); - while (child->simplify(true, false, false, 1, -1, false, false)) { } - } - for (auto child : decl->children) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) - { - AstNode *stmt = child->clone(); - stmt->replace_ids(prefix, replace_rules); - new_stmts.push_back(stmt); - } + new_stmts.push_back(child->clone()); new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3402,10 +3522,11 @@ skip_dynamic_range_lvalue_expansion:; } replace_fcall_with_id: + delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; - str = prefix + str; + str = prefix_id(prefix, "$result"); } if (type == AST_TCALL) str = ""; @@ -3605,24 +3726,9 @@ replace_fcall_later:; case AST_TERNARY: if (children[0]->isConst()) { - bool found_sure_true = false; - bool found_maybe_true = false; - - if (children[0]->type == AST_CONSTANT) - for (auto &bit : children[0]->bits) { - if (bit == RTLIL::State::S1) - found_sure_true = true; - if (bit > RTLIL::State::S1) - found_maybe_true = true; - } - else - found_sure_true = children[0]->asReal(sign_hint) != 0; - - AstNode *choice = NULL, *not_choice = NULL; - if (found_sure_true) - choice = children[1], not_choice = children[2]; - else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; + auto pair = get_tern_choice(); + AstNode *choice = pair.first; + AstNode *not_choice = pair.second; if (choice != NULL) { if (choice->type == AST_CONSTANT) { @@ -3856,63 +3962,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m return block; } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix) { - // `original_scope` defaults to false, and is used to prevent the premature - // prefixing of items in named sub-blocks - - if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { - if (children.empty()) { - current_scope[index_var]->children[0]->cloneInto(this); - } else { - AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); - p->str = stringf("$genval$%d", autoidx++); - current_ast_mod->children.push_back(p); - str = p->str; - id2ast = p; - } - } - if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { - if (name_map.count(str) > 0) { - str = name_map[str]; - } else { - // remap the prefix of this ident if it is a local generate scope - size_t pos = str.rfind('.'); - if (pos != std::string::npos) { - std::string existing_prefix = str.substr(0, pos); - if (name_map.count(existing_prefix) > 0) { - str = name_map[existing_prefix] + str.substr(pos); - } + log_assert(!str.empty()); + + // search starting in the innermost scope and then stepping outward + for (size_t ppos = prefix.size() - 1; ppos; --ppos) { + if (prefix.at(ppos) != '.') continue; + + std::string new_prefix = prefix.substr(0, ppos + 1); + auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { + std::string new_name = prefix_id(new_prefix, ident); + if (current_scope.count(new_name)) + return new_name; + return {}; + }; + + // attempt to resolve the full identifier + std::string resolved = attempt_resolve(str); + if (!resolved.empty()) { + str = resolved; + break; } - } - } - std::map<std::string, std::string> backup_name_map; - - auto prefix_node = [&](AstNode* child) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; + // attempt to resolve hierarchical prefixes within the identifier, + // as the prefix could refer to a local scope which exists but + // hasn't yet been elaborated + for (size_t spos = str.size() - 1; spos; --spos) { + if (str.at(spos) != '.') continue; + resolved = attempt_resolve(str.substr(0, spos)); + if (!resolved.empty()) { + str = resolved + str.substr(spos); + ppos = 1; // break outer loop + break; + } + } - // if within a nested scope - if (!original_scope) { - // this declaration shadows anything in the parent scope(s) - name_map[child->str] = child->str; - return; } + } - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = child->str.rfind('.'); - if (pos == std::string::npos) - pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - - name_map[child->str] = new_name; + auto prefix_node = [&prefix](AstNode* child) { + if (child->str.empty()) return; + std::string new_name = prefix_id(prefix, child->str); if (child->type == AST_FUNCTION) child->replace_result_wire_name_in_function(child->str, new_name); else @@ -3964,43 +4059,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma continue; // functions/tasks may reference wires, constants, etc. in this scope if (child->type == AST_FUNCTION || child->type == AST_TASK) - child->expand_genblock(index_var, prefix, name_map, false); - // continue prefixing if this child block is anonymous - else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) - child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); - else - child->expand_genblock(index_var, prefix, name_map, original_scope); - } - + continue; + // named blocks pick up the current prefix and will expanded later + if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) + continue; - if (backup_name_map.size() > 0) - name_map.swap(backup_name_map); + child->expand_genblock(prefix); + } } -// rename stuff (used when tasks of functions are instantiated) -void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set<std::string>& existing, int &counter) { - if (type == AST_BLOCK) - { - std::map<std::string, std::string> new_rules = rules; - std::string new_prefix = prefix + str; - - for (auto child : children) - if (child->type == AST_WIRE) { - new_rules[child->str] = new_prefix + child->str; - child->str = new_prefix + child->str; - } + switch (type) { + case AST_GENIF: + case AST_GENFOR: + case AST_GENCASE: + // seeing a proper generate control flow construct increments the + // counter once + ++counter; + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; - for (auto child : children) - if (child->type != AST_WIRE) - child->replace_ids(new_prefix, new_rules); + case AST_GENBLOCK: { + // if this block is unlabeled, generate its corresponding unique name + for (int padding = 0; str.empty(); ++padding) { + std::string candidate = "\\genblk"; + for (int i = 0; i < padding; ++i) + candidate += '0'; + candidate += std::to_string(counter); + if (!existing.count(candidate)) + str = candidate; + } + // within a genblk, the counter starts fresh + std::set<std::string> existing_local = existing; + int counter_local = 0; + for (AstNode *child : children) + child->label_genblks(existing_local, counter_local); + break; } - else - { - if (type == AST_IDENTIFIER && rules.count(str) > 0) - str = rules.at(str); - for (auto child : children) - child->replace_ids(prefix, rules); + + default: + // track names which could conflict with implicit genblk names + if (str.rfind("\\genblk", 0) == 0) + existing.insert(str); + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; } } @@ -4492,33 +4599,12 @@ bool AstNode::detect_latch(const std::string &var) } } -bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +bool AstNode::has_const_only_constructs() { - std::set<std::string> visited; - return has_const_only_constructs(visited, recommend_const_eval); -} - -bool AstNode::has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval) -{ - if (type == AST_FUNCTION || type == AST_TASK) - { - if (visited.count(str)) - { - recommend_const_eval = true; - return false; - } - visited.insert(str); - } - - if (type == AST_FOR) - recommend_const_eval = true; if (type == AST_WHILE || type == AST_REPEAT) return true; - if (type == AST_FCALL && current_scope.count(str)) - if (current_scope[str]->has_const_only_constructs(visited, recommend_const_eval)) - return true; for (auto child : children) - if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval)) + if (child->has_const_only_constructs()) return true; return false; } @@ -4534,19 +4620,26 @@ bool AstNode::is_simple_const_expr() } // helper function for AstNode::eval_const_function() -void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed) { if (type == AST_IDENTIFIER && variables.count(str)) { int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); if (!children.empty()) { - if (children.size() != 1 || children.at(0)->type != AST_RANGE) - log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - children.at(0)->replace_variables(variables, fcall); + if (children.size() != 1 || children.at(0)->type != AST_RANGE) { + if (!must_succeed) + return false; + log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s: ...called from here.\n", + fcall->loc_string().c_str()); + } + if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) + return false; while (simplify(true, false, false, 1, -1, false, true)) { } - if (!children.at(0)->range_valid) - log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!children.at(0)->range_valid) { + if (!must_succeed) + return false; + log_file_error(filename, location.first_line, "Non-constant range\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } 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); } @@ -4556,19 +4649,22 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); newNode->cloneInto(this); delete newNode; - return; + return true; } for (auto &child : children) - child->replace_variables(variables, fcall); + if (!child->replace_variables(variables, fcall, must_succeed)) + return false; + return true; } -// evaluate functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall) +// attempt to statically evaluate a functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { - std::map<std::string, AstNode*> backup_scope; + std::map<std::string, AstNode*> backup_scope = current_scope; std::map<std::string, AstNode::varinfo_t> variables; AstNode *block = new AstNode(AST_BLOCK); + AstNode *result = nullptr; size_t argidx = 0; for (auto child : children) @@ -4590,24 +4686,37 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WIRE) { while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!stmt->range_valid) - log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", - stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); - variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); - variables[stmt->str].is_signed = stmt->is_signed; + if (!stmt->range_valid) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s: ... called from here.\n", + stmt->str.c_str(), fcall->loc_string().c_str()); + } + AstNode::varinfo_t &variable = variables[stmt->str]; + int width = abs(stmt->range_left - stmt->range_right) + 1; + // if this variable has already been declared as an input, check the + // sizes match if it already had an explicit size + if (variable.arg && variable.explicitly_sized && variable.val.size() != width) { + log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str()); + } + variable.val = RTLIL::Const(RTLIL::State::Sx, width); + variable.offset = min(stmt->range_left, stmt->range_right); + variable.is_signed = stmt->is_signed; + variable.explicitly_sized = stmt->children.size() && + stmt->children.back()->type == AST_RANGE; + // identify the argument corresponding to this wire, if applicable if (stmt->is_input && argidx < fcall->children.size()) { - int width = variables[stmt->str].val.bits.size(); - auto* arg_node = fcall->children.at(argidx++); - if (arg_node->type == AST_CONSTANT) { - variables[stmt->str].val = arg_node->bitsAsConst(width); + variable.arg = fcall->children.at(argidx++); + } + // load the constant arg's value into this variable + if (variable.arg) { + if (variable.arg->type == AST_CONSTANT) { + variable.val = variable.arg->bitsAsConst(width); } else { - log_assert(arg_node->type == AST_REALVALUE); - variables[stmt->str].val = arg_node->realAsConst(width); + log_assert(variable.arg->type == AST_REALVALUE); + variable.val = variable.arg->realAsConst(width); } } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); @@ -4620,8 +4729,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); @@ -4632,32 +4739,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && stmt->children.at(0)->children.at(0)->type == AST_RANGE) - stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); - stmt->children.at(1)->replace_variables(variables, fcall); + if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) + goto finished; + if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) + goto finished; while (stmt->simplify(true, false, false, 1, -1, false, true)) { } if (stmt->type != AST_ASSIGN_EQ) continue; - if (stmt->children.at(1)->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(1)->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here. X\n", + fcall->loc_string().c_str()); + } - if (stmt->children.at(0)->type != AST_IDENTIFIER) - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(0)->type != AST_IDENTIFIER) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } - if (!variables.count(stmt->children.at(0)->str)) - log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!variables.count(stmt->children.at(0)->str)) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } 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()); } else { AstNode *range = stmt->children.at(0)->children.at(0); - if (!range->range_valid) - log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!range->range_valid) { + if (!must_succeed) + goto finished; + log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } 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]; @@ -4684,12 +4805,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WHILE) { AstNode *cond = stmt->children.at(0)->clone(); - cond->replace_variables(variables, fcall); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; while (cond->simplify(true, false, false, 1, -1, false, true)) { } - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -4705,12 +4830,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_REPEAT) { AstNode *num = stmt->children.at(0)->clone(); - num->replace_variables(variables, fcall); + if (!num->replace_variables(variables, fcall, must_succeed)) + goto finished; while (num->simplify(true, false, false, 1, -1, false, true)) { } - if (num->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (num->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -4724,7 +4853,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_CASE) { AstNode *expr = stmt->children.at(0)->clone(); - expr->replace_variables(variables, fcall); + if (!expr->replace_variables(variables, fcall, must_succeed)) + goto finished; while (expr->simplify(true, false, false, 1, -1, false, true)) { } AstNode *sel_case = NULL; @@ -4741,14 +4871,18 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) { AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); - cond->replace_variables(variables, fcall); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; cond = new AstNode(AST_EQ, expr->clone(), cond); while (cond->simplify(true, false, false, 1, -1, false, true)) { } - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } found_match = cond->asBool(); delete cond; @@ -4770,6 +4904,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_BLOCK) { + if (!stmt->str.empty()) + stmt->expand_genblock(stmt->str + "."); + block->children.erase(block->children.begin()); block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); stmt->children.clear(); @@ -4777,20 +4914,20 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) continue; } - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!must_succeed) + goto finished; + log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); log_abort(); } - delete block; + result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); - for (auto &it : backup_scope) - if (it.second == NULL) - current_scope.erase(it.first); - else - current_scope[it.first] = it.second; +finished: + delete block; + current_scope = backup_scope; - return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + return result; } void AstNode::allocateDefaultEnumValues() @@ -4821,4 +4958,54 @@ void AstNode::allocateDefaultEnumValues() } } +bool AstNode::is_recursive_function() const +{ + std::set<const AstNode *> visited; + std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) { + if (visited.count(node)) + return node == this; + visited.insert(node); + if (node->type == AST_FCALL) { + auto it = current_scope.find(node->str); + if (it != current_scope.end() && visit(it->second)) + return true; + } + for (const AstNode *child : node->children) { + if (visit(child)) + return true; + } + return false; + }; + + log_assert(type == AST_FUNCTION); + return visit(this); +} + +std::pair<AstNode*, AstNode*> AstNode::get_tern_choice() +{ + if (!children[0]->isConst()) + return {}; + + bool found_sure_true = false; + bool found_maybe_true = false; + + if (children[0]->type == AST_CONSTANT) + for (auto &bit : children[0]->bits) { + if (bit == RTLIL::State::S1) + found_sure_true = true; + if (bit > RTLIL::State::S1) + found_maybe_true = true; + } + else + found_sure_true = children[0]->asReal(true) != 0; + + AstNode *choice = nullptr, *not_choice = nullptr; + if (found_sure_true) + choice = children[1], not_choice = children[2]; + else if (!found_maybe_true) + choice = children[2], not_choice = children[1]; + + return {choice, not_choice}; +} + YOSYS_NAMESPACE_END diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1b34aaf3a..312f6d3be 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -72,10 +72,17 @@ struct JsonNode break; } - if ('0' <= ch && ch <= '9') + if (('0' <= ch && ch <= '9') || ch == '-') { + bool negative = false; type = 'N'; - data_number = ch - '0'; + if (ch == '-') { + data_number = 0; + negative = true; + } else { + data_number = ch - '0'; + } + data_string += ch; while (1) @@ -97,6 +104,7 @@ struct JsonNode data_string += ch; } + data_number = negative ? -data_number : data_number; data_string = ""; break; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index cf3bf1070..7aa3ebcbb 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -49,14 +49,17 @@ USING_YOSYS_NAMESPACE #include "VeriWrite.h" #include "VhdlUnits.h" #include "VeriLibrary.h" + +#if defined(YOSYSHQ_VERIFIC_INITSTATE) || defined(YOSYSHQ_VERIFIC_TEMPLATES) || defined(YOSYSHQ_VERIFIC_FORMALAPPS) #include "VeriExtensions.h" +#endif -#ifndef SYMBIOTIC_VERIFIC_API_VERSION -# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#ifndef YOSYSHQ_VERIFIC_API_VERSION +# error "Only YosysHQ flavored Verific is supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific." #endif -#if SYMBIOTIC_VERIFIC_API_VERSION < 20201101 -# error "Please update your version of Symbiotic EDA flavored Verific." +#if YOSYSHQ_VERIFIC_API_VERSION < 20210103 +# error "Please update your version of YosysHQ flavored Verific." #endif #ifdef __clang__ @@ -1471,7 +1474,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se continue; } - if (inst->Type() == PRIM_SEDA_INITSTATE) +#ifdef YOSYSHQ_VERIFIC_INITSTATE + if (inst->Type() == PRIM_YOSYSHQ_INITSTATE) { SigBit initstate = module->Initstate(new_verific_id(inst)); SigBit sig_o = net_map_at(inst->GetOutput()); @@ -1480,7 +1484,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (!mode_keep) continue; } - +#endif if (!mode_keep && verific_sva_prims.count(inst->Type())) { if (verific_verbose) log(" skipping SVA cell in non k-mode\n"); @@ -1958,9 +1962,10 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par for (const auto &i : parameters) verific_params.Insert(i.first.c_str(), i.second.c_str()); +#ifdef YOSYSHQ_VERIFIC_INITSTATE InitialAssertionRewriter rw; rw.RegisterCallBack(); - +#endif if (top.empty()) { netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); } @@ -2199,7 +2204,7 @@ struct VerificPass : public Pass { log("\n"); log(" verific -app <application>..\n"); log("\n"); - log("Execute SEDA formal application on loaded Verilog files.\n"); + log("Execute YosysHQ formal application on loaded Verilog files.\n"); log("\n"); log("Application options:\n"); log("\n"); @@ -2217,7 +2222,7 @@ struct VerificPass : public Pass { log("\n"); log("Applications:\n"); log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_FORMALAPPS) VerificFormalApplications vfa; log("%s\n",vfa.GetHelp().c_str()); #else @@ -2243,18 +2248,18 @@ struct VerificPass : public Pass { log("\n"); log("Templates:\n"); log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC +#if defined(YOSYS_ENABLE_VERIFIC) && defined(YOSYSHQ_VERIFIC_TEMPLATES) VerificTemplateGenerator vfg; log("%s\n",vfg.GetHelp().c_str()); #else log(" WARNING: Templates only available in commercial build.\n"); log("\n"); #endif - log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"); - log("https://www.symbioticeda.com/seda-suite\n"); + log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n"); + log("https://www.yosyshq.com/\n"); log("\n"); - log("Contact office@symbioticeda.com for free evaluation\n"); - log("binaries of Symbiotic EDA Suite.\n"); + log("Contact office@yosyshq.com for free evaluation\n"); + log("binaries of YosysHQ Tabby CAD Suite.\n"); log("\n"); } #ifdef YOSYS_ENABLE_VERIFIC @@ -2265,11 +2270,11 @@ struct VerificPass : public Pass { if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n"); @@ -2494,6 +2499,7 @@ struct VerificPass : public Pass { goto check_error; } +#ifdef YOSYSHQ_VERIFIC_FORMALAPPS if (argidx < GetSize(args) && args[argidx] == "-app") { if (!(argidx+1 < GetSize(args))) @@ -2587,7 +2593,7 @@ struct VerificPass : public Pass { } goto check_error; } - +#endif if (argidx < GetSize(args) && args[argidx] == "-pp") { const char* filename = nullptr; @@ -2630,6 +2636,7 @@ struct VerificPass : public Pass { goto check_error; } +#ifdef YOSYSHQ_VERIFIC_TEMPLATES if (argidx < GetSize(args) && args[argidx] == "-template") { if (!(argidx+1 < GetSize(args))) @@ -2713,7 +2720,7 @@ struct VerificPass : public Pass { fclose(of); goto check_error; } - +#endif if (GetSize(args) > argidx && args[argidx] == "-import") { std::set<Netlist*> nl_todo, nl_done; @@ -2798,9 +2805,10 @@ struct VerificPass : public Pass { std::set<std::string> top_mod_names; +#ifdef YOSYSHQ_VERIFIC_INITSTATE InitialAssertionRewriter rw; rw.RegisterCallBack(); - +#endif if (mode_all) { log("Running hier_tree::ElaborateAll().\n"); @@ -2926,11 +2934,11 @@ struct VerificPass : public Pass { void execute(std::vector<std::string>, RTLIL::Design *) override { log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); } #endif } VerificPass; diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 632043b6f..1f5da1b1d 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1759,6 +1759,11 @@ struct VerificSvaImporter clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); } + // accept in disable case + + if (clocking.disable_sig != State::S0) + sig_a_q = module->Or(NEW_ID, sig_a_q, clocking.disable_sig); + // generate fair/live cell RTLIL::Cell *c = nullptr; diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 752f7a7a8..de707593f 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -390,13 +390,16 @@ static void input_file(std::istream &f, std::string filename) // the argument list); false if we finished with ','. static bool read_argument(std::string &dest) { + skip_spaces(); std::vector<char> openers; for (;;) { - skip_spaces(); std::string tok = next_token(true); if (tok == ")") { - if (openers.empty()) + if (openers.empty()) { + while (dest.size() && (dest.back() == ' ' || dest.back() == '\t')) + dest = dest.substr(0, dest.size() - 1); return true; + } if (openers.back() != '(') log_error("Mismatched brackets in macro argument: %c and %c.\n", openers.back(), tok[0]); @@ -474,7 +477,16 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) std::string name = tok.substr(1); std::string skipped_spaces = skip_spaces(); tok = next_token(false); - if (tok == "(" && body->has_args) { + if (body->has_args) { + if (tok != "(") { + if (tok.size() == 1 && iscntrl(tok[0])) { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02x", tok[0]); + tok = buf; + } + log_error("Expected to find '(' to begin macro arguments for '%s', but instead found '%s'\n", + name.c_str(), tok.c_str()); + } std::vector<std::string> args; bool done = false; while (!done) { diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f2241066f..66772a097 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -234,7 +234,7 @@ static bool isUserType(std::string &s) "automatic" { return TOK_AUTOMATIC; } "unique" { SV_KEYWORD(TOK_UNIQUE); } -"unique0" { SV_KEYWORD(TOK_UNIQUE); } +"unique0" { SV_KEYWORD(TOK_UNIQUE0); } "priority" { SV_KEYWORD(TOK_PRIORITY); } "always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } @@ -267,6 +267,7 @@ static bool isUserType(std::string &s) "int" { SV_KEYWORD(TOK_INT); } "byte" { SV_KEYWORD(TOK_BYTE); } "shortint" { SV_KEYWORD(TOK_SHORTINT); } +"longint" { SV_KEYWORD(TOK_LONGINT); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } "s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 6c4b06d7f..476ee68ad 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -253,6 +253,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) struct specify_rise_fall *specify_rise_fall_ptr; bool boolean; char ch; + int integer; } %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE @@ -277,16 +278,18 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED %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 -%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_UNION +%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_UNIQUE0 TOK_PRIORITY +%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_LONGINT TOK_UNION %token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN -%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 <ast> range range_or_multirange non_opt_range non_opt_multirange +%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number %type <string> type_name -%type <ast> opt_enum_init enum_type struct_type non_wire_data_type -%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff +%type <ast> opt_enum_init enum_type struct_type non_wire_data_type func_return_type +%type <boolean> opt_property always_comb_or_latch always_or_always_ff +%type <boolean> opt_signedness_default_signed opt_signedness_default_unsigned +%type <integer> integer_atom_type %type <al> attr case_attr %type <ast> struct_union @@ -609,36 +612,34 @@ interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | modport_stmt; +mintypmax_expr: + expr { delete $1; } | + expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; + non_opt_delay: '#' TOK_ID { delete $2; } | '#' TOK_CONSTVAL { delete $2; } | '#' TOK_REALVAL { delete $2; } | - '#' '(' expr ')' { delete $3; } | - '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; }; + '#' '(' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ',' mintypmax_expr ')'; delay: non_opt_delay | %empty; -wire_type: - { - astbuf3 = new AstNode(AST_WIRE); - current_wire_rand = false; - current_wire_const = false; - } wire_type_token_list { - $$ = astbuf3; - SET_RULE_LOC(@$, @2, @$); - }; +io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; -wire_type_token_list: - wire_type_token | - wire_type_token_list wire_type_token | - wire_type_token_io | - hierarchical_type_id { - astbuf3->is_custom_type = true; - astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); - astbuf3->children.back()->str = *$1; - delete $1; - }; +non_io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_const_rand wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + +wire_type: + io_wire_type | + non_io_wire_type; wire_type_token_io: TOK_INPUT { @@ -652,8 +653,32 @@ wire_type_token_io: astbuf3->is_output = true; }; +wire_type_signedness: + TOK_SIGNED { astbuf3->is_signed = true; } | + TOK_UNSIGNED { astbuf3->is_signed = false; } | + %empty; + +wire_type_const_rand: + TOK_RAND TOK_CONST { + current_wire_rand = true; + current_wire_const = true; + } | + TOK_CONST { + current_wire_const = true; + } | + TOK_RAND { + current_wire_rand = true; + } | + %empty; + +opt_wire_type_token: + wire_type_token | %empty; + wire_type_token: - TOK_WIRE { + hierarchical_type_id { + astbuf3->is_custom_type = true; + astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); + astbuf3->children.back()->str = *$1; } | TOK_WOR { astbuf3->is_wor = true; @@ -661,20 +686,27 @@ wire_type_token: TOK_WAND { astbuf3->is_wand = true; } | + // wires + TOK_WIRE { + } | + TOK_WIRE logic_type { + } | + // regs TOK_REG { astbuf3->is_reg = true; } | - TOK_LOGIC { - astbuf3->is_logic = true; + TOK_VAR TOK_REG { + astbuf3->is_reg = true; } | + // logics TOK_VAR { astbuf3->is_logic = true; } | - TOK_INTEGER { - astbuf3->is_reg = true; - astbuf3->range_left = 31; - astbuf3->range_right = 0; - astbuf3->is_signed = true; + TOK_VAR logic_type { + astbuf3->is_logic = true; + } | + logic_type { + astbuf3->is_logic = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; @@ -682,17 +714,24 @@ wire_type_token: astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; + }; + +logic_type: + TOK_LOGIC { } | - TOK_SIGNED { + integer_atom_type { + astbuf3->range_left = $1 - 1; + astbuf3->range_right = 0; astbuf3->is_signed = true; - } | - TOK_RAND { - current_wire_rand = true; - } | - TOK_CONST { - current_wire_const = true; }; +integer_atom_type: + TOK_INTEGER { $$ = 32; } | + TOK_INT { $$ = 32; } | + TOK_SHORTINT { $$ = 16; } | + TOK_LONGINT { $$ = 64; } | + TOK_BYTE { $$ = 8; } ; + non_opt_range: '[' expr ':' expr ']' { $$ = new AstNode(AST_RANGE); @@ -737,15 +776,11 @@ range_or_multirange: range { $$ = $1; } | non_opt_multirange { $$ = $1; }; -range_or_signed_int: - range { $$ = $1; } - | TOK_INTEGER { $$ = makeRange(); } - ; - module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | + module_body gen_block | module_body ';' | %empty; @@ -811,29 +846,58 @@ task_func_decl: current_function_or_task = NULL; ast_stack.pop_back(); } | - attr TOK_FUNCTION opt_automatic opt_signed range_or_signed_int TOK_ID { + attr TOK_FUNCTION opt_automatic func_return_type TOK_ID { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$6; + current_function_or_task->str = *$5; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); ast_stack.push_back(current_function_or_task); AstNode *outreg = new AstNode(AST_WIRE); - outreg->str = *$6; - outreg->is_signed = $4; + outreg->str = *$5; + outreg->is_signed = false; outreg->is_reg = true; - if ($5 != NULL) { - outreg->children.push_back($5); - outreg->is_signed = $4 || $5->is_signed; - $5->is_signed = false; + if ($4 != NULL) { + outreg->children.push_back($4); + outreg->is_signed = $4->is_signed; + $4->is_signed = false; } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $6; + delete $5; } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; +func_return_type: + opt_type_vec opt_signedness_default_unsigned { + $$ = makeRange(0, 0, $2); + } | + opt_type_vec opt_signedness_default_unsigned non_opt_range { + $$ = $3; + $$->is_signed = $2; + } | + integer_atom_type opt_signedness_default_signed { + $$ = makeRange($1 - 1, 0, $2); + }; + +opt_type_vec: + %empty + | TOK_REG + | TOK_LOGIC + ; + +opt_signedness_default_signed: + %empty { $$ = true; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; +opt_signedness_default_unsigned: + %empty { $$ = false; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; + dpi_function_arg: TOK_ID TOK_ID { current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); @@ -859,14 +923,6 @@ opt_automatic: TOK_AUTOMATIC | %empty; -opt_signed: - TOK_SIGNED { - $$ = true; - } | - %empty { - $$ = false; - }; - task_func_args_opt: '(' ')' | %empty | '(' { albuf = nullptr; @@ -1349,11 +1405,8 @@ param_signed: } | %empty; param_integer: - TOK_INTEGER { - 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)); - astbuf1->is_signed = true; + type_atom { + astbuf1->is_reg = false; }; param_real: @@ -1369,7 +1422,13 @@ param_range: }; param_integer_type: param_integer param_signed; -param_range_type: type_vec param_signed param_range; +param_range_type: + type_vec param_signed { + addRange(astbuf1, 0, 0); + } | + type_vec param_signed non_opt_range { + astbuf1->children.push_back($3); + }; param_implicit_type: param_signed param_range; param_type: @@ -1466,11 +1525,12 @@ enum_base_type: type_atom type_signing | %empty { astbuf1->is_reg = true; addRange(astbuf1); } ; -type_atom: TOK_INTEGER { astbuf1->is_reg = true; addRange(astbuf1); } // 4-state signed - | TOK_INT { astbuf1->is_reg = true; addRange(astbuf1); } // 2-state signed - | TOK_SHORTINT { astbuf1->is_reg = true; addRange(astbuf1, 15, 0); } // 2-state signed - | TOK_BYTE { astbuf1->is_reg = true; addRange(astbuf1, 7, 0); } // 2-state signed - ; +type_atom: + integer_atom_type { + astbuf1->is_reg = true; + astbuf1->is_signed = true; + addRange(astbuf1, $1 - 1, 0); + }; type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned @@ -1759,7 +1819,13 @@ wire_name: } rewriteAsMemoryNode(node, $2); } - if (current_function_or_task == NULL) { + if (current_function_or_task) { + if (node->is_input || node->is_output) + node->port_id = current_function_or_task_port_id++; + } else if (ast_stack.back()->type == AST_GENBLOCK) { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Cannot declare module port `%s' within a generate block.", $1->c_str()); + } else { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { port_stubs[*$1] = ++port_counter; } @@ -1774,9 +1840,6 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } - } else { - if (node->is_input || node->is_output) - node->port_id = current_function_or_task_port_id++; } //FIXME: for some reason, TOK_ID has a location which always points to one column *after* the real last column... SET_AST_NODE_LOC(node, @1, @1); @@ -1803,7 +1866,7 @@ type_name: TOK_ID // first time seen ; typedef_decl: - TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { + TOK_TYPEDEF non_io_wire_type range type_name range_or_multirange ';' { astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); if (astbuf2) @@ -2435,6 +2498,16 @@ behavioral_stmt: exitTypeScope(); if ($4 != NULL && $8 != NULL && *$4 != *$8) frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); + AstNode *node = ast_stack.back(); + // In SystemVerilog, unnamed blocks with block item declarations + // create an implicit hierarchy scope + if (sv_mode && node->str.empty()) + for (const AstNode* child : node->children) + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER + || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { + node->str = "$unnamed_block$" + std::to_string(autoidx++); + break; + } SET_AST_NODE_LOC(ast_stack.back(), @2, @8); delete $4; delete $8; @@ -2449,6 +2522,7 @@ behavioral_stmt: ast_stack.back()->children.push_back($7); } ';' simple_behavioral_stmt ')' { AstNode *block = new AstNode(AST_BLOCK); + block->str = "$for_loop$" + std::to_string(autoidx++); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { @@ -2515,20 +2589,21 @@ behavioral_stmt: ast_stack.pop_back(); }; -unique_case_attr: - %empty { - $$ = false; +case_attr: + attr { + $$ = $1; } | - TOK_PRIORITY case_attr { - $$ = $2; + attr TOK_UNIQUE0 { + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + $$ = $1; } | - TOK_UNIQUE case_attr { - $$ = true; - }; - -case_attr: - attr unique_case_attr { - if ($2) (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + attr TOK_PRIORITY { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + $$ = $1; + } | + attr TOK_UNIQUE { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); $$ = $1; }; @@ -2698,6 +2773,7 @@ single_arg: module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | + module_gen_body gen_block | %empty; gen_stmt_or_module_body_stmt: @@ -2723,12 +2799,7 @@ gen_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); - AstNode *block = new AstNode(AST_GENBLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); - } gen_stmt_block { - ast_stack.pop_back(); - } opt_gen_else { + } gen_stmt_block opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | @@ -2741,6 +2812,18 @@ gen_stmt: SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | + TOK_MSG_TASKS { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @1, @3); + ast_stack.pop_back(); + }; + +gen_block: TOK_BEGIN { enterTypeScope(); } opt_label { @@ -2750,22 +2833,15 @@ gen_stmt: ast_stack.push_back(node); } module_gen_body TOK_END opt_label { exitTypeScope(); + if ($3 != NULL && $7 != NULL && *$3 != *$7) + frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1); delete $3; delete $7; SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); - } | - TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); - node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @1, @3); - ast_stack.pop_back(); }; +// result is wrapped in a genblock only if necessary gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); @@ -2774,7 +2850,7 @@ gen_stmt_block: } gen_stmt_or_module_body_stmt { SET_AST_NODE_LOC(ast_stack.back(), @2, @2); ast_stack.pop_back(); - }; + } | gen_block; opt_gen_else: TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; diff --git a/kernel/log.cc b/kernel/log.cc index c7ae873bc..41e91119e 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -345,9 +345,6 @@ static void logv_error_with_prefix(const char *prefix, log_make_debug = bak_log_make_debug; - if (log_error_atexit) - log_error_atexit(); - for (auto &item : log_expect_error) if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern)) item.second.current_count++; @@ -355,6 +352,9 @@ static void logv_error_with_prefix(const char *prefix, if (check_expected_logs) log_check_expected(); + if (log_error_atexit) + log_error_atexit(); + YS_DEBUGTRAP_IF_DEBUGGING; #ifdef EMSCRIPTEN diff --git a/kernel/rtlil.h b/kernel/rtlil.h index cd966b815..6170ea55e 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -334,6 +334,10 @@ namespace RTLIL return compare(size()-len, len, suffix) == 0; } + bool contains(const char* str) const { + return strstr(c_str(), str); + } + size_t size() const { return strlen(c_str()); } @@ -731,6 +735,7 @@ struct RTLIL::SigChunk RTLIL::SigChunk extract(int offset, int length) const; inline int size() const { return width; } + inline bool is_wire() const { return wire != NULL; } bool operator <(const RTLIL::SigChunk &other) const; bool operator ==(const RTLIL::SigChunk &other) const; @@ -756,6 +761,8 @@ struct RTLIL::SigBit SigBit(const RTLIL::SigBit &sigbit) = default; RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default; + inline bool is_wire() const { return wire != NULL; } + bool operator <(const RTLIL::SigBit &other) const; bool operator ==(const RTLIL::SigBit &other) const; bool operator !=(const RTLIL::SigBit &other) const; diff --git a/manual/CHAPTER_TextRtlil.tex b/manual/CHAPTER_TextRtlil.tex index 243b56a87..5615a8707 100644 --- a/manual/CHAPTER_TextRtlil.tex +++ b/manual/CHAPTER_TextRtlil.tex @@ -241,7 +241,7 @@ See Sec.~\ref{sec:rtlil_process} for an overview of processes. <proc-stmt> ::= "process" <id> <eol> -<process-body> ::= <assign-stmt>$*$ <switch> <assign-stmt>$*$ <sync>$*$ +<process-body> ::= <assign-stmt>$*$ <switch>$?$ <assign-stmt>$*$ <sync>$*$ <assign-stmt> ::= "assign" <dest-sigspec> <src-sigspec> <eol> diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 8e7f3f990..7aa9a484f 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -37,7 +37,7 @@ struct SccWorker RTLIL::Design *design; RTLIL::Module *module; SigMap sigmap; - CellTypes ct; + CellTypes ct, specifyCells; std::set<RTLIL::Cell*> workQueue; std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> cellToNextCell; @@ -100,7 +100,7 @@ struct SccWorker } } - SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : + SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, bool specifyMode, int maxDepth) : design(design), module(module), sigmap(module) { if (module->processes.size() > 0) { @@ -115,6 +115,18 @@ struct SccWorker ct.setup_stdcells(); } + // Discover boxes with specify rules in them, for special handling. + if (specifyMode) { + for (auto mod : design->modules()) + if (mod->get_blackbox_attribute(false)) + for (auto cell : mod->cells()) + if (cell->type == ID($specify2)) + { + specifyCells.setup_module(mod); + break; + } + } + SigPool selectedSignals; SigSet<RTLIL::Cell*> sigToNextCells; @@ -129,29 +141,52 @@ struct SccWorker if (!design->selected(module, cell)) continue; - if (!allCellTypes && !ct.cell_known(cell->type)) + if (!allCellTypes && !ct.cell_known(cell->type) && !specifyCells.cell_known(cell->type)) continue; workQueue.insert(cell); RTLIL::SigSpec inputSignals, outputSignals; - for (auto &conn : cell->connections()) - { - bool isInput = true, isOutput = true; + if (specifyCells.cell_known(cell->type)) { + // Use specify rules of the type `(X => Y) = NN` to look for asynchronous paths in boxes. + for (auto subcell : design->module(cell->type)->cells()) + { + if (subcell->type != ID($specify2)) + continue; - if (ct.cell_known(cell->type)) { - isInput = ct.cell_input(cell->type, conn.first); - isOutput = ct.cell_output(cell->type, conn.first); + for (auto bit : subcell->getPort(ID::SRC)) + { + if (!bit.wire || !cell->hasPort(bit.wire->name)) + continue; + inputSignals.append(sigmap(cell->getPort(bit.wire->name))); + } + + for (auto bit : subcell->getPort(ID::DST)) + { + if (!bit.wire || !cell->hasPort(bit.wire->name)) + continue; + outputSignals.append(sigmap(cell->getPort(bit.wire->name))); + } } + } else { + for (auto &conn : cell->connections()) + { + bool isInput = true, isOutput = true; + + if (ct.cell_known(cell->type)) { + isInput = ct.cell_input(cell->type, conn.first); + isOutput = ct.cell_output(cell->type, conn.first); + } - RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second)); - sig.sort_and_unify(); + RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second)); + sig.sort_and_unify(); - if (isInput) - inputSignals.append(sig); - if (isOutput) - outputSignals.append(sig); + if (isInput) + inputSignals.append(sig); + if (isOutput) + outputSignals.append(sig); + } } inputSignals.sort_and_unify(); @@ -228,7 +263,7 @@ struct SccPass : public Pass { log("design.\n"); log("\n"); log(" -expect <num>\n"); - log(" expect to find exactly <num> SSCs. A different number of SSCs will\n"); + log(" expect to find exactly <num> SCCs. A different number of SCCs will\n"); log(" produce an error.\n"); log("\n"); log(" -max_depth <num>\n"); @@ -254,6 +289,9 @@ struct SccPass : public Pass { log(" replace the current selection with a selection of all cells and wires\n"); log(" that are part of a found logic loop\n"); log("\n"); + log(" -specify\n"); + log(" examine specify rules to detect logic loops in whitebox/blackbox cells\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { @@ -261,6 +299,7 @@ struct SccPass : public Pass { bool allCellTypes = false; bool selectMode = false; bool nofeedbackMode = false; + bool specifyMode = false; int maxDepth = -1; int expect = -1; @@ -293,6 +332,10 @@ struct SccPass : public Pass { selectMode = true; continue; } + if (args[argidx] == "-specify") { + specifyMode = true; + continue; + } break; } int origSelectPos = design->selection_stack.size() - 1; @@ -303,7 +346,7 @@ struct SccPass : public Pass { for (auto mod : design->selected_modules()) { - SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth); + SccWorker worker(design, mod, nofeedbackMode, allCellTypes, specifyMode, maxDepth); if (!setAttr.empty()) { diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 53296699c..62a478673 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -244,8 +244,8 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector< } if (shared_op->type.in(ID($alu))) { - shared_op->setPort(ID::X, module->addWire(NEW_ID, GetSize(new_sig_out))); - shared_op->setPort(ID::CO, module->addWire(NEW_ID, GetSize(new_sig_out))); + shared_op->setPort(ID::X, module->addWire(NEW_ID, GetSize(new_out))); + shared_op->setPort(ID::CO, module->addWire(NEW_ID, GetSize(new_out))); } bool is_fine = shared_op->type.in(FINE_BITWISE_OPS); diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index e9a10465e..f31b78804 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -88,7 +88,7 @@ struct AssertpmuxWorker { SigSpec output; - for (auto muxuser : sigbit_muxusers.at(bit)) + for (auto muxuser : sigbit_muxusers[bit]) { Cell *cell = std::get<0>(muxuser); int portidx = std::get<1>(muxuser); diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 192e39372..d5286f4e9 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -38,7 +38,7 @@ #define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}" #define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" #define ABC_FAST_COMMAND_LUT "strash; dretime; if" -#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_SOP "strash; dretime; cover {I} {P}" #define ABC_FAST_COMMAND_DFL "strash; dretime; map" #include "kernel/register.h" @@ -54,6 +54,7 @@ #include <cerrno> #include <sstream> #include <climits> +#include <vector> #ifndef _WIN32 # include <unistd.h> @@ -654,7 +655,7 @@ struct abc_output_filter }; void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, + std::vector<std::string> &liberty_files, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress) { @@ -709,8 +710,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); - if (!liberty_file.empty()) { - abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + if (!liberty_files.empty()) { + for (std::string liberty_file : liberty_files) abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); if (!constr_file.empty()) abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); } else @@ -738,7 +739,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; if (all_luts_cost_same && !fast_mode) abc_script += "; lutpack {S}"; - } else if (!liberty_file.empty()) + } else if (!liberty_files.empty()) abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); else if (sop_mode) abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; @@ -1019,7 +1020,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (ifs.fail()) log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - bool builtin_lib = liberty_file.empty(); + bool builtin_lib = liberty_files.empty(); RTLIL::Design *mapped_design = new RTLIL::Design; parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, sop_mode); @@ -1471,7 +1472,8 @@ struct AbcPass : public Pass { po_map.clear(); std::string exe_file = yosys_abc_executable; - std::string script_file, liberty_file, constr_file, clk_str; + std::string script_file, default_liberty_file, constr_file, clk_str; + std::vector<std::string> liberty_files; std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false, sop_mode = false; @@ -1489,7 +1491,7 @@ struct AbcPass : public Pass { std::string lut_arg, luts_arg, g_arg; exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */); script_file = design->scratchpad_get_string("abc.script", script_file); - liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file); + default_liberty_file = design->scratchpad_get_string("abc.liberty", default_liberty_file); constr_file = design->scratchpad_get_string("abc.constr", constr_file); if (design->scratchpad.count("abc.D")) { delay_target = "-D " + design->scratchpad_get_string("abc.D"); @@ -1551,7 +1553,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-liberty" && argidx+1 < args.size()) { - liberty_file = args[++argidx]; + liberty_files.push_back(args[++argidx]); continue; } if (arg == "-constr" && argidx+1 < args.size()) { @@ -1643,12 +1645,16 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); + if (liberty_files.empty() && !default_liberty_file.empty()) liberty_files.push_back(default_liberty_file); + rewrite_filename(script_file); if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') script_file = std::string(pwd) + "/" + script_file; - rewrite_filename(liberty_file); - if (!liberty_file.empty() && !is_absolute_path(liberty_file)) - liberty_file = std::string(pwd) + "/" + liberty_file; + for (int i = 0; i < GetSize(liberty_files); i++) { + rewrite_filename(liberty_files[i]); + if (!liberty_files[i].empty() && !is_absolute_path(liberty_files[i])) + liberty_files[i] = std::string(pwd) + "/" + liberty_files[i]; + } rewrite_filename(constr_file); if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; @@ -1794,6 +1800,7 @@ struct AbcPass : public Pass { gate_list.push_back("OAI4"); gate_list.push_back("MUX"); gate_list.push_back("NMUX"); + goto ok_alias; } if (g_arg_from_cmd) cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str())); @@ -1811,9 +1818,9 @@ struct AbcPass : public Pass { } } - if (!lut_costs.empty() && !liberty_file.empty()) + if (!lut_costs.empty() && !liberty_files.empty()) log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n"); - if (!constr_file.empty() && liberty_file.empty()) + if (!constr_file.empty() && liberty_files.empty()) log_cmd_error("Got -constr but no -liberty!\n"); if (enabled_gates.empty()) { @@ -1844,7 +1851,7 @@ struct AbcPass : public Pass { initvals.set(&assign_map, mod); if (!dff_mode || !clk_str.empty()) { - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + abc_module(design, mod, script_file, exe_file, liberty_files, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress); continue; } @@ -1989,7 +1996,7 @@ struct AbcPass : public Pass { clk_sig = assign_map(std::get<1>(it.first)); en_polarity = std::get<2>(it.first); en_sig = assign_map(std::get<3>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + abc_module(design, mod, script_file, exe_file, liberty_files, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress); assign_map.set(mod); } diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 7d017ac40..56bb15495 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -339,7 +339,7 @@ struct Abc9Pass : public ScriptPass if (check_label("pre")) { run("read_verilog -icells -lib -specify +/abc9_model.v"); - run("scc -set_attr abc9_scc_id {}"); + run("scc -specify -set_attr abc9_scc_id {}"); if (help_mode) run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); else diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index ec5f83fb0..f35b7ff60 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -211,7 +211,7 @@ struct FlattenWorker log_assert(new_conn.first.size() == new_conn.second.size()); if (sigmap(new_conn.first).has_const()) - log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", + log_error("Cell port %s.%s.%s is driving constant bits: %s <= %s\n", log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second)); module->connect(new_conn); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index d43737c8d..96843d710 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -118,19 +118,14 @@ struct TechmapWorker return result; for (auto w : module->wires()) { - const char *p = w->name.c_str(); - if (*p == '$') + if (*w->name.c_str() == '$') continue; - const char *q = strrchr(p+1, '.'); - if (q) - p = q; - - if (!strncmp(p, "\\_TECHMAP_", 10)) { + if (w->name.contains("_TECHMAP_") && !w->name.contains("_TECHMAP_REPLACE_")) { TechmapWireData record; record.wire = w; record.value = w; - result[p].push_back(record); + result[w->name].push_back(record); w->set_bool_attribute(ID::keep); w->set_bool_attribute(ID::_techmap_special_); } @@ -165,7 +160,7 @@ struct TechmapWorker orig_cell_name = cell->name.str(); for (auto tpl_cell : tpl->cells()) - if (tpl_cell->name == ID::_TECHMAP_REPLACE_) { + if (tpl_cell->name.ends_with("_TECHMAP_REPLACE_")) { module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); break; } @@ -226,8 +221,8 @@ struct TechmapWorker } design->select(module, w); - if (tpl_w->name.begins_with("\\_TECHMAP_REPLACE_.")) { - IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), tpl_w->name.c_str() + strlen("\\_TECHMAP_REPLACE_")); + if (const char *p = strstr(tpl_w->name.c_str(), "_TECHMAP_REPLACE_.")) { + IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_")); Wire *replace_w = module->addWire(replace_name, tpl_w); module->connect(replace_w, w); } @@ -327,12 +322,12 @@ struct TechmapWorker for (auto tpl_cell : tpl->cells()) { IdString c_name = tpl_cell->name; - bool techmap_replace_cell = (c_name == ID::_TECHMAP_REPLACE_); + bool techmap_replace_cell = c_name.ends_with("_TECHMAP_REPLACE_"); if (techmap_replace_cell) c_name = orig_cell_name; - else if (tpl_cell->name.begins_with("\\_TECHMAP_REPLACE_.")) - c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_")); + else if (const char *p = strstr(tpl_cell->name.c_str(), "_TECHMAP_REPLACE_.")) + c_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_")); else apply_prefix(cell->name, c_name); @@ -730,12 +725,16 @@ struct TechmapWorker for (auto &it : twd) techmap_wire_names.insert(it.first); - for (auto &it : twd[ID::_TECHMAP_FAIL_]) { - RTLIL::SigSpec value = it.value; - if (value.is_fully_const() && value.as_bool()) { - log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", - derived_name.c_str(), log_id(it.wire->name), log_signal(value)); - techmap_do_cache[tpl] = false; + for (auto &it : twd) { + if (!it.first.ends_with("_TECHMAP_FAIL_")) + continue; + for (const TechmapWireData &elem : it.second) { + RTLIL::SigSpec value = elem.value; + if (value.is_fully_const() && value.as_bool()) { + log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", + derived_name.c_str(), log_id(elem.wire->name), log_signal(value)); + techmap_do_cache[tpl] = false; + } } } @@ -744,7 +743,7 @@ struct TechmapWorker for (auto &it : twd) { - if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty()) + if (!it.first.contains("_TECHMAP_DO_") || it.second.empty()) continue; auto &data = it.second.front(); @@ -756,7 +755,7 @@ struct TechmapWorker const char *p = data.wire->name.c_str(); const char *q = strrchr(p+1, '.'); - q = q ? q : p+1; + q = q ? q+1 : p+1; std::string cmd_string = data.value.as_const().decode_string(); @@ -873,7 +872,7 @@ struct TechmapWorker TechmapWires twd = techmap_find_special_wires(tpl); for (auto &it : twd) { - if (it.first != ID::_TECHMAP_FAIL_ && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.begins_with("\\_TECHMAP_DO_") && !it.first.begins_with("\\_TECHMAP_DONE_")) + if (!it.first.ends_with("_TECHMAP_FAIL_") && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.contains("_TECHMAP_DO_") && !it.first.contains("_TECHMAP_DONE_")) log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first)); if (techmap_do_cache[tpl]) for (auto &it2 : it.second) diff --git a/techlibs/common/adff2dff.v b/techlibs/common/adff2dff.v index eca0110eb..2e4357b64 100644 --- a/techlibs/common/adff2dff.v +++ b/techlibs/common/adff2dff.v @@ -11,7 +11,7 @@ module adff2dff (CLK, ARST, D, Q); (* force_downto *) output reg [WIDTH-1:0] Q; (* force_downto *) - wire reg [WIDTH-1:0] NEXT_Q; + reg [WIDTH-1:0] NEXT_Q; wire [1023:0] _TECHMAP_DO_ = "proc;;"; diff --git a/techlibs/common/cmp2lcu.v b/techlibs/common/cmp2lcu.v index a221727e7..4e62039e9 100644 --- a/techlibs/common/cmp2lcu.v +++ b/techlibs/common/cmp2lcu.v @@ -41,10 +41,7 @@ generate wire [WIDTH-1:0] BB = {{(WIDTH-B_WIDTH){B_SIGNED ? B[B_WIDTH-1] : 1'b0}}, B}; // For $ge operation, start with the assumption that A and B are // equal (propagating this equality if A and B turn out to be so) - if (_TECHMAP_CELLTYPE_ == "$ge") - localparam CI = 1'b1; - else - localparam CI = 1'b0; + localparam CI = _TECHMAP_CELLTYPE_ == "$ge"; $__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y)); end @@ -81,12 +78,12 @@ generate assign Y = CO[LCU_WIDTH-1]; end else begin - if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) - localparam COST = 0; - else if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) - localparam COST = 1; - else - localparam COST = 2; + localparam COST = + _TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0] + ? 0 + : (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0] + ? 1 + : 2); if (BUDGET < COST) $__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI)) @@ -104,21 +101,21 @@ generate // from MSB down, deferring to less significant bits if the // MSBs are equal assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]); + (* force_downto *) + wire [LCU_WIDTH-1:0] P_, G_; if (LCU_WIDTH == 1) begin // Propagate only if all pairs are equal // (inconclusive evidence to say A >= B) - wire P_ = P[0] & PP; + assign P_ = P[0] & PP; // Generate if any comparisons call for it - wire G_ = G[0] | GG; + assign G_ = G[0] | GG; end else begin // Propagate only if all pairs are equal // (inconclusive evidence to say A >= B) - (* force_downto *) - wire [LCU_WIDTH-1:0] P_ = {P[LCU_WIDTH-1:1], P[0] & PP}; + assign P_ = {P[LCU_WIDTH-1:1], P[0] & PP}; // Generate if any comparisons call for it - (* force_downto *) - wire [LCU_WIDTH-1:0] G_ = {G[LCU_WIDTH-1:1], G[0] | GG}; + assign G_ = {G[LCU_WIDTH-1:1], G[0] | GG}; end if (AB_WIDTH == 1) $__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI)) diff --git a/techlibs/common/cmp2lut.v b/techlibs/common/cmp2lut.v index ec8f98e8d..c753bd2f1 100644 --- a/techlibs/common/cmp2lut.v +++ b/techlibs/common/cmp2lut.v @@ -66,14 +66,12 @@ function automatic [(1 << `LUT_WIDTH)-1:0] gen_lut; endfunction generate - if (_TECHMAP_CELLTYPE_ == "$lt") - localparam operation = 0; - if (_TECHMAP_CELLTYPE_ == "$le") - localparam operation = 1; - if (_TECHMAP_CELLTYPE_ == "$gt") - localparam operation = 2; - if (_TECHMAP_CELLTYPE_ == "$ge") - localparam operation = 3; + localparam operation = + _TECHMAP_CELLTYPE_ == "$lt" ? 0 : + _TECHMAP_CELLTYPE_ == "$le" ? 1 : + _TECHMAP_CELLTYPE_ == "$gt" ? 2 : + _TECHMAP_CELLTYPE_ == "$ge" ? 3 : + -1; if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) wire _TECHMAP_FAIL_ = 1; diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v index bec47d01f..f22f47b4a 100644 --- a/techlibs/common/mul2dsp.v +++ b/techlibs/common/mul2dsp.v @@ -121,7 +121,7 @@ module _80_mul (A, B, Y); localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);
localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
- if (A_SIGNED && B_SIGNED) begin
+ if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@@ -129,7 +129,7 @@ module _80_mul (A, B, Y); (* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end
- else begin
+ else begin : blk
(* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@@ -148,15 +148,15 @@ module _80_mul (A, B, Y); ) mul (
.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
.B(B),
- .Y(partial[i])
+ .Y(blk.partial[i])
);
// TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary
// reduction' approach also exists...
if (i == 0)
- assign partial_sum[i] = partial[i];
+ assign blk.partial_sum[i] = blk.partial[i];
else
- assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
+ assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];
end
\$__mul #(
@@ -168,17 +168,17 @@ module _80_mul (A, B, Y); ) sliceA.last (
.A(A[A_WIDTH-1 -: last_A_WIDTH]),
.B(B),
- .Y(last_partial)
+ .Y(blk.last_partial)
);
- assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
- assign Y = partial_sum[n];
+ assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
+ assign Y = blk.partial_sum[n];
end
else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);
localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
- if (A_SIGNED && B_SIGNED) begin
+ if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@@ -186,7 +186,7 @@ module _80_mul (A, B, Y); (* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end
- else begin
+ else begin : blk
(* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@@ -205,15 +205,15 @@ module _80_mul (A, B, Y); ) mul (
.A(A),
.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),
- .Y(partial[i])
+ .Y(blk.partial[i])
);
// TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary
// reduction' approach also exists...
if (i == 0)
- assign partial_sum[i] = partial[i];
+ assign blk.partial_sum[i] = blk.partial[i];
else
- assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
+ assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];
end
\$__mul #(
@@ -225,20 +225,24 @@ module _80_mul (A, B, Y); ) mul_sliceB_last (
.A(A),
.B(B[B_WIDTH-1 -: last_B_WIDTH]),
- .Y(last_partial)
+ .Y(blk.last_partial)
);
- assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
- assign Y = partial_sum[n];
+ assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
+ assign Y = blk.partial_sum[n];
end
else begin
- if (A_SIGNED)
+ if (A_SIGNED) begin : blkA
wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
- else
+ end
+ else begin : blkA
wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
- if (B_SIGNED)
+ end
+ if (B_SIGNED) begin : blkB
wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
- else
+ end
+ else begin : blkB
wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
+ end
`DSP_NAME #(
.A_SIGNED(A_SIGNED),
@@ -247,8 +251,8 @@ module _80_mul (A, B, Y); .B_WIDTH(`DSP_B_MAXWIDTH),
.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
) _TECHMAP_REPLACE_ (
- .A(Aext),
- .B(Bext),
+ .A(blkA.Aext),
+ .B(blkB.Bext),
.Y(Y)
);
end
diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index e94884025..5c9efad27 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -237,7 +237,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $reduce_and (A, B, Y) +//- $reduce_and (A, Y) //- //- An AND reduction. This corresponds to the Verilog unary prefix '&' operator. //- @@ -264,7 +264,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $reduce_or (A, B, Y) +//- $reduce_or (A, Y) //- //- An OR reduction. This corresponds to the Verilog unary prefix '|' operator. //- @@ -291,7 +291,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $reduce_xor (A, B, Y) +//- $reduce_xor (A, Y) //- //- A XOR reduction. This corresponds to the Verilog unary prefix '^' operator. //- @@ -318,7 +318,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $reduce_xnor (A, B, Y) +//- $reduce_xnor (A, Y) //- //- A XNOR reduction. This corresponds to the Verilog unary prefix '~^' operator. //- @@ -345,7 +345,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $reduce_bool (A, B, Y) +//- $reduce_bool (A, Y) //- //- An OR reduction. This cell type is used instead of $reduce_or when a signal is //- implicitly converted to a boolean signal, e.g. for operands of '&&' and '||'. diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v index ad3bccd21..db9f5d8ce 100644 --- a/techlibs/ice40/brams_map.v +++ b/techlibs/ice40/brams_map.v @@ -254,6 +254,41 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B wire [15:0] A1DATA_16, B1DATA_16; +`define INSTANCE \ + \$__ICE40_RAM4K #( \ + .READ_MODE(MODE), \ + .WRITE_MODE(MODE), \ + .NEGCLK_R(!CLKPOL2), \ + .NEGCLK_W(!CLKPOL3), \ + .INIT_0(INIT_0), \ + .INIT_1(INIT_1), \ + .INIT_2(INIT_2), \ + .INIT_3(INIT_3), \ + .INIT_4(INIT_4), \ + .INIT_5(INIT_5), \ + .INIT_6(INIT_6), \ + .INIT_7(INIT_7), \ + .INIT_8(INIT_8), \ + .INIT_9(INIT_9), \ + .INIT_A(INIT_A), \ + .INIT_B(INIT_B), \ + .INIT_C(INIT_C), \ + .INIT_D(INIT_D), \ + .INIT_E(INIT_E), \ + .INIT_F(INIT_F) \ + ) _TECHMAP_REPLACE_ ( \ + .RDATA(A1DATA_16), \ + .RADDR(A1ADDR_11), \ + .RCLK(CLK2), \ + .RCLKE(A1EN), \ + .RE(1'b1), \ + .WDATA(B1DATA_16), \ + .WADDR(B1ADDR_11), \ + .WCLK(CLK3), \ + .WCLKE(|B1EN), \ + .WE(1'b1) \ + ); + generate if (MODE == 1) begin assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8], @@ -261,51 +296,23 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8], B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA; `include "brams_init1.vh" + `INSTANCE end if (MODE == 2) begin assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]}; assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA; `include "brams_init2.vh" + `INSTANCE end if (MODE == 3) begin assign A1DATA = {A1DATA_16[11], A1DATA_16[3]}; assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA; `include "brams_init3.vh" + `INSTANCE end endgenerate - \$__ICE40_RAM4K #( - .READ_MODE(MODE), - .WRITE_MODE(MODE), - .NEGCLK_R(!CLKPOL2), - .NEGCLK_W(!CLKPOL3), - .INIT_0(INIT_0), - .INIT_1(INIT_1), - .INIT_2(INIT_2), - .INIT_3(INIT_3), - .INIT_4(INIT_4), - .INIT_5(INIT_5), - .INIT_6(INIT_6), - .INIT_7(INIT_7), - .INIT_8(INIT_8), - .INIT_9(INIT_9), - .INIT_A(INIT_A), - .INIT_B(INIT_B), - .INIT_C(INIT_C), - .INIT_D(INIT_D), - .INIT_E(INIT_E), - .INIT_F(INIT_F) - ) _TECHMAP_REPLACE_ ( - .RDATA(A1DATA_16), - .RADDR(A1ADDR_11), - .RCLK(CLK2), - .RCLKE(A1EN), - .RE(1'b1), - .WDATA(B1DATA_16), - .WADDR(B1ADDR_11), - .WCLK(CLK3), - .WCLKE(|B1EN), - .WE(1'b1) - ); +`undef INSTANCE + endmodule diff --git a/techlibs/machxo2/Makefile.inc b/techlibs/machxo2/Makefile.inc new file mode 100644 index 000000000..6f6f6ce94 --- /dev/null +++ b/techlibs/machxo2/Makefile.inc @@ -0,0 +1,5 @@ + +OBJS += techlibs/machxo2/synth_machxo2.o + +$(eval $(call add_share_file,share/machxo2,techlibs/machxo2/cells_map.v)) +$(eval $(call add_share_file,share/machxo2,techlibs/machxo2/cells_sim.v)) diff --git a/techlibs/machxo2/cells_map.v b/techlibs/machxo2/cells_map.v new file mode 100644 index 000000000..82eb10d95 --- /dev/null +++ b/techlibs/machxo2/cells_map.v @@ -0,0 +1,34 @@ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + + localparam rep = 1<<(4-WIDTH); + wire [3:0] I; + + generate + if(WIDTH == 1) begin + assign I = {1'b0, 1'b0, 1'b0, A[0]}; + end else if(WIDTH == 2) begin + assign I = {1'b0, 1'b0, A[1], A[0]}; + end else if(WIDTH == 3) begin + assign I = {1'b0, A[2], A[1], A[0]}; + end else if(WIDTH == 4) begin + assign I = {A[3], A[2], A[1], A[0]}; + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate + + LUT4 #(.INIT({rep{LUT}})) _TECHMAP_REPLACE_ (.A(I[0]), .B(I[1]), .C(I[2]), .D(I[3]), .Z(Y)); +endmodule + +// DFFs +module \$_DFF_P_ (input D, C, output Q); FACADE_FF #(.CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +// IO- "$__" cells for the iopadmap pass. +module \$__FACADE_OUTPAD (input I, output O); FACADE_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.PAD(O), .I(I), .T(1'b0)); endmodule +module \$__FACADE_INPAD (input I, output O); FACADE_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.PAD(I), .O(O)); endmodule +module \$__FACADE_TOUTPAD (input I, OE, output O); FACADE_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.PAD(O), .I(I), .T(~OE)); endmodule +module \$__FACADE_TINOUTPAD (input I, OE, output O, inout B); FACADE_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.PAD(B), .I(I), .O(O), .T(~OE)); endmodule diff --git a/techlibs/machxo2/cells_sim.v b/techlibs/machxo2/cells_sim.v new file mode 100644 index 000000000..161ddfe2e --- /dev/null +++ b/techlibs/machxo2/cells_sim.v @@ -0,0 +1,212 @@ +module LUT4 #( + parameter [15:0] INIT = 0 +) ( + input A, B, C, D, + output Z +); + // This form of LUT propagates as few x's as possible. + wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0]; + wire [3:0] s2 = C ? s3[ 7:4] : s3[3:0]; + wire [1:0] s1 = B ? s2[ 3:2] : s2[1:0]; + assign Z = A ? s1[1] : s1[0]; +endmodule + +module FACADE_FF #( + parameter GSR = "ENABLED", + parameter CEMUX = "1", + parameter CLKMUX = "0", + parameter LSRMUX = "LSR", + parameter LSRONMUX = "LSRMUX", + parameter SRMODE = "LSR_OVER_CE", + parameter REGSET = "SET", + parameter REGMODE = "FF" +) ( + input CLK, DI, LSR, CE, + output reg Q +); + + wire muxce; + generate + case (CEMUX) + "1": assign muxce = 1'b1; + "0": assign muxce = 1'b0; + "INV": assign muxce = ~CE; + default: assign muxce = CE; + endcase + endgenerate + + wire muxlsr = (LSRMUX == "INV") ? ~LSR : LSR; + wire muxlsron = (LSRONMUX == "LSRMUX") ? muxlsr : 1'b0; + wire muxclk = (CLKMUX == "INV") ? ~CLK : CLK; + wire srval = (REGSET == "SET") ? 1'b1 : 1'b0; + + initial Q = srval; + + generate + if (REGMODE == "FF") begin + if (SRMODE == "ASYNC") begin + always @(posedge muxclk, posedge muxlsron) + if (muxlsron) + Q <= srval; + else if (muxce) + Q <= DI; + end else begin + always @(posedge muxclk) + if (muxlsron) + Q <= srval; + else if (muxce) + Q <= DI; + end + end else if (REGMODE == "LATCH") begin + ERROR_UNSUPPORTED_FF_MODE error(); + end else begin + ERROR_UNKNOWN_FF_MODE error(); + end + endgenerate +endmodule + +/* For consistency with ECP5; represents F0/F1 => OFX0 mux in a slice. */ +module PFUMX (input ALUT, BLUT, C0, output Z); + assign Z = C0 ? ALUT : BLUT; +endmodule + +/* For consistency with ECP5; represents FXA/FXB => OFX1 mux in a slice. */ +module L6MUX21 (input D0, D1, SD, output Z); + assign Z = SD ? D1 : D0; +endmodule + +/* For consistency, input order matches TRELLIS_SLICE even though the BELs in +prjtrellis were filled in clockwise order from bottom left. */ +module FACADE_SLICE #( + parameter MODE = "LOGIC", + parameter GSR = "ENABLED", + parameter SRMODE = "LSR_OVER_CE", + parameter CEMUX = "1", + parameter CLKMUX = "0", + parameter LSRMUX = "LSR", + parameter LSRONMUX = "LSRMUX", + parameter LUT0_INITVAL = 16'hFFFF, + parameter LUT1_INITVAL = 16'hFFFF, + parameter REGMODE = "FF", + parameter REG0_SD = "1", + parameter REG1_SD = "1", + parameter REG0_REGSET = "SET", + parameter REG1_REGSET = "SET", + parameter CCU2_INJECT1_0 = "YES", + parameter CCU2_INJECT1_1 = "YES", + parameter WREMUX = "INV" +) ( + input A0, B0, C0, D0, + input A1, B1, C1, D1, + input M0, M1, + input FCI, FXA, FXB, + + input CLK, LSR, CE, + input DI0, DI1, + + input WD0, WD1, + input WAD0, WAD1, WAD2, WAD3, + input WRE, WCK, + + output F0, Q0, + output F1, Q1, + output FCO, OFX0, OFX1, + + output WDO0, WDO1, WDO2, WDO3, + output WADO0, WADO1, WADO2, WADO3 +); + + generate + if (MODE == "LOGIC") begin + L6MUX21 FXMUX (.D0(FXA), .D1(FXB), .SD(M1), .Z(OFX1)); + + wire k0; + wire k1; + PFUMX K0K1MUX (.ALUT(k1), .BLUT(k0), .C0(M0), .Z(OFX0)); + + LUT4 #(.INIT(LUT0_INITVAL)) LUT_0 (.A(A0), .B(B0), .C(C0), .D(D0), .Z(k0)); + LUT4 #(.INIT(LUT1_INITVAL)) LUT_1 (.A(A0), .B(B0), .C(C0), .D(D0), .Z(k1)); + + assign F0 = k0; + assign F1 = k1; + end else if (MODE == "CCU2") begin + ERROR_UNSUPPORTED_SLICE_MODE error(); + end else if (MODE == "DPRAM") begin + ERROR_UNSUPPORTED_SLICE_MODE error(); + end else begin + ERROR_UNKNOWN_SLICE_MODE error(); + end + endgenerate + + /* Reg can be fed either by M, or DI inputs; DI inputs muxes OFX and F + outputs (in other words, feeds back into FACADE_SLICE). */ + wire di0 = (REG0_SD == "1") ? DI0 : M0; + wire di1 = (REG1_SD == "1") ? DI1 : M1; + + FACADE_FF#(.GSR(GSR), .CEMUX(CEMUX), .CLKMUX(CLKMUX), .LSRMUX(LSRMUX), + .LSRONMUX(LSRONMUX), .SRMODE(SRMODE), .REGSET(REG0_REGSET), + .REGMODE(REGMODE)) REG_0 (.CLK(CLK), .DI(di0), .LSR(LSR), .CE(CE), .Q(Q0)); + FACADE_FF#(.GSR(GSR), .CEMUX(CEMUX), .CLKMUX(CLKMUX), .LSRMUX(LSRMUX), + .LSRONMUX(LSRONMUX), .SRMODE(SRMODE), .REGSET(REG1_REGSET), + .REGMODE(REGMODE)) REG_1 (.CLK(CLK), .DI(di1), .LSR(LSR), .CE(CE), .Q(Q1)); +endmodule + +module FACADE_IO #( + parameter DIR = "INPUT" +) ( + inout PAD, + input I, T, + output O +); + generate + if (DIR == "INPUT") begin + assign O = PAD; + end else if (DIR == "OUTPUT") begin + assign PAD = T ? 1'bz : I; + end else if (DIR == "BIDIR") begin + assign PAD = T ? 1'bz : I; + assign O = PAD; + end else begin + ERROR_UNKNOWN_IO_MODE error(); + end + endgenerate +endmodule + +(* blackbox *) +module OSCH #( + parameter NOM_FREQ = "2.08" +) ( + input STDBY, + output OSC, + output SEDSTDBY +); +endmodule + +(* blackbox *) +module DCCA ( + input CLKI, + input CE, + output CLKO +); +endmodule + +(* blackbox *) +module DCMA ( + input CLK0, + input CLK1, + input SEL, + output DCMOUT +); +endmodule + +// IO- "$__" cells for the iopadmap pass. These are temporary cells not meant +// to be instantiated by the end user. They are required in this file for +// attrmvcp to work. +(* blackbox *) +module \$__FACADE_OUTPAD (input I, output O); endmodule +(* blackbox *) +module \$__FACADE_INPAD (input I, output O); endmodule +(* blackbox *) +module \$__FACADE_TOUTPAD (input I, OE, output O); endmodule +(* blackbox *) +module \$__FACADE_TINOUTPAD (input I, OE, output O, inout B); endmodule diff --git a/techlibs/machxo2/synth_machxo2.cc b/techlibs/machxo2/synth_machxo2.cc new file mode 100644 index 000000000..bd56fbba9 --- /dev/null +++ b/techlibs/machxo2/synth_machxo2.cc @@ -0,0 +1,248 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 William D. Jones <wjones@wdj-consulting.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. + * + */ + +#include "kernel/register.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SynthMachXO2Pass : public ScriptPass +{ + SynthMachXO2Pass() : ScriptPass("synth_machxo2", "synthesis for MachXO2 FPGAs. This work is experimental.") { } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_machxo2 [options]\n"); + log("\n"); + log("This command runs synthesis for MachXO2 FPGAs.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -blif <file>\n"); + log(" write the design to the specified BLIF file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -edif <file>\n"); + log(" write the design to the specified EDIF file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -json <file>\n"); + log(" write the design to the specified JSON file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -noiopad\n"); + log(" do not insert IO buffers\n"); + log("\n"); + log(" -vpr\n"); + log(" generate an output netlist (and BLIF file) suitable for VPR\n"); + log(" (this feature is experimental and incomplete)\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + string top_opt, blif_file, edif_file, json_file; + bool flatten, vpr, noiopad; + + void clear_flags() override + { + top_opt = "-auto-top"; + blif_file = ""; + edif_file = ""; + json_file = ""; + flatten = true; + vpr = false; + noiopad = false; + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-blif" && argidx+1 < args.size()) { + blif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + continue; + } + if (args[argidx] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-noiopad") { + noiopad = true; + continue; + } + if (args[argidx] == "-vpr") { + vpr = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_MACHXO2 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() override + { + if (check_label("begin")) + { + run("read_verilog -lib -icells +/machxo2/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + } + + if (check_label("flatten", "(unless -noflatten)")) + { + if (flatten || help_mode) { + run("proc"); + run("flatten"); + run("tribuf -logic"); + run("deminout"); + } + } + + if (check_label("coarse")) + { + run("synth -run coarse"); + } + + if (check_label("fine")) + { + run("memory_map"); + run("opt -full"); + run("techmap -map +/techmap.v"); + run("opt -fast"); + } + + if (check_label("map_ios", "(unless -noiopad)")) + { + if (!noiopad || help_mode) + { + run("iopadmap -bits -outpad $__FACADE_OUTPAD I:O -inpad $__FACADE_INPAD O:I -toutpad $__FACADE_TOUTPAD OE:I:O -tinoutpad $__FACADE_TINOUTPAD OE:O:I:B A:top"); + run("attrmvcp -attr src -attr LOC t:$__FACADE_OUTPAD %x:+[O] t:$__FACADE_TOUTPAD %x:+[O] t:$__FACADE_TINOUTPAD %x:+[B]"); + run("attrmvcp -attr src -attr LOC -driven t:$__FACADE_INPAD %x:+[I]"); + } + } + + if (check_label("map_ffs")) + { + run("dfflegalize -cell $_DFF_P_ 0"); + } + + if (check_label("map_luts")) + { + run("abc -lut 4 -dress"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("techmap -map +/machxo2/cells_map.v"); + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + } + + if (check_label("blif")) + { + if (!blif_file.empty() || help_mode) { + if (vpr || help_mode) { + run(stringf("opt_clean -purge"), + " (vpr mode)"); + run(stringf("write_blif -attr -cname -conn -param %s", + help_mode ? "<file-name>" : blif_file.c_str()), + " (vpr mode)"); + } + if (!vpr) + run(stringf("write_blif -gates -attr -param %s", + help_mode ? "<file-name>" : blif_file.c_str()), + " (non-vpr mode)"); + } + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "<file-name>" : edif_file.c_str())); + } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str())); + } + } +} SynthMachXO2Pass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/nexus/cells_sim.v b/techlibs/nexus/cells_sim.v index b5938e08f..1e876a210 100644 --- a/techlibs/nexus/cells_sim.v +++ b/techlibs/nexus/cells_sim.v @@ -941,3 +941,118 @@ module MULTADDSUB36X36 #( .Z(Z) ); endmodule + +module MULTADDSUB9X9WIDE #( + parameter REGINPUTAB0 = "REGISTER", + parameter REGINPUTAB1 = "REGISTER", + parameter REGINPUTAB2 = "REGISTER", + parameter REGINPUTAB3 = "REGISTER", + parameter REGINPUTC = "REGISTER", + parameter REGADDSUB = "REGISTER", + parameter REGLOADC = "REGISTER", + parameter REGLOADC2 = "REGISTER", + parameter REGPIPELINE = "REGISTER", + parameter REGOUTPUT = "REGISTER", + parameter GSR = "ENABLED", + parameter RESETMODE = "SYNC" +) ( + input [8:0] A0, B0, A1, B1, A2, B2, A3, B3, + input [53:0] C, + input CLK, + input CEA0A1, CEA2A3, + input RSTA0A1, RSTA2A3, + input CEB0B1, CEB2B3, + input RSTB0B1, RSTB2B3, + input CEC, RSTC, + input CECTRL, RSTCTRL, + input SIGNED, + input RSTPIPE, CEPIPE, + input RSTOUT, CEOUT, + input LOADC, + input [3:0] ADDSUB, + output [53:0] Z +); + wire [17:0] m0, m1, m2, m3; + + localparam M_WIDTH = 18; + localparam Z_WIDTH = 54; + + MULT9X9 #( + .REGINPUTA(REGINPUTAB0), .REGINPUTB(REGINPUTAB0), .REGOUTPUT(REGPIPELINE), .GSR(GSR), .RESETMODE(RESETMODE) + ) m9_0 ( + .A(A0), .B(B0), .SIGNEDA(SIGNED), .SIGNEDB(SIGNED), + .CLK(CLK), + .CEA(CEA0A1), .RSTA(RSTA0A1), + .CEB(CEB0B1), .RSTB(RSTB0B1), + .CEOUT(CEPIPE), .RSTOUT(RSTPIPE), + .Z(m0) + ); + MULT9X9 #( + .REGINPUTA(REGINPUTAB1), .REGINPUTB(REGINPUTAB1), .REGOUTPUT(REGPIPELINE), .GSR(GSR), .RESETMODE(RESETMODE) + ) m9_1 ( + .A(A1), .B(B1), .SIGNEDA(SIGNED), .SIGNEDB(SIGNED), + .CLK(CLK), + .CEA(CEA0A1), .RSTA(RSTA0A1), + .CEB(CEB0B1), .RSTB(RSTB0B1), + .CEOUT(CEPIPE), .RSTOUT(RSTPIPE), + .Z(m1) + ); + MULT9X9 #( + .REGINPUTA(REGINPUTAB2), .REGINPUTB(REGINPUTAB2), .REGOUTPUT(REGPIPELINE), .GSR(GSR), .RESETMODE(RESETMODE) + ) m9_2 ( + .A(A2), .B(B2), .SIGNEDA(SIGNED), .SIGNEDB(SIGNED), + .CLK(CLK), + .CEA(CEA2A3), .RSTA(RSTA2A3), + .CEB(CEB2B3), .RSTB(RSTB2B3), + .CEOUT(CEPIPE), .RSTOUT(RSTPIPE), + .Z(m2) + ); + MULT9X9 #( + .REGINPUTA(REGINPUTAB3), .REGINPUTB(REGINPUTAB3), .REGOUTPUT(REGPIPELINE), .GSR(GSR), .RESETMODE(RESETMODE) + ) m9_3 ( + .A(A3), .B(B3), .SIGNEDA(SIGNED), .SIGNEDB(SIGNED), + .CLK(CLK), + .CEA(CEA2A3), .RSTA(RSTA2A3), + .CEB(CEB2B3), .RSTB(RSTB2B3), + .CEOUT(CEPIPE), .RSTOUT(RSTPIPE), + .Z(m3) + ); + + wire [53:0] c_r, c_r2; + wire [3:0] addsub_r, addsub_r2; + wire sgd_r, sgd_r2, csgd_r, csgd_r2; + wire loadc_r, loadc_r2; + + OXIDE_DSP_REG #(5, REGADDSUB, RESETMODE) addsub_reg(CLK, CECTRL, RSTCTRL, {SIGNED, ADDSUB}, {sgd_r, addsub_r}); + OXIDE_DSP_REG #(5, REGADDSUB, RESETMODE) addsub2_reg(CLK, CECTRL, RSTCTRL, {sgd_r, addsub_r}, {sgd_r2, addsub_r2}); + + OXIDE_DSP_REG #(1, REGLOADC, RESETMODE) loadc_reg(CLK, CECTRL, RSTCTRL, LOADC, loadc_r); + OXIDE_DSP_REG #(1, REGLOADC2, RESETMODE) loadc2_reg(CLK, CECTRL, RSTCTRL, loadc_r, loadc_r2); + + OXIDE_DSP_REG #(55, REGINPUTC, RESETMODE) c_reg(CLK, CEC, RSTC, {SIGNED, C}, {csgd_r, c_r}); + OXIDE_DSP_REG #(55, REGPIPELINE, RESETMODE) c2_reg(CLK, CEC, RSTC, {csgd_r, c_r}, {csgd_r2, c_r2}); + + + wire [18:0] m0_ext, m1_ext, m2_ext, m3_ext; + + assign m0_ext = {sgd_r2 ? m0[M_WIDTH-1] : 1'b0, m0}; + assign m1_ext = {sgd_r2 ? m1[M_WIDTH-1] : 1'b0, m1}; + assign m2_ext = {sgd_r2 ? m2[M_WIDTH-1] : 1'b0, m2}; + assign m3_ext = {sgd_r2 ? m3[M_WIDTH-1] : 1'b0, m3}; + + wire [18:0] s0 = addsub_r2[2] ? (m0_ext - m1_ext) : (m0_ext + m1_ext); + wire [18:0] s1 = addsub_r2[3] ? (m2_ext - m3_ext) : (m2_ext + m3_ext); + + wire [53:0] s0_ext = {{(54-19){sgd_r2 ? s0[18] : 1'b0}}, s0}; + wire [53:0] s1_ext = {{(54-19){sgd_r2 ? s1[18] : 1'b0}}, s1}; + + wire [53:0] c_op = loadc_r2 ? c_r2 : Z; + + // The diagram in the docs is wrong! It is not two cascaded 2-input add/subs as shown, + // but a three-input unit with negation controls on two inputs (i.e. addsub_r2[0] + // negates s1 not (s1 +/- s0)) + wire [53:0] z_d = c_op + (addsub_r2[0] ? -s1_ext : s1_ext) + (addsub_r2[1] ? -s0_ext : s0_ext); + + OXIDE_DSP_REG #(Z_WIDTH, REGOUTPUT, RESETMODE) z_reg(CLK, CEOUT, RSTOUT, z_d, Z); + +endmodule diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index eb8a04bde..63be7563e 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -151,6 +151,8 @@ generate if (`LUT_SIZE == 4) begin ); end endgenerate + assign X = S; + end else begin localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; @@ -193,8 +195,8 @@ end else begin end end endgenerate -end endgenerate - assign X = S; + +end endgenerate endmodule diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index adaf7aee1..a079f1c95 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -633,6 +633,41 @@ module FDRSE ( Q <= d; endmodule +module FDRSE_1 ( + output reg Q, + (* clkbuf_sink *) + (* invertible_pin = "IS_C_INVERTED" *) + input C, + (* invertible_pin = "IS_CE_INVERTED" *) + input CE, + (* invertible_pin = "IS_D_INVERTED" *) + input D, + (* invertible_pin = "IS_R_INVERTED" *) + input R, + (* invertible_pin = "IS_S_INVERTED" *) + input S +); + parameter [0:0] INIT = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_CE_INVERTED = 1'b0; + parameter [0:0] IS_D_INVERTED = 1'b0; + parameter [0:0] IS_R_INVERTED = 1'b0; + parameter [0:0] IS_S_INVERTED = 1'b0; + initial Q <= INIT; + wire c = C ^ IS_C_INVERTED; + wire ce = CE ^ IS_CE_INVERTED; + wire d = D ^ IS_D_INVERTED; + wire r = R ^ IS_R_INVERTED; + wire s = S ^ IS_S_INVERTED; + always @(negedge c) + if (r) + Q <= 0; + else if (s) + Q <= 1; + else if (ce) + Q <= d; +endmodule + (* abc9_box, lib_whitebox *) module FDCE ( output reg Q, @@ -837,6 +872,51 @@ module FDCPE ( assign Q = qs ? qp : qc; endmodule +module FDCPE_1 ( + output wire Q, + (* clkbuf_sink *) + (* invertible_pin = "IS_C_INVERTED" *) + input C, + input CE, + (* invertible_pin = "IS_CLR_INVERTED" *) + input CLR, + input D, + (* invertible_pin = "IS_PRE_INVERTED" *) + input PRE +); + parameter [0:0] INIT = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_CLR_INVERTED = 1'b0; + parameter [0:0] IS_PRE_INVERTED = 1'b0; + wire c = C ^ IS_C_INVERTED; + wire clr = CLR ^ IS_CLR_INVERTED; + wire pre = PRE ^ IS_PRE_INVERTED; + // Hacky model to avoid simulation-synthesis mismatches. + reg qc, qp, qs; + initial qc = INIT; + initial qp = INIT; + initial qs = 0; + always @(negedge c, posedge clr) begin + if (clr) + qc <= 0; + else if (CE) + qc <= D; + end + always @(negedge c, posedge pre) begin + if (pre) + qp <= 1; + else if (CE) + qp <= D; + end + always @* begin + if (clr) + qs <= 0; + else if (pre) + qs <= 1; + end + assign Q = qs ? qp : qc; +endmodule + module LDCE ( output reg Q, (* invertible_pin = "IS_CLR_INVERTED" *) diff --git a/techlibs/xilinx/xilinx_dffopt.cc b/techlibs/xilinx/xilinx_dffopt.cc index 365f505fb..598f1b216 100644 --- a/techlibs/xilinx/xilinx_dffopt.cc +++ b/techlibs/xilinx/xilinx_dffopt.cc @@ -209,7 +209,7 @@ lut_sigin_done: continue; LutData lut_d = it_D->second.first; Cell *cell_d = it_D->second.second; - if (cell->getParam(ID(IS_D_INVERTED)).as_bool()) { + if (cell->hasParam(ID(IS_D_INVERTED)) && cell->getParam(ID(IS_D_INVERTED)).as_bool()) { // Flip all bits in the LUT. for (int i = 0; i < GetSize(lut_d.first); i++) lut_d.first.bits[i] = (lut_d.first.bits[i] == State::S1) ? State::S0 : State::S1; @@ -249,7 +249,7 @@ lut_sigin_done: if (has_s) { SigBit sig_S = sigmap(cell->getPort(ID::S)); LutData lut_s = LutData(Const(2, 2), {sig_S}); - bool inv_s = cell->getParam(ID(IS_S_INVERTED)).as_bool(); + bool inv_s = cell->hasParam(ID(IS_S_INVERTED)) && cell->getParam(ID(IS_S_INVERTED)).as_bool(); auto it_S = bit_to_lut.find(sig_S); if (it_S != bit_to_lut.end()) lut_s = it_S->second.first; @@ -271,7 +271,7 @@ lut_sigin_done: if (has_r) { SigBit sig_R = sigmap(cell->getPort(ID::R)); LutData lut_r = LutData(Const(2, 2), {sig_R}); - bool inv_r = cell->getParam(ID(IS_R_INVERTED)).as_bool(); + bool inv_r = cell->hasParam(ID(IS_R_INVERTED)) && cell->getParam(ID(IS_R_INVERTED)).as_bool(); auto it_R = bit_to_lut.find(sig_R); if (it_R != bit_to_lut.end()) lut_r = it_R->second.first; diff --git a/tests/arch/machxo2/.gitignore b/tests/arch/machxo2/.gitignore new file mode 100644 index 000000000..1d329c933 --- /dev/null +++ b/tests/arch/machxo2/.gitignore @@ -0,0 +1,2 @@ +*.log +/run-test.mk diff --git a/tests/arch/machxo2/add_sub.ys b/tests/arch/machxo2/add_sub.ys new file mode 100644 index 000000000..d9497b818 --- /dev/null +++ b/tests/arch/machxo2/add_sub.ys @@ -0,0 +1,8 @@ +read_verilog ../common/add_sub.v +hierarchy -top top +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd top # Constrain all select calls below inside the top module +select -assert-count 10 t:LUT4 +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/dffs.ys b/tests/arch/machxo2/dffs.ys new file mode 100644 index 000000000..83a79a9d6 --- /dev/null +++ b/tests/arch/machxo2/dffs.ys @@ -0,0 +1,19 @@ +read_verilog ../common/dffs.v +design -save read + +hierarchy -top dff +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd dff # Constrain all select calls below inside the top module +select -assert-count 1 t:FACADE_FF +select -assert-none t:FACADE_FF t:FACADE_IO %% t:* %D + +design -load read +hierarchy -top dffe +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd dffe # Constrain all select calls below inside the top module +select -assert-count 2 t:FACADE_FF t:LUT4 +select -assert-none t:FACADE_FF t:LUT4 t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/fsm.ys b/tests/arch/machxo2/fsm.ys new file mode 100644 index 000000000..847a61161 --- /dev/null +++ b/tests/arch/machxo2/fsm.ys @@ -0,0 +1,15 @@ +read_verilog ../common/fsm.v +hierarchy -top fsm +proc +flatten + +equiv_opt -run :prove -map +/machxo2/cells_sim.v synth_machxo2 +miter -equiv -make_assert -flatten gold gate miter +sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter + +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd fsm # Constrain all select calls below inside the top module + +select -assert-max 16 t:LUT4 +select -assert-count 6 t:FACADE_FF +select -assert-none t:FACADE_FF t:LUT4 t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/logic.ys b/tests/arch/machxo2/logic.ys new file mode 100644 index 000000000..bf93ab128 --- /dev/null +++ b/tests/arch/machxo2/logic.ys @@ -0,0 +1,8 @@ +read_verilog ../common/logic.v +hierarchy -top top +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd top # Constrain all select calls below inside the top module +select -assert-count 9 t:LUT4 +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/mux.ys b/tests/arch/machxo2/mux.ys new file mode 100644 index 000000000..6c8aa857c --- /dev/null +++ b/tests/arch/machxo2/mux.ys @@ -0,0 +1,40 @@ +read_verilog ../common/mux.v +design -save read + +hierarchy -top mux2 +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux2 # Constrain all select calls below inside the top module +select -assert-count 1 t:LUT4 +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D + +design -load read +hierarchy -top mux4 +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux4 # Constrain all select calls below inside the top module +select -assert-count 2 t:LUT4 + +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D + +design -load read +hierarchy -top mux8 +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux8 # Constrain all select calls below inside the top module +select -assert-count 5 t:LUT4 + +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D + +design -load read +hierarchy -top mux16 +proc +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux16 # Constrain all select calls below inside the top module +select -assert-count 11 t:LUT4 + +select -assert-none t:LUT4 t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/run-test.sh b/tests/arch/machxo2/run-test.sh new file mode 100644 index 000000000..4be4b70ae --- /dev/null +++ b/tests/arch/machxo2/run-test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu +source ../../gen-tests-makefile.sh +run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'" diff --git a/tests/arch/machxo2/shifter.ys b/tests/arch/machxo2/shifter.ys new file mode 100644 index 000000000..87fdab0fa --- /dev/null +++ b/tests/arch/machxo2/shifter.ys @@ -0,0 +1,10 @@ +read_verilog ../common/shifter.v +hierarchy -top top +proc +flatten +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd top # Constrain all select calls below inside the top module + +select -assert-count 8 t:FACADE_FF +select -assert-none t:FACADE_FF t:FACADE_IO %% t:* %D diff --git a/tests/arch/machxo2/tribuf.ys b/tests/arch/machxo2/tribuf.ys new file mode 100644 index 000000000..9c00a8bcf --- /dev/null +++ b/tests/arch/machxo2/tribuf.ys @@ -0,0 +1,10 @@ +read_verilog ../common/tribuf.v +hierarchy -top tristate +proc +flatten +equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd tristate # Constrain all select calls below inside the top module +select -assert-count 3 t:FACADE_IO +select -assert-count 1 t:$not +select -assert-none t:FACADE_IO t:$not %% t:* %D diff --git a/tests/arch/xilinx/mux.ys b/tests/arch/xilinx/mux.ys index 1b2788448..c2a23de6d 100644 --- a/tests/arch/xilinx/mux.ys +++ b/tests/arch/xilinx/mux.ys @@ -40,10 +40,11 @@ proc equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux16 # Constrain all select calls below inside the top module +select -assert-max 2 t:LUT3 select -assert-max 2 t:LUT4 select -assert-min 4 t:LUT6 select -assert-max 7 t:LUT6 select -assert-max 2 t:MUXF7 dump -select -assert-none t:LUT6 t:LUT4 t:MUXF7 %% t:* %D +select -assert-none t:LUT6 t:LUT4 t:LUT3 t:MUXF7 %% t:* %D diff --git a/tests/arch/xilinx/xilinx_dffopt.ys b/tests/arch/xilinx/xilinx_dffopt.ys index 2c729832e..c09699411 100644 --- a/tests/arch/xilinx/xilinx_dffopt.ys +++ b/tests/arch/xilinx/xilinx_dffopt.ys @@ -223,3 +223,49 @@ select -assert-count 1 t:LUT2 select -assert-none t:FDRSE t:LUT4 t:LUT2 %% t:* %D design -reset + + +read_verilog << EOT + +// FDSE_1, mergeable CE and S, but CE only not worth it. + +module t0 (...); +input wire clk; +input wire [7:0] i; +output wire [7:0] o; + +wire [7:0] tmp ; + +LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); +LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); + +FDSE_1 ff (.D(tmp[0]), .CE(i[7]), .S(tmp[1]), .Q(o[0])); + +endmodule + +EOT + +read_verilog -lib +/xilinx/cells_sim.v +design -save t0 + +equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt +design -load postopt +clean + +cd t0 +select -assert-count 1 t:FDSE_1 +select -assert-count 1 t:LUT5 +select -assert-none t:FDSE_1 t:LUT5 %% t:* %D + +design -load t0 + +equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4 +design -load postopt +clean + +cd t0 +select -assert-count 1 t:FDSE_1 +select -assert-count 2 t:LUT2 +select -assert-none t:FDSE_1 t:LUT2 %% t:* %D + +design -reset diff --git a/tests/opt/opt_share_bug2538.ys b/tests/opt/opt_share_bug2538.ys new file mode 100644 index 000000000..7261c6695 --- /dev/null +++ b/tests/opt/opt_share_bug2538.ys @@ -0,0 +1,20 @@ +read_verilog <<EOT + +module top(...); + +input [3:0] A; +input S; +output [1:0] Y; + +wire [3:0] A1 = A + 1; +wire [3:0] A2 = A + 2; +assign Y = S ? A1[3:2] : A2[3:2]; + +endmodule + +EOT + +proc +alumacc +equiv_opt -assert opt_share + diff --git a/tests/sat/bug2595.ys b/tests/sat/bug2595.ys new file mode 100644 index 000000000..f668fd747 --- /dev/null +++ b/tests/sat/bug2595.ys @@ -0,0 +1,18 @@ +read_ilang <<EOT +module \top + wire input 3 \A + wire width 2 input 2 \B + wire width 2 input 1 \S + wire \Y + cell $pmux \my_pmux + parameter signed \S_WIDTH 2 + parameter signed \WIDTH 1 + connect \A \A + connect \B \B + connect \S \S + connect \Y \Y + end +end +EOT + +assertpmux diff --git a/tests/simple/const_branch_finish.v b/tests/simple/const_branch_finish.v index 8166688e6..f585be87a 100644 --- a/tests/simple/const_branch_finish.v +++ b/tests/simple/const_branch_finish.v @@ -21,9 +21,6 @@ module top; end end generate - begin : unconditional_block - initial `CONSTANT_CHECK - end if (WIDTH == 32) begin : conditional_block initial `CONSTANT_CHECK end diff --git a/tests/simple/const_fold_func.v b/tests/simple/const_fold_func.v new file mode 100644 index 000000000..ee2f12e06 --- /dev/null +++ b/tests/simple/const_fold_func.v @@ -0,0 +1,61 @@ +module top( + input wire [3:0] inp, + output wire [3:0] out1, out2, out3, out4, out5, + output reg [3:0] out6 +); + function automatic [3:0] flip; + input [3:0] inp; + flip = ~inp; + endfunction + + function automatic [3:0] help; + input [3:0] inp; + help = flip(inp); + endfunction + + // while loops are const-eval-only + function automatic [3:0] loop; + input [3:0] inp; + reg [3:0] val; + begin + val = inp; + loop = 1; + while (val != inp) begin + loop = loop * 2; + val = val + 1; + end + end + endfunction + + // not const-eval-only, despite calling a const-eval-only function + function automatic [3:0] help_mul; + input [3:0] inp; + help_mul = inp * loop(2); + endfunction + + // can be elaborated so long as exp is a constant + function automatic [3:0] pow_flip_a; + input [3:0] base, exp; + begin + pow_flip_a = 1; + if (exp > 0) + pow_flip_a = base * pow_flip_a(flip(base), exp - 1); + end + endfunction + + function automatic [3:0] pow_flip_b; + input [3:0] base, exp; + begin + out6[exp] = base & 1; + pow_flip_b = 1; + if (exp > 0) + pow_flip_b = base * pow_flip_b(flip(base), exp - 1); + end + endfunction + + assign out1 = flip(flip(inp)); + assign out2 = help(flip(inp)); + assign out3 = help_mul(inp); + assign out4 = pow_flip_a(flip(inp), 3); + assign out5 = pow_flip_b(2, 2); +endmodule diff --git a/tests/simple/const_func_shadow.v b/tests/simple/const_func_shadow.v new file mode 100644 index 000000000..ca63606d9 --- /dev/null +++ b/tests/simple/const_func_shadow.v @@ -0,0 +1,33 @@ +module top(w, x, y, z); + function [11:0] func; + input reg [2:0] x; + input reg [2:0] y; + begin + x = x * (y + 1); + begin : foo + reg [2:0] y; + y = x + 1; + begin : bar + reg [2:0] x; + x = y + 1; + begin : blah + reg [2:0] y; + y = x + 1; + func[2:0] = y; + end + func[5:3] = x; + end + func[8:6] = y; + end + func[11:9] = x; + end + endfunction + output wire [func(2, 3) - 1:0] w; + output wire [func(1, 3) - 1:0] x; + output wire [func(3, 1) - 1:0] y; + output wire [func(5, 2) - 1:0] z; + assign w = 1'sb1; + assign x = 1'sb1; + assign y = 1'sb1; + assign z = 1'sb1; +endmodule diff --git a/tests/simple/func_block.v b/tests/simple/func_block.v new file mode 100644 index 000000000..be759d1a9 --- /dev/null +++ b/tests/simple/func_block.v @@ -0,0 +1,33 @@ +`default_nettype none + +module top(inp, out1, out2, out3); + input wire [31:0] inp; + + function automatic [31:0] func1; + input [31:0] inp; + reg [31:0] idx; + for (idx = 0; idx < 32; idx = idx + 1) begin : blk + func1[idx] = (idx & 1'b1) ^ inp[idx]; + end + endfunction + + function automatic [31:0] func2; + input [31:0] inp; + reg [31:0] idx; + for (idx = 0; idx < 32; idx = idx + 1) begin : blk + func2[idx] = (idx & 1'b1) ^ inp[idx]; + end + endfunction + + function automatic [31:0] func3; + localparam A = 32 - 1; + parameter B = 1 - 0; + input [31:0] inp; + func3[A:B] = inp[A:B]; + endfunction + + output wire [31:0] out1, out2, out3; + assign out1 = func1(inp); + assign out2 = func2(inp); + assign out3 = func3(inp); +endmodule diff --git a/tests/simple/func_recurse.v b/tests/simple/func_recurse.v new file mode 100644 index 000000000..d61c8cc06 --- /dev/null +++ b/tests/simple/func_recurse.v @@ -0,0 +1,25 @@ +module top( + input wire [3:0] inp, + output wire [3:0] out1, out2 +); + function automatic [3:0] pow_a; + input [3:0] base, exp; + begin + pow_a = 1; + if (exp > 0) + pow_a = base * pow_a(base, exp - 1); + end + endfunction + + function automatic [3:0] pow_b; + input [3:0] base, exp; + begin + pow_b = 1; + if (exp > 0) + pow_b = base * pow_b(base, exp - 1); + end + endfunction + + assign out1 = pow_a(inp, 3); + assign out2 = pow_b(2, 2); +endmodule diff --git a/tests/simple/func_width_scope.v b/tests/simple/func_width_scope.v new file mode 100644 index 000000000..ce81e894e --- /dev/null +++ b/tests/simple/func_width_scope.v @@ -0,0 +1,41 @@ +module top(inp, out1, out2); + input wire signed inp; + + localparam WIDTH_A = 5; + function automatic [WIDTH_A-1:0] func1; + input reg [WIDTH_A-1:0] inp; + func1 = ~inp; + endfunction + wire [func1(0)-1:0] xc; + assign xc = 1'sb1; + wire [WIDTH_A-1:0] xn; + assign xn = func1(inp); + + generate + if (1) begin : blk + localparam WIDTH_A = 6; + function automatic [WIDTH_A-1:0] func2; + input reg [WIDTH_A-1:0] inp; + func2 = ~inp; + endfunction + wire [func2(0)-1:0] yc; + assign yc = 1'sb1; + wire [WIDTH_A-1:0] yn; + assign yn = func2(inp); + + localparam WIDTH_B = 7; + function automatic [WIDTH_B-1:0] func3; + input reg [WIDTH_B-1:0] inp; + func3 = ~inp; + endfunction + wire [func3(0)-1:0] zc; + assign zc = 1'sb1; + wire [WIDTH_B-1:0] zn; + assign zn = func3(inp); + end + endgenerate + + output wire [1023:0] out1, out2; + assign out1 = {xc, 1'b0, blk.yc, 1'b0, blk.zc}; + assign out2 = {xn, 1'b0, blk.yn, 1'b0, blk.zn}; +endmodule diff --git a/tests/simple/genblk_collide.v b/tests/simple/genblk_collide.v new file mode 100644 index 000000000..f42dd2cfc --- /dev/null +++ b/tests/simple/genblk_collide.v @@ -0,0 +1,27 @@ +`default_nettype none + +module top1; + generate + if (1) begin : foo + if (1) begin : bar + wire x; + end + assign bar.x = 1; + wire y; + end + endgenerate +endmodule + +module top2; + genvar i; + generate + if (1) begin : foo + wire x; + for (i = 0; i < 1; i = i + 1) begin : foo + if (1) begin : foo + assign x = 1; + end + end + end + endgenerate +endmodule diff --git a/tests/simple/genblk_dive.v b/tests/simple/genblk_dive.v new file mode 100644 index 000000000..98d0e1f4b --- /dev/null +++ b/tests/simple/genblk_dive.v @@ -0,0 +1,21 @@ +`default_nettype none +module top(output wire x); + generate + if (1) begin : Z + if (1) begin : A + wire x; + if (1) begin : B + wire x; + if (1) begin : C + wire x; + assign B.x = 0; + wire z = A.B.C.x; + end + assign A.x = A.B.C.x; + end + assign B.C.x = B.x; + end + end + endgenerate + assign x = Z.A.x; +endmodule diff --git a/tests/simple/genblk_order.v b/tests/simple/genblk_order.v new file mode 100644 index 000000000..7c3a7a756 --- /dev/null +++ b/tests/simple/genblk_order.v @@ -0,0 +1,18 @@ +`default_nettype none +module top( + output wire out1, + output wire out2 +); + generate + if (1) begin : outer + if (1) begin : foo + wire x = 0; + if (1) begin : foo + wire x = 1; + assign out1 = foo.x; + end + assign out2 = foo.x; + end + end + endgenerate +endmodule diff --git a/tests/simple/genblk_port_shadow.v b/tests/simple/genblk_port_shadow.v new file mode 100644 index 000000000..a04631a20 --- /dev/null +++ b/tests/simple/genblk_port_shadow.v @@ -0,0 +1,10 @@ +module top(x); + generate + if (1) begin : blk + wire x; + assign x = 0; + end + endgenerate + output wire x; + assign x = blk.x; +endmodule diff --git a/tests/simple/generate.v b/tests/simple/generate.v index 12327b36e..445c88ba8 100644 --- a/tests/simple/generate.v +++ b/tests/simple/generate.v @@ -167,7 +167,7 @@ module gen_test7; reg [2:0] out2; wire [2:0] out3; generate - begin : cond + if (1) begin : cond reg [2:0] sub_out1; reg [2:0] sub_out2; wire [2:0] sub_out3; @@ -215,9 +215,9 @@ module gen_test8; wire [1:0] x = 2'b11; generate - begin : A + if (1) begin : A wire [1:0] x; - begin : B + if (1) begin : B wire [1:0] x = 2'b00; `ASSERT(x == 0) `ASSERT(A.x == 2) @@ -228,7 +228,7 @@ module gen_test8; `ASSERT(gen_test8.A.C.x == 1) `ASSERT(gen_test8.A.B.x == 0) end - begin : C + if (1) begin : C wire [1:0] x = 2'b01; `ASSERT(x == 1) `ASSERT(A.x == 2) @@ -260,3 +260,66 @@ module gen_test8; `ASSERT(gen_test8.A.C.x == 1) `ASSERT(gen_test8.A.B.x == 0) endmodule + +// ------------------------------------------ + +module gen_test9; + +// `define VERIFY +`ifdef VERIFY + `define ASSERT(expr) assert property (expr); +`else + `define ASSERT(expr) +`endif + + wire [1:0] w = 2'b11; + generate + begin : A + wire [1:0] x; + begin : B + wire [1:0] y = 2'b00; + `ASSERT(w == 3) + `ASSERT(x == 2) + `ASSERT(y == 0) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + begin : C + wire [1:0] z = 2'b01; + `ASSERT(w == 3) + `ASSERT(x == 2) + `ASSERT(z == 1) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + assign x = B.y ^ 2'b11 ^ C.z; + `ASSERT(x == 2) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + endgenerate + + `ASSERT(w == 3) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) +endmodule diff --git a/tests/simple/local_loop_var.sv b/tests/simple/local_loop_var.sv new file mode 100644 index 000000000..46b4e5c22 --- /dev/null +++ b/tests/simple/local_loop_var.sv @@ -0,0 +1,11 @@ +module top(out); + output integer out; + initial begin + integer i; + for (i = 0; i < 5; i = i + 1) + if (i == 0) + out = 1; + else + out += 2 ** i; + end +endmodule diff --git a/tests/simple/loop_var_shadow.v b/tests/simple/loop_var_shadow.v new file mode 100644 index 000000000..0222a4493 --- /dev/null +++ b/tests/simple/loop_var_shadow.v @@ -0,0 +1,15 @@ +module top(out); + genvar i; + generate + for (i = 0; i < 2; i = i + 1) begin : loop + localparam j = i + 1; + if (1) begin : blk + localparam i = j + 1; + wire [i:0] x; + assign x = 1'sb1; + end + end + endgenerate + output wire [63:0] out; + assign out = {loop[0].blk.x, loop[1].blk.x}; +endmodule diff --git a/tests/simple/macro_arg_spaces.sv b/tests/simple/macro_arg_spaces.sv new file mode 100644 index 000000000..75c4cd136 --- /dev/null +++ b/tests/simple/macro_arg_spaces.sv @@ -0,0 +1,28 @@ +module top( + input wire [31:0] i, + output wire [31:0] x, y, z +); + +`define BAR(a) a +`define FOO(a = function automatic [31:0] f) a + +`BAR(function automatic [31:0] a); + input [31:0] i; + a = i * 2; +endfunction + +`FOO(); + input [31:0] i; + f = i * 3; +endfunction + +`FOO(function automatic [31:0] b); + input [31:0] i; + b = i * 5; +endfunction + +assign x = a(i); +assign y = f(i); +assign z = b(i); + +endmodule diff --git a/tests/simple/macro_arg_surrounding_spaces.v b/tests/simple/macro_arg_surrounding_spaces.v new file mode 100644 index 000000000..3dbb5ea01 --- /dev/null +++ b/tests/simple/macro_arg_surrounding_spaces.v @@ -0,0 +1,20 @@ +module top( + IDENT_V_, + IDENT_W_, + IDENT_X_, + IDENT_Y_, + IDENT_Z_, + IDENT_A_, + IDENT_B_, + IDENT_C_ +); + `define MACRO(dummy, x) IDENT_``x``_ + output wire IDENT_V_; + output wire `MACRO(_,W); + output wire `MACRO(_, X); + output wire `MACRO(_,Y ); + output wire `MACRO(_, Z ); + output wire `MACRO(_, A); + output wire `MACRO(_,B ); + output wire `MACRO(_, C ); +endmodule diff --git a/tests/simple/named_genblk.v b/tests/simple/named_genblk.v new file mode 100644 index 000000000..b8300fc4d --- /dev/null +++ b/tests/simple/named_genblk.v @@ -0,0 +1,27 @@ +`default_nettype none +module top; + generate + if (1) begin + wire t; + begin : foo + wire x; + end + wire u; + end + begin : bar + wire x; + wire y; + begin : baz + wire x; + wire z; + end + end + endgenerate + assign genblk1.t = 1; + assign genblk1.foo.x = 1; + assign genblk1.u = 1; + assign bar.x = 1; + assign bar.y = 1; + assign bar.baz.x = 1; + assign bar.baz.z = 1; +endmodule diff --git a/tests/simple/nested_genblk_resolve.v b/tests/simple/nested_genblk_resolve.v new file mode 100644 index 000000000..da5593f8a --- /dev/null +++ b/tests/simple/nested_genblk_resolve.v @@ -0,0 +1,14 @@ +`default_nettype none +module top; + generate + if (1) begin + wire x; + genvar i; + for (i = 0; i < 1; i = i + 1) begin + if (1) begin + assign x = 1; + end + end + end + endgenerate +endmodule diff --git a/tests/simple/unnamed_block_decl.sv b/tests/simple/unnamed_block_decl.sv new file mode 100644 index 000000000..e81b457a8 --- /dev/null +++ b/tests/simple/unnamed_block_decl.sv @@ -0,0 +1,17 @@ +module top(z); + output integer z; + initial begin + integer x; + x = 1; + begin + integer y; + y = x + 1; + begin + integer z; + z = y + 1; + y = z + 1; + end + z = y + 1; + end + end +endmodule diff --git a/tests/svtypes/typedef_struct_port.sv b/tests/svtypes/typedef_struct_port.sv new file mode 100644 index 000000000..ecc03bee8 --- /dev/null +++ b/tests/svtypes/typedef_struct_port.sv @@ -0,0 +1,111 @@ +package p; + +typedef struct packed { + byte a; + byte b; +} p_t; + +typedef logic [31:0] l_t; + +endpackage + +module foo1( + input p::p_t p, + output p::p_t o +); + assign o = p; +endmodule + +module foo2(p, o); + input p::p_t p; + output p::p_t o; + assign o = p; +endmodule + +module foo3(input p::l_t p, input p::l_t o); + assign o = p; +endmodule + +module foo4(input logic [15:0] p, input logic [15:0] o); + assign o = p; +endmodule + +module test_parser(a,b,c,d,e,f,g,h,i); +input [7:0] a; // no explicit net declaration - net is unsigned +input [7:0] b; +input signed [7:0] c; +input signed [7:0] d; // no explicit net declaration - net is signed +output [7:0] e; // no explicit net declaration - net is unsigned +output [7:0] f; +output signed [7:0] g; +output signed [7:0] h; // no explicit net declaration - net is signed +output unsigned [7:0] i; +wire signed [7:0] b; // port b inherits signed attribute from net decl. +wire [7:0] c; // net c inherits signed attribute from port +logic signed [7:0] f;// port f inherits signed attribute from logic decl. +logic [7:0] g; // logic g inherits signed attribute from port + + assign a = 8'b10001111; + assign b = 8'b10001111; + assign c = 8'b10001111; + assign d = 8'b10001111; + assign e = 8'b10001111; + assign f = 8'b10001111; + assign g = 8'b10001111; + assign h = 8'b10001111; + assign i = 8'b10001111; + always_comb begin + assert($unsigned(143) == a); + assert($signed(-113) == b); + assert($signed(-113) == c); + assert($signed(-113) == d); + assert($unsigned(143) == e); + assert($unsigned(143) == f); + assert($signed(-113) == g); + assert($signed(-113) == h); + assert($unsigned(143) == i); + end +endmodule + +module top; + p::p_t ps; + assign ps.a = 8'hAA; + assign ps.b = 8'h55; + foo1 foo(.p(ps)); + + p::p_t body; + assign body.a = 8'hBB; + assign body.b = 8'h66; + foo2 foo_b(.p(body)); + + typedef p::l_t local_alias; + + local_alias l_s; + assign l_s = 32'hAAAAAAAA; + foo3 foo_l(.p(l_s)); + + typedef logic [15:0] sl_t; + + sl_t sl_s; + assign sl_s = 16'hBBBB; + foo4 foo_sl(.p(sl_s)); + + typedef sl_t local_alias_st; + + local_alias_st lsl_s; + assign lsl_s = 16'hCCCC; + foo4 foo_lsl(.p(lsl_s)); + + const logic j = 1'b1; + + always_comb begin + assert(8'hAA == ps.a); + assert(8'h55 == ps.b); + assert(8'hBB == body.a); + assert(8'h66 == body.b); + assert(32'hAAAAAAAA == l_s); + assert(16'hBBBB == sl_s); + assert(16'hCCCC == lsl_s); + assert(1'b1 == j); + end +endmodule diff --git a/tests/svtypes/typedef_struct_port.ys b/tests/svtypes/typedef_struct_port.ys new file mode 100644 index 000000000..5b75c3105 --- /dev/null +++ b/tests/svtypes/typedef_struct_port.ys @@ -0,0 +1,6 @@ +read_verilog -sv typedef_struct_port.sv +hierarchy; proc; opt +select -module top +sat -verify -seq 1 -tempinduct -prove-asserts -show-all +select -module test_parser +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/techmap/adff2dff.ys b/tests/techmap/adff2dff.ys new file mode 100644 index 000000000..53f7d2f08 --- /dev/null +++ b/tests/techmap/adff2dff.ys @@ -0,0 +1,19 @@ +read_verilog -icells << EOT +module top(...); + +input [1:0] D; +input C, R; +output [1:0] Q; + +always @(posedge C, posedge R) + if (R) + Q <= 0; + else + Q <= D; + +endmodule +EOT + +proc + +equiv_opt -async2sync techmap -map +/adff2dff.v diff --git a/tests/techmap/dff2ff.ys b/tests/techmap/dff2ff.ys new file mode 100644 index 000000000..5adf14b07 --- /dev/null +++ b/tests/techmap/dff2ff.ys @@ -0,0 +1,16 @@ +read_verilog -icells << EOT +module top(...); + +input [1:0] D; +input C; +output [1:0] Q; + +always @(posedge C) + Q <= D; + +endmodule +EOT + +proc + +equiv_opt techmap -map +/dff2ff.v diff --git a/tests/techmap/pmux2mux.ys b/tests/techmap/pmux2mux.ys new file mode 100644 index 000000000..1714a6b87 --- /dev/null +++ b/tests/techmap/pmux2mux.ys @@ -0,0 +1,15 @@ +read_verilog -icells << EOT +module top(...); + +input [3:0] A; +input [3:0] B0; +input [3:0] B1; +input [1:0] S; +output [3:0] O; + +\$pmux #(.WIDTH(4), .S_WIDTH(2)) pm (.A(A), .B({B1, B0}), .S(S), .Y(O)); + +endmodule +EOT + +equiv_opt techmap -map +/pmux2mux.v diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 12d4e5048..2bb6c7179 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -4,3 +4,4 @@ /write_gzip.v.gz /run-test.mk /plugin.so +/plugin.so.dSYM diff --git a/tests/various/const_arg_loop.v b/tests/various/const_arg_loop.sv index 358fb439a..f28d06e68 100644 --- a/tests/various/const_arg_loop.v +++ b/tests/various/const_arg_loop.sv @@ -20,11 +20,11 @@ module top; endfunction function automatic [31:0] operation2; - input [4:0] var; + input [4:0] inp; input integer num; begin - var[0] = var[0] ^ 1; - operation2 = num * var; + inp[0] = inp[0] ^ 1; + operation2 = num * inp; end endfunction @@ -79,15 +79,14 @@ module top; wire [31:0] x5; assign x5 = operation5(64); -// `define VERIFY -`ifdef VERIFY - assert property (a == 2); - assert property (A == 3); - assert property (x1 == 16); - assert property (x1b == 16); - assert property (x2 == 4); - assert property (x3 == 16); - assert property (x4 == a << 1); - assert property (x5 == 64); -`endif + always_comb begin + assert(a == 2); + assert(A == 3); + assert(x1 == 16); + assert(x1b == 16); + assert(x2 == 4); + assert(x3 == 16); + assert(x4 == a << 1); + assert(x5 == 64); + end endmodule diff --git a/tests/various/const_arg_loop.ys b/tests/various/const_arg_loop.ys index b039bda10..392532213 100644 --- a/tests/various/const_arg_loop.ys +++ b/tests/various/const_arg_loop.ys @@ -1 +1,6 @@ -read_verilog const_arg_loop.v +read_verilog -sv const_arg_loop.sv +hierarchy +proc +opt -full +select -module top +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/various/const_func.v b/tests/various/const_func.sv index 541e63b19..af65f5c73 100644 --- a/tests/various/const_func.v +++ b/tests/various/const_func.sv @@ -62,26 +62,25 @@ module top(out); localparam signed Y = $floor(W / X); localparam signed Z = negate($floor(W / X)); -// `define VERIFY -`ifdef VERIFY - assert property (a1 == 0); - assert property (a2 == 0); - assert property (a3 == "BAR"); - assert property (a4 == 0); - assert property (b1 == "FOO"); - assert property (b2 == "FOO"); - assert property (b3 == 0); - assert property (b4 == "HI"); - assert property (c1 == 1); - assert property (c2 == 1); - assert property (c3 == 0); - assert property (c4 == 0); - assert property (d1 == 0); - assert property (d2 == 0); - assert property (d3 == 1); - assert property (d4 == 1); + always_comb begin + assert(a1 == 0); + assert(a2 == 0); + assert(a3 == "BAR"); + assert(a4 == 0); + assert(b1 == "FOO"); + assert(b2 == "FOO"); + assert(b3 == 0); + assert(b4 == "HI"); + assert(c1 == 1); + assert(c2 == 1); + assert(c3 == 0); + assert(c4 == 0); + assert(d1 == 0); + assert(d2 == 0); + assert(d3 == 1); + assert(d4 == 1); - assert property (Y == 3); - assert property (Z == ~3); -`endif + assert(Y == 3); + assert(Z == ~3); + end endmodule diff --git a/tests/various/const_func.ys b/tests/various/const_func.ys index 5e3c04105..2f60acfe6 100644 --- a/tests/various/const_func.ys +++ b/tests/various/const_func.ys @@ -1 +1,7 @@ -read_verilog const_func.v +read_verilog -sv const_func.sv +hierarchy +proc +flatten +opt -full +select -module top +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/various/countbits.sv b/tests/various/countbits.sv new file mode 100644 index 000000000..5762217bb --- /dev/null +++ b/tests/various/countbits.sv @@ -0,0 +1,69 @@ +module top; + + assert property ($countbits(15'b011xxxxzzzzzzzz, '0 ) == 1); + assert property ($countbits(15'b011xxxxzzzzzzzz, '1 ) == 2); + assert property ($countbits(15'b011xxxxzzzzzzzz, 'x ) == 4); + assert property ($countbits(15'b011xxxxzzzzzzzz, 'z ) == 8); + assert property ($countbits(15'b011xxxxzzzzzzzz, '0, '1 ) == 3); + assert property ($countbits(15'b011xxxxzzzzzzzz, '1, '1, '0 ) == 3); + assert property ($countbits(15'b011xxxxzzzzzzzz, '0, 'x ) == 5); + assert property ($countbits(15'b011xxxxzzzzzzzz, '0, 'z ) == 9); + assert property ($countbits(15'bz1x10xzxzzxzzzz, '0, 'z ) == 9); + assert property ($countbits(15'b011xxxxzzzzzzzz, 'x, 'z ) == 12); + assert property ($countbits(15'b011xxxxzzzzzzzz, '1, 'z ) == 10); + assert property ($countbits(15'b011xxxxzzzzzzzz, '1, 'x, 'z ) == 14); + assert property ($countbits(15'b011xxxxzzzzzzzz, '1, 'x, 'z, '0) == 15); + + assert property ($countbits(0, '0) == 32); // test integers + assert property ($countbits(0, '1) == 0); + assert property ($countbits(80'b0, '0) == 80); // test something bigger than integer + assert property ($countbits(80'bx0, 'x) == 79); + + always_comb begin + logic one; + logic [1:0] two; + logic [3:0] four; + + // Make sure that the width of the whole expression doesn't affect the width of the shift + // operations inside the function. + one = $countbits(3'b100, '1) & 1'b1; + two = $countbits(3'b111, '1) & 2'b11; + four = $countbits(3'b111, '1) & 4'b1111; + + assert (one == 1); + assert (two == 3); + assert (four == 3); + end + + assert property ($countones(8'h00) == 0); + assert property ($countones(8'hff) == 8); + assert property ($countones(8'ha5) == 4); + assert property ($countones(8'h13) == 3); + + logic test1 = 1'b1; + logic [4:0] test5 = 5'b10101; + + assert property ($countones(test1) == 1); + assert property ($countones(test5) == 3); + + assert property ($isunknown(8'h00) == 0); + assert property ($isunknown(8'hff) == 0); + assert property ($isunknown(8'hx0) == 1); + assert property ($isunknown(8'h1z) == 1); + assert property ($isunknown(8'hxz) == 1); + + assert property ($onehot(8'h00) == 0); + assert property ($onehot(8'hff) == 0); + assert property ($onehot(8'h01) == 1); + assert property ($onehot(8'h80) == 1); + assert property ($onehot(8'h81) == 0); + assert property ($onehot(8'h20) == 1); + + assert property ($onehot0(8'h00) == 1); + assert property ($onehot0(8'hff) == 0); + assert property ($onehot0(8'h01) == 1); + assert property ($onehot0(8'h80) == 1); + assert property ($onehot0(8'h81) == 0); + assert property ($onehot0(8'h20) == 1); + +endmodule diff --git a/tests/various/countbits.ys b/tests/various/countbits.ys new file mode 100644 index 000000000..a556f7c5d --- /dev/null +++ b/tests/various/countbits.ys @@ -0,0 +1,7 @@ +read_verilog -sv countbits.sv +hierarchy +proc +flatten +opt -full +select -module top +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/various/fib_tern.v b/tests/various/fib_tern.v new file mode 100644 index 000000000..fefde74ce --- /dev/null +++ b/tests/various/fib_tern.v @@ -0,0 +1,70 @@ +module gate( + off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9 +); + input wire signed [31:0] off; + + function automatic blah( + input x + ); + blah = x; + endfunction + + function automatic integer fib( + input integer k + ); + fib = k == 0 + ? 0 + : k == 1 + ? 1 + : fib(k - 1) + fib(k - 2); + endfunction + + function automatic integer fib_wrap( + input integer k, + output integer o + ); + o = off + fib(k); + endfunction + + output integer fib0; + output integer fib1; + output integer fib2; + output integer fib3; + output integer fib4; + output integer fib5; + output integer fib6; + output integer fib7; + output integer fib8; + output integer fib9; + + initial begin : blk + integer unused; + unused = fib_wrap(0, fib0); + unused = fib_wrap(1, fib1); + unused = fib_wrap(2, fib2); + unused = fib_wrap(3, fib3); + unused = fib_wrap(4, fib4); + unused = fib_wrap(5, fib5); + unused = fib_wrap(6, fib6); + unused = fib_wrap(7, fib7); + unused = fib_wrap(8, fib8); + unused = fib_wrap(9, fib9); + end +endmodule + +module gold( + off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9 +); + input wire signed [31:0] off; + + output integer fib0 = off + 0; + output integer fib1 = off + 1; + output integer fib2 = off + 1; + output integer fib3 = off + 2; + output integer fib4 = off + 3; + output integer fib5 = off + 5; + output integer fib6 = off + 8; + output integer fib7 = off + 13; + output integer fib8 = off + 21; + output integer fib9 = off + 34; +endmodule diff --git a/tests/various/fib_tern.ys b/tests/various/fib_tern.ys new file mode 100644 index 000000000..e5bf186e1 --- /dev/null +++ b/tests/various/fib_tern.ys @@ -0,0 +1,6 @@ +read_verilog fib_tern.v +hierarchy +proc +equiv_make gold gate equiv +equiv_simple +equiv_status -assert diff --git a/tests/various/gen_if_null.v b/tests/various/gen_if_null.v index a12ac6288..992bc68b3 100644 --- a/tests/various/gen_if_null.v +++ b/tests/various/gen_if_null.v @@ -1,13 +1,17 @@ -module test(x, y, z); +`default_nettype none +module test; localparam OFF = 0; generate if (OFF) ; - else input x; - if (!OFF) input y; + else wire x; + if (!OFF) wire y; else ; if (OFF) ; else ; if (OFF) ; - input z; + wire z; endgenerate + assign genblk1.x = 0; + assign genblk2.y = 0; + assign z = 0; endmodule diff --git a/tests/various/gen_if_null.ys b/tests/various/gen_if_null.ys index 31dfc444b..0733e3a94 100644 --- a/tests/various/gen_if_null.ys +++ b/tests/various/gen_if_null.ys @@ -1,4 +1,4 @@ read_verilog gen_if_null.v -select -assert-count 1 test/x -select -assert-count 1 test/y +select -assert-count 1 test/genblk1.x +select -assert-count 1 test/genblk2.y select -assert-count 1 test/z diff --git a/tests/various/port_sign_extend.v b/tests/various/port_sign_extend.v index 446268268..813ceb503 100644 --- a/tests/various/port_sign_extend.v +++ b/tests/various/port_sign_extend.v @@ -24,8 +24,8 @@ module PassThrough(a, b); assign b = a; endmodule -module act(o1, o2, o3, o4, o5, o6, yay1, nay1, yay2, nay2); - output wire [3:0] o1, o2, o3, o4, o5, o6; +module act(o1, o2, o3, o4, o5, o6, o7, o8, o9, yay1, nay1, yay2, nay2); + output wire [3:0] o1, o2, o3, o4, o5, o6, o7, o8, o9; // unsigned constant PassThrough pt1(1'b1, o1); @@ -52,6 +52,17 @@ module act(o1, o2, o3, o4, o5, o6, yay1, nay1, yay2, nay2); wire signed [2:0] tmp6b = 3'b001; PassThrough pt6(tmp6a ? tmp6a : tmp6b, o6); + wire signed [2:0] tmp7 = 3'b011; + PassThrough pt7(~tmp7, o7); + + reg signed [2:0] tmp8 [0:0]; + initial tmp8[0] = 3'b101; + PassThrough pt8(tmp8[0], o8); + + wire signed [2:0] tmp9a = 3'b100; + wire signed [1:0] tmp9b = 2'b11; + PassThrough pt9(0 ? tmp9a : tmp9b, o9); + output wire [2:0] yay1, nay1; GeneratorSigned1 os1(yay1); GeneratorUnsigned1 ou1(nay1); @@ -61,8 +72,8 @@ module act(o1, o2, o3, o4, o5, o6, yay1, nay1, yay2, nay2); GeneratorUnsigned2 ou2(nay2); endmodule -module ref(o1, o2, o3, o4, o5, o6, yay1, nay1, yay2, nay2); - output wire [3:0] o1, o2, o3, o4, o5, o6; +module ref(o1, o2, o3, o4, o5, o6, o7, o8, o9, yay1, nay1, yay2, nay2); + output wire [3:0] o1, o2, o3, o4, o5, o6, o7, o8, o9; assign o1 = 4'b0001; assign o2 = 4'b0001; @@ -70,6 +81,9 @@ module ref(o1, o2, o3, o4, o5, o6, yay1, nay1, yay2, nay2); assign o4 = 4'b1111; assign o5 = 4'b1110; assign o6 = 4'b1100; + assign o7 = 4'b1100; + assign o8 = 4'b1101; + assign o9 = 4'b1111; output wire [2:0] yay1, nay1; assign yay1 = 3'b111; diff --git a/tests/various/port_sign_extend.ys b/tests/various/port_sign_extend.ys index 0a6a93810..6d1adf7f3 100644 --- a/tests/various/port_sign_extend.ys +++ b/tests/various/port_sign_extend.ys @@ -1,22 +1,29 @@ -read_verilog port_sign_extend.v +read_verilog -nomem2reg port_sign_extend.v hierarchy flatten +proc +memory equiv_make ref act equiv equiv_simple equiv_status -assert delete -read_verilog port_sign_extend.v +read_verilog -nomem2reg port_sign_extend.v flatten +proc +memory equiv_make ref act equiv equiv_simple equiv_status -assert delete -read_verilog port_sign_extend.v +read_verilog -nomem2reg port_sign_extend.v hierarchy +proc +memory equiv_make ref act equiv prep -flatten -top equiv +equiv_induct equiv_status -assert diff --git a/tests/various/rand_const.sv b/tests/various/rand_const.sv new file mode 100644 index 000000000..be00812c0 --- /dev/null +++ b/tests/various/rand_const.sv @@ -0,0 +1,8 @@ +module top; + rand const reg rx; + const reg ry; + rand reg rz; + rand const integer ix; + const integer iy; + rand integer iz; +endmodule diff --git a/tests/various/rand_const.ys b/tests/various/rand_const.ys new file mode 100644 index 000000000..74e43c7cc --- /dev/null +++ b/tests/various/rand_const.ys @@ -0,0 +1 @@ +read_verilog -sv rand_const.sv diff --git a/tests/verilog/atom_type_signedness.ys b/tests/verilog/atom_type_signedness.ys new file mode 100644 index 000000000..22bbe6efc --- /dev/null +++ b/tests/verilog/atom_type_signedness.ys @@ -0,0 +1,19 @@ +read_verilog -dump_ast1 -dump_ast2 -sv <<EOT +module dut(); + +enum integer { uInteger = -10 } a; +enum int { uInt = -11 } b; +enum shortint { uShortInt = -12 } c; +enum byte { uByte = -13 } d; + +always_comb begin + assert(-10 == uInteger); + assert(-11 == uInt); + assert(-12 == uShortInt); + assert(-13 == uByte); +end +endmodule +EOT +hierarchy; proc; opt +select -module dut +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/verilog/block_labels.ys b/tests/verilog/block_labels.ys new file mode 100644 index 000000000..e76bcf771 --- /dev/null +++ b/tests/verilog/block_labels.ys @@ -0,0 +1,26 @@ +read_verilog <<EOT +module foo; + + genvar a = 0; + for (a = 0; a < 10; a++) begin : a + end : a +endmodule +EOT +read_verilog <<EOT +module foo2; + + genvar a = 0; + for (a = 0; a < 10; a++) begin : a + end +endmodule +EOT + +logger -expect error "Begin label \(a\) and end label \(b\) don't match\." 1 +read_verilog <<EOT +module foo3; + + genvar a = 0; + for (a = 0; a < 10; a++) begin : a + end : b +endmodule +EOT diff --git a/tests/verilog/bug2493.ys b/tests/verilog/bug2493.ys new file mode 100644 index 000000000..380d2a823 --- /dev/null +++ b/tests/verilog/bug2493.ys @@ -0,0 +1,12 @@ +logger -expect error "Failed to detect width for identifier \\genblk1\.y!" 1 +read_verilog <<EOT +module top1; + wire x; + generate + if (1) begin + mod y(); + assign x = y; + end + endgenerate +endmodule +EOT diff --git a/tests/verilog/bug656.v b/tests/verilog/bug656.v new file mode 100644 index 000000000..068d045fd --- /dev/null +++ b/tests/verilog/bug656.v @@ -0,0 +1,21 @@ +module top #( + parameter WIDTH = 6 +) ( + input [WIDTH-1:0] a_i, + input [WIDTH-1:0] b_i, + output [WIDTH-1:0] z_o +); + genvar g; + generate + for (g = 0; g < WIDTH; g = g + 1) begin + if (g > 2) begin + wire tmp; + assign tmp = a_i[g] || b_i[g]; + assign z_o[g] = tmp; + end + else begin + assign z_o[g] = a_i[g] && b_i[g]; + end + end + endgenerate +endmodule diff --git a/tests/verilog/bug656.ys b/tests/verilog/bug656.ys new file mode 100644 index 000000000..7f367341a --- /dev/null +++ b/tests/verilog/bug656.ys @@ -0,0 +1,13 @@ +read_verilog bug656.v + +select -assert-count 1 top/a_i +select -assert-count 1 top/b_i +select -assert-count 1 top/z_o + +select -assert-none top/genblk1[0].genblk1.tmp +select -assert-none top/genblk1[1].genblk1.tmp +select -assert-none top/genblk1[2].genblk1.tmp + +select -assert-count 1 top/genblk1[3].genblk1.tmp +select -assert-count 1 top/genblk1[4].genblk1.tmp +select -assert-count 1 top/genblk1[5].genblk1.tmp diff --git a/tests/verilog/conflict_assert.ys b/tests/verilog/conflict_assert.ys new file mode 100644 index 000000000..121a0cf51 --- /dev/null +++ b/tests/verilog/conflict_assert.ys @@ -0,0 +1,8 @@ +logger -expect error "Cannot add procedural assertion `\\x' because a signal with the same name was already created" 1 +read_verilog -sv <<EOT +module top; + wire x, y; + always @* + x: assert(y == 1); +endmodule +EOT diff --git a/tests/verilog/conflict_cell_memory.ys b/tests/verilog/conflict_cell_memory.ys new file mode 100644 index 000000000..ddc67596f --- /dev/null +++ b/tests/verilog/conflict_cell_memory.ys @@ -0,0 +1,9 @@ +logger -expect error "Cannot add cell `\\x' because a memory with the same name was already created" 1 +read_verilog <<EOT +module mod; +endmodule +module top; + reg [2:0] x [0:0]; + mod x(); +endmodule +EOT diff --git a/tests/verilog/conflict_interface_port.ys b/tests/verilog/conflict_interface_port.ys new file mode 100644 index 000000000..b97ec029e --- /dev/null +++ b/tests/verilog/conflict_interface_port.ys @@ -0,0 +1,17 @@ +logger -expect error "Cannot add interface port `\\i' because a signal with the same name was already created" 1 +read_verilog -sv <<EOT +interface intf; + logic x; + assign x = 1; + modport m(input x); +endinterface +module mod(intf.m i); + wire x; + assign x = i.x; + wire i; +endmodule +module top; + intf i(); + mod m(i); +endmodule +EOT diff --git a/tests/verilog/conflict_memory_wire.ys b/tests/verilog/conflict_memory_wire.ys new file mode 100644 index 000000000..5c296074f --- /dev/null +++ b/tests/verilog/conflict_memory_wire.ys @@ -0,0 +1,7 @@ +logger -expect error "Cannot add memory `\\x' because a signal with the same name was already created" 1 +read_verilog <<EOT +module top; + reg [2:0] x; + reg [2:0] x [0:0]; +endmodule +EOT diff --git a/tests/verilog/conflict_pwire.ys b/tests/verilog/conflict_pwire.ys new file mode 100644 index 000000000..ecc30d33a --- /dev/null +++ b/tests/verilog/conflict_pwire.ys @@ -0,0 +1,8 @@ +logger -expect error "Cannot add pwire `\\x' because a signal with the same name was already created" 1 +read_verilog -pwires <<EOT +module top; + wire x; + assign x = 1; + localparam x = 2; +endmodule +EOT diff --git a/tests/verilog/conflict_wire_memory.ys b/tests/verilog/conflict_wire_memory.ys new file mode 100644 index 000000000..77520fea9 --- /dev/null +++ b/tests/verilog/conflict_wire_memory.ys @@ -0,0 +1,7 @@ +logger -expect error "Cannot add signal `\\x' because a memory with the same name was already created" 1 +read_verilog <<EOT +module top; + reg [2:0] x [0:0]; + reg [2:0] x; +endmodule +EOT diff --git a/tests/verilog/delay_mintypmax.ys b/tests/verilog/delay_mintypmax.ys new file mode 100644 index 000000000..74359f557 --- /dev/null +++ b/tests/verilog/delay_mintypmax.ys @@ -0,0 +1,213 @@ +logger -expect-no-warnings +read_verilog <<EOT +module test (a, b, c, y); + input a; + input signed [1:0] b; + input signed [2:0] c; + output y; + assign #(12.5 : 14.5 : 20) y = ^(a ? b : c); +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog << EOT +module test (input [7:0] a, b, c, d, output [7:0] x, y, z); + assign #(20:20:25) x = a + b, y = b + c, z = c + d; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog << EOT +module test (input [7:0] a, b, c, d, output [7:0] x, y, z); + assign #(20:20:25, 40:45:50, 60:65:75) x = a + b, y = b + c, z = c + d; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test (a, b, c, y); + localparam TIME_STEP = 0.011; + input signed [3:0] a; + input signed [1:0] b; + input signed [1:0] c; + output [5:0] y; + assign #(TIME_STEP:TIME_STEP:TIME_STEP) y = (a >> b) >>> c; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + wire o, a, b; + and #(1:2:3, 4:5:6) and_gate (o, a, b); + wire #(1:2:3, 4:5:6, 7:8:9) x; + assign o = x; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + localparam TIME_TYP = 0.7; + wire o, a, b; + and #(0:TIME_TYP:2) and_gate (o, a, b); + wire #(2:TIME_TYP:4) x; + assign o = x; +endmodule +EOT + +design -reset +logger -expect warning "Yosys has only limited support for tri-state logic at the moment." 1 +read_verilog <<EOT +module test (input en, input a, input b, output c); + wire [15:0] add0_res = a + b; + assign #(15:20:30) c = (en) ? a : 1'bz; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test (input en, d, t_min, t, t_max); + reg o; + always @* + if (en) + o = #(t_min : t : t_max, t_min : t : t_max) ~d; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test #(parameter DELAY_RISE = 0, DELAY_FALL = 0, DELAY_Z = 0) + (input clock, input reset, input req_0, input req_1, output gnt_0, output gnt_1); + parameter SIZE = 3; + parameter IDLE = 3'b001, GNT0 = 3'b010, GNT1 = 3'b100; + reg [SIZE-1:0] state; + reg [SIZE-1:0] next_state; + reg gnt_0, gnt_1; + + always @ (state or req_0 or req_1) + begin : FSM_COMBO + next_state = 3'b000; + case (state) + IDLE : if (req_0 == 1'b1) begin + next_state = #(DELAY_RISE-1 : DELAY_RISE : DELAY_RISE+1) GNT0; + end else if (req_1 == 1'b1) begin + next_state = #(DELAY_FALL/1.2 : DELAY_FALL : DELAY_FALL*2.5) GNT1; + end else begin + next_state = #(DELAY_Z : DELAY_Z : DELAY_Z) IDLE; + end + GNT0 : if (req_0 == 1'b1) begin + #(DELAY_RISE : DELAY_RISE : DELAY_FALL) next_state = GNT0; + end else begin + #DELAY_RISE next_state = IDLE; + end + GNT1 : if (req_1 == 1'b1) begin + #10 next_state = GNT1; + end else begin + #(10:10:15, 20:25:25) next_state = IDLE; + end + default : next_state = IDLE; + endcase + end + + always @ (posedge clock) + begin : FSM_SEQ + if (reset == 1'b1) begin + #(10:10:15) state <= IDLE; + end else begin + #(10) state <= next_state; + end + end + + always @ (posedge clock) + begin : FSM_OUTPUT + if (reset == 1'b1) begin + gnt_0 <= #(8:9:10) 1'b0; + gnt_1 <= #1 1'b0; + end else begin + case (state) + IDLE : begin + gnt_0 <= #(8:9:10) 1'b0; + gnt_1 <= #1 1'b0; + end + GNT0 : begin + gnt_0 <= #(4:5:6,8:9:10) 1'b1; + gnt_1 <= #1 1'b0; + end + GNT1 : begin + gnt_0 <= #(2:3:4,4:5:6,8:9:10) 1'b0; + gnt_1 <= #1 1'b1; + end + default : begin + gnt_0 <= 1'b0; + gnt_1 <= 1'b0; + end + endcase + end + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg q; + initial begin + q = 1; + #(1:2:2) q = 0; + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test #(parameter hyst = 16) + (input [0:1] inA, input rst, output reg out); + parameter real updatePeriod = 100.0; + initial out = 1'b0; + always #(updatePeriod-5:updatePeriod:updatePeriod+5) begin + if (rst) out <= 1'b0; + else if (inA[0] > inA[1]) out <= 1'b1; + else if (inA[0] < inA[1] - hyst) out <= 1'b0; + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg clk; + initial clk = 1'b0; + always #(100:180:230) clk = ~clk; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg clk; + initial clk = 1'b0; + always clk = #(100:180:230, 100:180:230) ~clk; + task t_test; + sig_036_A <= #(2, 4, 5.5) 0; + sig_036_B <= #(1.3, 3) 0; + sig_036_S <= #(2) 0; + #(100 : 200 : 300, 400 : 500 : 600, 700 : 800 : 900); + sig_036_A <= #(1.5:2.5:3.0, 3:4:5, 7) ~0; + sig_036_B <= #(2, 4:6:6) ~0; + sig_036_S <= #(1.5:2.5:3.0) ~0; + #100; + endtask +endmodule +EOT diff --git a/tests/verilog/delay_risefall.ys b/tests/verilog/delay_risefall.ys new file mode 100644 index 000000000..ffbe1909d --- /dev/null +++ b/tests/verilog/delay_risefall.ys @@ -0,0 +1,225 @@ +logger -expect-no-warnings +read_verilog <<EOT +module test (a, b, c, y); + input a; + input signed [1:0] b; + input signed [2:0] c; + output y; + assign #(12.5, 14.5) y = ^(a ? b : c); +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog << EOT +module test (input [7:0] a, b, c, d, output [7:0] x, y, z); + assign #(20, 20, 25) x = a + b, y = b + c, z = c + d; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test (a, b, c, y); + localparam TIME_STEP = 0.011; + input signed [3:0] a; + input signed [1:0] b; + input signed [1:0] c; + output [5:0] y; + assign #(TIME_STEP, TIME_STEP, TIME_STEP) y = (a >> b) >>> c; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + localparam TIME_STEP = 0.7; + wire o, a, b; + and #(TIME_STEP, 2) and_gate (o, a, b); + wire #(0, TIME_STEP, TIME_STEP) x; + assign o = x; +endmodule +EOT + +design -reset +logger -expect warning "Yosys has only limited support for tri-state logic at the moment." 1 +read_verilog <<EOT +module test (input en, input a, input b, output c); + wire [15:0] add0_res = a + b; + assign #(3, 3) c = (en) ? a : 1'bz; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test (input en, d, t_rise, t_fall); + reg o; + always @* + if (en) + o = #(t_rise, t_fall, 50) ~d; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test #(parameter DELAY_RISE = 0, DELAY_FALL = 0, DELAY_Z = 0) + (input clock, input reset, input req_0, input req_1, output gnt_0, output gnt_1); + parameter SIZE = 3; + parameter IDLE = 3'b001, GNT0 = 3'b010, GNT1 = 3'b100; + reg [SIZE-1:0] state; + reg [SIZE-1:0] next_state; + reg gnt_0, gnt_1; + + always @ (state or req_0 or req_1) + begin : FSM_COMBO + next_state = 3'b000; + case (state) + IDLE : if (req_0 == 1'b1) begin + next_state = #(DELAY_RISE * 2) GNT0; + end else if (req_1 == 1'b1) begin + next_state = #(DELAY_RISE * 2.5, DELAY_FALL) GNT1; + end else begin + next_state = #(DELAY_RISE * 2.5, DELAY_FALL, DELAY_Z) IDLE; + end + GNT0 : if (req_0 == 1'b1) begin + #(DELAY_RISE, DELAY_FALL) next_state = GNT0; + end else begin + #DELAY_RISE next_state = IDLE; + end + GNT1 : if (req_1 == 1'b1) begin + #10 next_state = GNT1; + end else begin + #(10) next_state = IDLE; + end + default : next_state = IDLE; + endcase + end + + always @ (posedge clock) + begin : FSM_SEQ + if (reset == 1'b1) begin + #3 state <= IDLE; + end else begin + #(1, 3) state <= next_state; + end + end + + always @ (posedge clock) + begin : FSM_OUTPUT + if (reset == 1'b1) begin + gnt_0 <= #(DELAY_RISE, DELAY_FALL, DELAY_Z) 1'b0; + gnt_1 <= #1 1'b0; + end else begin + case (state) + IDLE : begin + gnt_0 <= #(DELAY_RISE, DELAY_FALL, DELAY_Z) 1'b0; + gnt_1 <= #1 1'b0; + end + GNT0 : begin + gnt_0 <= #(DELAY_RISE, DELAY_FALL) 1'b1; + gnt_1 <= #1 1'b0; + end + GNT1 : begin + gnt_0 <= #(DELAY_RISE) 1'b0; + gnt_1 <= #1 1'b1; + end + default : begin + gnt_0 <= 1'b0; + gnt_1 <= 1'b0; + end + endcase + end + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg q; + initial #(1,2) q = 1; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test #(parameter hyst = 16) + (input [0:1] inA, input rst, output reg out); + parameter real updatePeriod = 100.0; + initial out = 1'b0; + always #updatePeriod begin + if (rst) out <= 1'b0; + else if (inA[0] > inA[1]) out <= 1'b1; + else if (inA[0] < inA[1] - hyst) out <= 1'b0; + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test #(parameter hyst = 16) + (input [0:1] inA, input rst, output reg out); + parameter updatePeriod = (100:125:200); + initial out = 1'b0; + always #updatePeriod begin + if (rst) out <= 1'b0; + else if (inA[0] > inA[1]) out <= 1'b1; + else if (inA[0] < inA[1] - hyst) out <= 1'b0; + end +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg clk; + initial clk = 1'b0; + always #(100, 180, 230) clk = ~clk; +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test; + reg clk; + initial clk = 1'b0; + always clk = #(100, 180, 230) ~clk; + task t_test; + sig_036_A <= #(2, 4, 5.5) 0; + sig_036_B <= #(1.3, 3) 0; + sig_036_S <= #(2) 0; + #(100, 200, 300); + sig_036_A <= #(2 : 3 : 5) ~0; + sig_036_B <= #(4 : 6 : 6, 10) ~0; + sig_036_S <= #(1 : 2 : 3, 2 : 2 : 3, 10) ~0; + #100; + endtask +endmodule +EOT + +design -reset +logger -expect-no-warnings +read_verilog <<EOT +module test (clk, en, i, o); + input clk, en, i; + reg p; + output o; + always @ (posedge clk) + begin + if (en) begin + p <= #(5:15:25, 20, 30) i; + end else begin + #(5, 3:5:8, 10) p <= i; + end + end + assign #(10, 20, 15:20:30) o = p; +endmodule +EOT diff --git a/tests/verilog/func_arg_mismatch_1.ys b/tests/verilog/func_arg_mismatch_1.ys new file mode 100644 index 000000000..a0e82db0c --- /dev/null +++ b/tests/verilog/func_arg_mismatch_1.ys @@ -0,0 +1,12 @@ +logger -expect error "Incompatible re-declaration of wire" 1 +read_verilog -sv <<EOT +module top; + function automatic integer f; + input [0:0] inp; + integer inp; + f = inp; + endfunction + integer x, y; + initial x = f(y); +endmodule +EOT diff --git a/tests/verilog/func_arg_mismatch_2.ys b/tests/verilog/func_arg_mismatch_2.ys new file mode 100644 index 000000000..c2c29c1fb --- /dev/null +++ b/tests/verilog/func_arg_mismatch_2.ys @@ -0,0 +1,12 @@ +logger -expect error "Incompatible re-declaration of constant function wire" 1 +read_verilog -sv <<EOT +module top; + function automatic integer f; + input [0:0] inp; + integer inp; + f = inp; + endfunction + integer x; + initial x = f(0); +endmodule +EOT diff --git a/tests/verilog/func_arg_mismatch_3.ys b/tests/verilog/func_arg_mismatch_3.ys new file mode 100644 index 000000000..892824c09 --- /dev/null +++ b/tests/verilog/func_arg_mismatch_3.ys @@ -0,0 +1,12 @@ +logger -expect error "Incompatible re-declaration of wire" 1 +read_verilog -sv <<EOT +module top; + function automatic integer f; + input [1:0] inp; + integer inp; + f = inp; + endfunction + integer x, y; + initial x = f(y); +endmodule +EOT diff --git a/tests/verilog/func_arg_mismatch_4.ys b/tests/verilog/func_arg_mismatch_4.ys new file mode 100644 index 000000000..87ec1c299 --- /dev/null +++ b/tests/verilog/func_arg_mismatch_4.ys @@ -0,0 +1,12 @@ +logger -expect error "Incompatible re-declaration of constant function wire" 1 +read_verilog -sv <<EOT +module top; + function automatic integer f; + input [1:0] inp; + integer inp; + f = inp; + endfunction + integer x; + initial x = f(0); +endmodule +EOT diff --git a/tests/verilog/genblk_case.v b/tests/verilog/genblk_case.v new file mode 100644 index 000000000..081fb09d3 --- /dev/null +++ b/tests/verilog/genblk_case.v @@ -0,0 +1,26 @@ +module top; + parameter YES = 1; + generate + if (YES) wire y; + else wire n; + + if (!YES) wire n; + else wire y; + + case (YES) + 1: wire y; + 0: wire n; + endcase + + case (!YES) + 0: wire y; + 1: wire n; + endcase + + if (YES) wire y; + else wire n; + + if (!YES) wire n; + else wire y; + endgenerate +endmodule diff --git a/tests/verilog/genblk_case.ys b/tests/verilog/genblk_case.ys new file mode 100644 index 000000000..3c1bb51f9 --- /dev/null +++ b/tests/verilog/genblk_case.ys @@ -0,0 +1,15 @@ +read_verilog genblk_case.v + +select -assert-count 0 top/genblk1.n +select -assert-count 0 top/genblk2.n +select -assert-count 0 top/genblk3.n +select -assert-count 0 top/genblk4.n +select -assert-count 0 top/genblk5.n +select -assert-count 0 top/genblk6.n + +select -assert-count 1 top/genblk1.y +select -assert-count 1 top/genblk2.y +select -assert-count 1 top/genblk3.y +select -assert-count 1 top/genblk4.y +select -assert-count 1 top/genblk5.y +select -assert-count 1 top/genblk6.y diff --git a/tests/verilog/genblk_port_decl.ys b/tests/verilog/genblk_port_decl.ys new file mode 100644 index 000000000..589d3d2e1 --- /dev/null +++ b/tests/verilog/genblk_port_decl.ys @@ -0,0 +1,12 @@ +logger -expect error "Cannot declare module port `\\x' within a generate block\." 1 +read_verilog <<EOT +module top(x); + generate + if (1) begin : blk + output wire x; + assign x = 1; + end + endgenerate + output wire x; +endmodule +EOT diff --git a/tests/verilog/hidden_decl.ys b/tests/verilog/hidden_decl.ys new file mode 100644 index 000000000..aed7847dc --- /dev/null +++ b/tests/verilog/hidden_decl.ys @@ -0,0 +1,11 @@ +logger -expect error "Identifier `\\y' is implicitly declared and `default_nettype is set to none" 1 +read_verilog <<EOT +`default_nettype none +module top1; + wire x; + generate + if (1) wire y; + endgenerate + assign x = y; +endmodule +EOT diff --git a/tests/verilog/int_types.sv b/tests/verilog/int_types.sv new file mode 100644 index 000000000..8133f8218 --- /dev/null +++ b/tests/verilog/int_types.sv @@ -0,0 +1,47 @@ +`define TEST(typ, width, is_signed) \ + if (1) begin \ + typ x = -1; \ + localparam typ y = -1; \ + logic [127:0] a = x; \ + logic [127:0] b = y; \ + if ($bits(x) != width) \ + $error(`"typ doesn't have expected size width`"); \ + if ($bits(x) != $bits(y)) \ + $error(`"localparam typ doesn't match size of typ`"); \ + function automatic typ f; \ + input integer x; \ + f = x; \ + endfunction \ + logic [127:0] c = f(-1); \ + always @* begin \ + assert (x == y); \ + assert (a == b); \ + assert (a == c); \ + assert ((a == -1) == is_signed); \ + end \ + end + +`define TEST_INTEGER_ATOM(typ, width) \ + `TEST(typ, width, 1) \ + `TEST(typ signed, width, 1) \ + `TEST(typ unsigned, width, 0) + +`define TEST_INTEGER_VECTOR(typ) \ + `TEST(typ, 1, 0) \ + `TEST(typ signed, 1, 1) \ + `TEST(typ unsigned, 1, 0) \ + `TEST(typ [1:0], 2, 0) \ + `TEST(typ signed [1:0], 2, 1) \ + `TEST(typ unsigned [1:0], 2, 0) + +module top; + `TEST_INTEGER_ATOM(integer, 32) + `TEST_INTEGER_ATOM(int, 32) + `TEST_INTEGER_ATOM(shortint, 16) + `TEST_INTEGER_ATOM(longint, 64) + `TEST_INTEGER_ATOM(byte, 8) + + `TEST_INTEGER_VECTOR(reg) + `TEST_INTEGER_VECTOR(logic) + `TEST_INTEGER_VECTOR(bit) +endmodule diff --git a/tests/verilog/int_types.ys b/tests/verilog/int_types.ys new file mode 100644 index 000000000..c17c44b4c --- /dev/null +++ b/tests/verilog/int_types.ys @@ -0,0 +1,7 @@ +read_verilog -sv int_types.sv +hierarchy +proc +flatten +opt -full +select -module top +sat -verify -seq 1 -tempinduct -prove-asserts -show-all diff --git a/tests/verilog/macro_unapplied.ys b/tests/verilog/macro_unapplied.ys new file mode 100644 index 000000000..81eb10b8b --- /dev/null +++ b/tests/verilog/macro_unapplied.ys @@ -0,0 +1,17 @@ +logger -expect-no-warnings +read_verilog -sv <<EOT +`define MACRO(a = 1, b = 2) initial $display("MACRO(a = %d, b = %d)", a, b) +module top; + `MACRO(); +endmodule +EOT + +design -reset + +logger -expect error "Expected to find '\(' to begin macro arguments for 'MACRO', but instead found ';'" 1 +read_verilog -sv <<EOT +`define MACRO(a = 1, b = 2) initial $display("MACRO(a = %d, b = %d)", a, b) +module top; + `MACRO; +endmodule +EOT diff --git a/tests/verilog/macro_unapplied_newline.ys b/tests/verilog/macro_unapplied_newline.ys new file mode 100644 index 000000000..a3f88d5b4 --- /dev/null +++ b/tests/verilog/macro_unapplied_newline.ys @@ -0,0 +1,5 @@ +logger -expect error "Expected to find '\(' to begin macro arguments for 'foo', but instead found '\\x0a'" 1 +read_verilog -sv <<EOT +`define foo(a=1) (a) +`foo +EOT diff --git a/tests/verilog/param_int_types.sv b/tests/verilog/param_int_types.sv new file mode 100644 index 000000000..3228369b8 --- /dev/null +++ b/tests/verilog/param_int_types.sv @@ -0,0 +1,19 @@ +module gate(out); + parameter integer a = -1; + parameter int b = -2; + parameter shortint c = -3; + parameter longint d = -4; + parameter byte e = -5; + output wire [1023:0] out; + assign out = {a, b, c, d, e}; +endmodule + +module gold(out); + integer a = -1; + int b = -2; + shortint c = -3; + longint d = -4; + byte e = -5; + output wire [1023:0] out; + assign out = {a, b, c, d, e}; +endmodule diff --git a/tests/verilog/param_int_types.ys b/tests/verilog/param_int_types.ys new file mode 100644 index 000000000..7727801cf --- /dev/null +++ b/tests/verilog/param_int_types.ys @@ -0,0 +1,5 @@ +read_verilog -sv param_int_types.sv +proc +equiv_make gold gate equiv +equiv_simple +equiv_status -assert diff --git a/tests/verilog/unnamed_block.ys b/tests/verilog/unnamed_block.ys new file mode 100644 index 000000000..0f209a089 --- /dev/null +++ b/tests/verilog/unnamed_block.ys @@ -0,0 +1,28 @@ +read_verilog <<EOT +module top; + initial begin : blk + integer x; + end +endmodule +EOT + +delete + +read_verilog -sv <<EOT +module top; + initial begin + integer x; + end +endmodule +EOT + +delete + +logger -expect error "Local declaration in unnamed block is only supported in SystemVerilog mode!" 1 +read_verilog <<EOT +module top; + initial begin + integer x; + end +endmodule +EOT diff --git a/tests/verilog/unnamed_genblk.sv b/tests/verilog/unnamed_genblk.sv new file mode 100644 index 000000000..41828b1b0 --- /dev/null +++ b/tests/verilog/unnamed_genblk.sv @@ -0,0 +1,39 @@ +// This test is taken directly from Section 27.6 of IEEE 1800-2017 + +module top; + parameter genblk2 = 0; + genvar i; + + // The following generate block is implicitly named genblk1 + + if (genblk2) logic a; // top.genblk1.a + else logic b; // top.genblk1.b + + // The following generate block is implicitly named genblk02 + // as genblk2 is already a declared identifier + + if (genblk2) logic a; // top.genblk02.a + else logic b; // top.genblk02.b + + // The following generate block would have been named genblk3 + // but is explicitly named g1 + + for (i = 0; i < 1; i = i + 1) begin : g1 // block name + // The following generate block is implicitly named genblk1 + // as the first nested scope inside g1 + if (1) logic a; // top.g1[0].genblk1.a + end + + // The following generate block is implicitly named genblk4 since + // it belongs to the fourth generate construct in scope "top". + // The previous generate block would have been + // named genblk3 if it had not been explicitly named g1 + + for (i = 0; i < 1; i = i + 1) + // The following generate block is implicitly named genblk1 + // as the first nested generate block in genblk4 + if (1) logic a; // top.genblk4[0].genblk1.a + + // The following generate block is implicitly named genblk5 + if (1) logic a; // top.genblk5.a +endmodule diff --git a/tests/verilog/unnamed_genblk.ys b/tests/verilog/unnamed_genblk.ys new file mode 100644 index 000000000..2b9aa9d69 --- /dev/null +++ b/tests/verilog/unnamed_genblk.ys @@ -0,0 +1,8 @@ +read_verilog -sv unnamed_genblk.sv +select -assert-count 0 top/genblk1.a +select -assert-count 1 top/genblk02.b +select -assert-count 0 top/genblk1.a +select -assert-count 1 top/genblk02.b +select -assert-count 1 top/g1[0].genblk1.a +select -assert-count 1 top/genblk4[0].genblk1.a +select -assert-count 1 top/genblk5.a diff --git a/tests/verilog/wire_and_var.sv b/tests/verilog/wire_and_var.sv new file mode 100644 index 000000000..79c7c04c6 --- /dev/null +++ b/tests/verilog/wire_and_var.sv @@ -0,0 +1,33 @@ +`define TEST(kwd) \ + kwd kwd``_1; \ + kwd kwd``_2; \ + initial kwd``_1 = 1; \ + assign kwd``_2 = 1; + +`define TEST_VAR(kwd) \ + var kwd var_``kwd``_1; \ + var kwd var_``kwd``_2; \ + initial var_``kwd``_1 = 1; \ + assign var_``kwd``_2 = 1; + +`define TEST_WIRE(kwd) \ + wire kwd wire_``kwd``_1; \ + wire kwd wire_``kwd``_2; \ + initial wire_``kwd``_1 = 1; \ + assign wire_``kwd``_2 = 1; + +module top; + +`TEST(wire) // wire assigned in a block +`TEST(reg) // reg assigned in a continuous assignment +`TEST(logic) +`TEST(integer) + +`TEST_VAR(reg) // reg assigned in a continuous assignment +`TEST_VAR(logic) +`TEST_VAR(integer) + +`TEST_WIRE(logic) // wire assigned in a block +`TEST_WIRE(integer) // wire assigned in a block + +endmodule diff --git a/tests/verilog/wire_and_var.ys b/tests/verilog/wire_and_var.ys new file mode 100644 index 000000000..9359a9d55 --- /dev/null +++ b/tests/verilog/wire_and_var.ys @@ -0,0 +1,9 @@ +logger -expect warning "wire '\\wire_1' is assigned in a block" 1 +logger -expect warning "reg '\\reg_2' is assigned in a continuous assignment" 1 + +logger -expect warning "reg '\\var_reg_2' is assigned in a continuous assignment" 1 + +logger -expect warning "wire '\\wire_logic_1' is assigned in a block" 1 +logger -expect warning "wire '\\wire_integer_1' is assigned in a block" 1 + +read_verilog -sv wire_and_var.sv |