From 2507d01b03cb4ec0a1345532d22688dd9a623948 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 23 Mar 2019 16:45:36 -0700 Subject: [PATCH 01/15] Add a pmux-to-shiftx optimisation to proc_mux --- passes/proc/proc_mux.cc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 1329c1fef..7cac0327b 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -340,6 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d // evaluate in reverse order to give the first entry the top priority RTLIL::SigSpec initial_val = result; RTLIL::Cell *last_mux_cell = NULL; + bool shiftx = true; for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; @@ -348,6 +349,26 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); else result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); + + // Ignore output values which are entirely don't care + if (shiftx && !value.is_fully_undef()) { + // Keep checking if case condition is the same as the current case index + if (cs2->compare.size() == 1 && cs2->compare.front().is_fully_const()) + shiftx = (cs2->compare.front().as_int() == case_idx); + } + } + + // Transform into a $shiftx if possible + if (shiftx && last_mux_cell->type == "$pmux") { + // Sanity check that A port of $pmux should be 'bx + log_assert(last_mux_cell->getPort("\\A").is_fully_undef()); + // Because we went in reverse order above, un-reverse its B port here + auto b_port = last_mux_cell->getPort("\\B").chunks(); + std::reverse(b_port.begin(), b_port.end()); + // Create a $shiftx that shifts by the address line used in the case statement + mod->addShiftx(NEW_ID, b_port, sw->signal, last_mux_cell->getPort("\\Y")); + // Disconnect $pmux by replacing its output port with a floating wire + last_mux_cell->setPort("\\Y", mod->addWire(NEW_ID, last_mux_cell->getParam("\\WIDTH").as_int())); } } From b7a3d35c6b02f44dcad4052e2efe05c32fdada6b Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 25 Mar 2019 11:16:56 -0700 Subject: [PATCH 02/15] Create one $shiftx per bit in width --- passes/proc/proc_mux.cc | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 7cac0327b..7d3d23408 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -340,7 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d // evaluate in reverse order to give the first entry the top priority RTLIL::SigSpec initial_val = result; RTLIL::Cell *last_mux_cell = NULL; - bool shiftx = true; + bool shiftx = initial_val.is_fully_undef(); for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; @@ -355,20 +355,27 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d // Keep checking if case condition is the same as the current case index if (cs2->compare.size() == 1 && cs2->compare.front().is_fully_const()) shiftx = (cs2->compare.front().as_int() == case_idx); + else + shiftx = false; } } - // Transform into a $shiftx if possible + // Transform into a $shiftx where possible if (shiftx && last_mux_cell->type == "$pmux") { - // Sanity check that A port of $pmux should be 'bx - log_assert(last_mux_cell->getPort("\\A").is_fully_undef()); - // Because we went in reverse order above, un-reverse its B port here - auto b_port = last_mux_cell->getPort("\\B").chunks(); - std::reverse(b_port.begin(), b_port.end()); - // Create a $shiftx that shifts by the address line used in the case statement - mod->addShiftx(NEW_ID, b_port, sw->signal, last_mux_cell->getPort("\\Y")); + // Create bit-blasted $shiftx-es that shifts by the address line used in the case statement + auto pmux_b_port = last_mux_cell->getPort("\\B"); + auto pmux_y_port = last_mux_cell->getPort("\\Y"); + int width = last_mux_cell->getParam("\\WIDTH").as_int(); + for (int i = 0; i < width; ++i) { + RTLIL::SigSpec a_port; + // Because we went in reverse order above, un-reverse $pmux's B port here + for (int j = pmux_b_port.size()/width-1; j >= 0; --j) + a_port.append(pmux_b_port.extract(j*width+i, 1)); + // Create a $shiftx that shifts by the address line used in the case statement + mod->addShiftx(NEW_ID, a_port, sw->signal, pmux_y_port.extract(i, 1)); + } // Disconnect $pmux by replacing its output port with a floating wire - last_mux_cell->setPort("\\Y", mod->addWire(NEW_ID, last_mux_cell->getParam("\\WIDTH").as_int())); + last_mux_cell->setPort("\\Y", mod->addWire(NEW_ID, width)); } } From c863796e9ff91c76f0f8679b6871b8ffcb75edb6 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 26 Mar 2019 14:17:46 +0100 Subject: [PATCH 03/15] Fix "verific -extnets" for more complex situations Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 86 +++++++++++++++++++++++++++++------- tests/sva/extnets.sv | 22 +++++++++ 2 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 tests/sva/extnets.sv diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index c412cd3a3..95b7d3586 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1619,30 +1619,35 @@ struct VerificExtNets int portname_cnt = 0; // a map from Net to the same Net one level up in the design hierarchy - std::map net_level_up; + std::map net_level_up_drive_up; + std::map net_level_up_drive_down; - Net *get_net_level_up(Net *net) + Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr) { + auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down; + if (net_level_up.count(net) == 0) { Netlist *nl = net->Owner(); // Simply return if Netlist is not unique - if (nl->NumOfRefs() != 1) - return net; + log_assert(nl->NumOfRefs() == 1); Instance *up_inst = (Instance*)nl->GetReferences()->GetLast(); Netlist *up_nl = up_inst->Owner(); // create new Port string name = stringf("___extnets_%d", portname_cnt++); - Port *new_port = new Port(name.c_str(), DIR_OUT); + Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN); nl->Add(new_port); net->Connect(new_port); // create new Net in up Netlist - Net *new_net = new Net(name.c_str()); - up_nl->Add(new_net); + Net *new_net = final_net; + if (new_net == nullptr || new_net->Owner() != up_nl) { + new_net = new Net(name.c_str()); + up_nl->Add(new_net); + } up_inst->Connect(new_port, new_net); net_level_up[net] = new_net; @@ -1651,6 +1656,39 @@ struct VerificExtNets return net_level_up.at(net); } + Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr) + { + while (net->Owner() != dest) + net = route_up(net, drive_up, final_net); + if (final_net != nullptr) + log_assert(net == final_net); + return net; + } + + Netlist *find_common_ancestor(Netlist *A, Netlist *B) + { + std::set ancestors_of_A; + + Netlist *cursor = A; + while (1) { + ancestors_of_A.insert(cursor); + if (cursor->NumOfRefs() != 1) + break; + cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); + } + + cursor = B; + while (1) { + if (ancestors_of_A.count(cursor)) + return cursor; + if (cursor->NumOfRefs() != 1) + break; + cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); + } + + log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str()); + } + void run(Netlist *nl) { MapIter mi, mi2; @@ -1674,19 +1712,37 @@ struct VerificExtNets if (verific_verbose) log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name()); - while (net->IsExternalTo(nl)) - { - Net *newnet = get_net_level_up(net); - if (newnet == net) break; + Netlist *ext_nl = net->Owner(); + if (verific_verbose) + log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str()); + + Netlist *ca_nl = find_common_ancestor(nl, ext_nl); + + if (verific_verbose) + log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str()); + + Net *ca_net = route_up(net, !port->IsOutput(), ca_nl); + Net *new_net = ca_net; + + if (ca_nl != nl) + { if (verific_verbose) - log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name()); - net = newnet; + log(" net in common ancestor: %s\n", ca_net->Name()); + + string name = stringf("___extnets_%d", portname_cnt++); + new_net = new Net(name.c_str()); + nl->Add(new_net); + + Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net); + log_assert(n == ca_net); } if (verific_verbose) - log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : ""); - todo_connect.push_back(tuple(inst, port, net)); + log(" new local net: %s\n", new_net->Name()); + + log_assert(!new_net->IsExternalTo(nl)); + todo_connect.push_back(tuple(inst, port, new_net)); } for (auto it : todo_connect) { diff --git a/tests/sva/extnets.sv b/tests/sva/extnets.sv new file mode 100644 index 000000000..47312de7a --- /dev/null +++ b/tests/sva/extnets.sv @@ -0,0 +1,22 @@ +module top(input i, output o); + A A(); + B B(); + assign A.i = i; + assign o = B.o; + always @* assert(o == i); +endmodule + +module A; + wire i, y; +`ifdef FAIL + assign B.x = i; +`else + assign B.x = !i; +`endif + assign y = !B.y; +endmodule + +module B; + wire x, y, o; + assign y = x, o = A.y; +endmodule From d0b9b1bece9866fd7b0e153c991fc7e9b57a1efc Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 26 Mar 2019 14:51:35 +0100 Subject: [PATCH 04/15] Add "hdlname" attribute Signed-off-by: Clifford Wolf --- README.md | 3 +++ passes/hierarchy/uniquify.cc | 2 ++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index c5cd47707..4048ecbc7 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,9 @@ Verilog Attributes and non-standard features - The ``dynports'' attribute is used by the Verilog front-end to mark modules that have ports with a width that depends on a parameter. +- The ``hdlname'' attribute is used by some passes to document the original + (HDL) name of a module when renaming a module. + - The ``keep`` attribute on cells and wires is used to mark objects that should never be removed by the optimizer. This is used for example for cells that have hidden connections that are not part of the netlist, such as IO pads. diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc index c88ecd82e..e6154e94f 100644 --- a/passes/hierarchy/uniquify.cc +++ b/passes/hierarchy/uniquify.cc @@ -87,6 +87,8 @@ struct UniquifyPass : public Pass { smod->name = newname; cell->type = newname; smod->set_bool_attribute("\\unique"); + if (smod->attributes.count("\\hdlname") == 0) + smod->attributes["\\hdlname"] = string(log_id(tmod->name)); design->add(smod); did_something = true; From 38b3fbd3f0bbdace11a3ab7b3d153b1a05059378 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 26 Mar 2019 16:01:14 +0100 Subject: [PATCH 05/15] Add "cutpoint -undef" Signed-off-by: Clifford Wolf --- passes/sat/cutpoint.cc | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 3a38ebac0..048aec7f3 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -33,20 +33,24 @@ struct CutpointPass : public Pass { log("\n"); log("This command adds formal cut points to the design.\n"); log("\n"); + log(" -undef\n"); + log(" set cupoint nets to undef (x). the default behavior is to create a\n"); + log(" $anyseq cell and drive the cutpoint net from that\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - // bool flag_noinit = false; + bool flag_undef = false; log_header(design, "Executing CUTPOINT pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - // if (args[argidx] == "-noinit") { - // flag_noinit = true; - // continue; - // } + if (args[argidx] == "-undef") { + flag_undef = true; + continue; + } break; } extra_args(args, argidx, design); @@ -63,7 +67,7 @@ struct CutpointPass : public Pass { if (wire->port_output) output_wires.push_back(wire); for (auto wire : output_wires) - module->connect(wire, module->Anyseq(NEW_ID, GetSize(wire))); + module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire))); continue; } @@ -76,7 +80,7 @@ struct CutpointPass : public Pass { log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell)); for (auto &conn : cell->connections()) { if (cell->output(conn.first)) - module->connect(conn.second, module->Anyseq(NEW_ID, GetSize(conn.second))); + module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second))); } module->remove(cell); } @@ -86,7 +90,7 @@ struct CutpointPass : public Pass { log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire)); Wire *new_wire = module->addWire(NEW_ID, wire); module->swap_names(wire, new_wire); - module->connect(new_wire, module->Anyseq(NEW_ID, GetSize(new_wire))); + module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire))); wire->port_id = 0; wire->port_input = false; wire->port_output = false; @@ -142,7 +146,7 @@ struct CutpointPass : public Pass { rhs.append(SigBit(new_wire, i)); } if (GetSize(lhs)) - module->connect(lhs, rhs); + module->connect(lhs, rhs); module->swap_names(wire, new_wire); wire->port_id = 0; wire->port_input = false; @@ -154,7 +158,7 @@ struct CutpointPass : public Pass { for (auto chunk : sig.chunks()) { SigSpec s(chunk); - module->connect(s, module->Anyseq(NEW_ID, GetSize(s))); + module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s))); } } } From d351b7cb99efe8c412ef7fa8bc0b99b72bc56726 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 27 Mar 2019 13:33:26 +0100 Subject: [PATCH 06/15] Improve "rename" help message Signed-off-by: Clifford Wolf --- passes/cmds/rename.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 698ce7235..466a5da53 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -108,15 +108,19 @@ struct RenamePass : public Pass { log("Rename the specified object. Note that selection patterns are not supported\n"); log("by this command.\n"); log("\n"); + log("\n"); log(" rename -src [selection]\n"); log("\n"); log("Assign names auto-generated from the src attribute to all selected wires and\n"); log("cells with private names.\n"); log("\n"); + log("\n"); log(" rename -wire [selection]\n"); + log("\n"); log("Assign auto-generated names based on the wires they drive to all selected\n"); log("cells with private names. Ignores cells driving privatly named wires.\n"); log("\n"); + log("\n"); log(" rename -enumerate [-pattern ] [selection]\n"); log("\n"); log("Assign short auto-generated names to all selected wires and cells with private\n"); @@ -124,11 +128,13 @@ struct RenamePass : public Pass { log("The character %% in the pattern is replaced with a integer number. The default\n"); log("pattern is '_%%_'.\n"); log("\n"); + log("\n"); log(" rename -hide [selection]\n"); log("\n"); log("Assign private names (the ones with $-prefix) to all selected wires and cells\n"); log("with public names. This ignores all selected ports.\n"); log("\n"); + log("\n"); log(" rename -top new_name\n"); log("\n"); log("Rename top module.\n"); From 2c7fe42ad158a9859895399bdd876f5dbb2c7376 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 27 Mar 2019 13:47:42 +0100 Subject: [PATCH 07/15] Add "rename -output" Signed-off-by: Clifford Wolf --- passes/cmds/rename.cc | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 466a5da53..9b1830b7b 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -24,7 +24,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name) +static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output) { from_name = RTLIL::escape_id(from_name); to_name = RTLIL::escape_id(to_name); @@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: Wire *w = it.second; log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module)); module->rename(w, to_name); - if (w->port_id) + if (w->port_id || flag_output) { + if (flag_output) + w->port_output = true; module->fixup_ports(); + } return; } for (auto &it : module->cells_) if (it.first == from_name) { + if (flag_output) + log_cmd_error("Called with -output but the specified object is a cell.\n"); log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module)); module->rename(it.second, to_name); return; @@ -109,6 +114,13 @@ struct RenamePass : public Pass { log("by this command.\n"); log("\n"); log("\n"); + log("\n"); + log(" rename -output old_name new_name\n"); + log("\n"); + log("Like above, but also make the wire an output. This will fail if the object is\n"); + log("not a wire.\n"); + log("\n"); + log("\n"); log(" rename -src [selection]\n"); log("\n"); log("Assign names auto-generated from the src attribute to all selected wires and\n"); @@ -148,6 +160,7 @@ struct RenamePass : public Pass { bool flag_enumerate = false; bool flag_hide = false; bool flag_top = false; + bool flag_output = false; bool got_mode = false; size_t argidx; @@ -159,6 +172,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-output" && !got_mode) { + flag_output = true; + got_mode = true; + continue; + } if (arg == "-wire" && !got_mode) { flag_wire = true; got_mode = true; @@ -328,10 +346,12 @@ struct RenamePass : public Pass { if (!design->selected_active_module.empty()) { if (design->modules_.count(design->selected_active_module) > 0) - rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name); + rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output); } else { + if (flag_output) + log_cmd_error("Mode -output requires that there is an active module selected.\n"); for (auto &mod : design->modules_) { if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) { to_name = RTLIL::escape_id(to_name); From 7682629b79fd59f5ed49fb35a3a2441a405bfd63 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 27 Mar 2019 14:03:35 +0100 Subject: [PATCH 08/15] Add "read -verific" and "read -noverific" Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 95b7d3586..ed9727b88 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2386,21 +2386,43 @@ struct ReadPass : public Pass { log("\n"); log("Add directory to global Verilog/SystemVerilog include directories.\n"); log("\n"); + log("\n"); + log(" read -verific\n"); + log(" read -noverific\n"); + log("\n"); + log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n"); + log("with -verific will result in an error on Yosys binaries that are built without\n"); + log("Verific support. The default is to use Verific if it is available.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { +#ifdef YOSYS_ENABLE_VERIFIC + static bool verific_available = !check_noverific_env(); +#else + static bool verific_available = false; +#endif + static bool use_verific = verific_available; + if (args.size() < 2 || args[1][0] != '-') log_cmd_error("Missing mode parameter.\n"); + if (args[1] == "-verific" || args[1] == "-noverific") { + if (args.size() != 2) + log_cmd_error("Additional arguments to -verific/-noverific.\n"); + if (args[1] == "-verific") { + if (!verific_available) + log_cmd_error("This version of Yosys is built without Verific support.\n"); + use_verific = true; + } else { + use_verific = false; + } + return; + } + if (args.size() < 3) log_cmd_error("Missing file name parameter.\n"); -#ifdef YOSYS_ENABLE_VERIFIC - bool use_verific = !check_noverific_env(); -#else - bool use_verific = false; -#endif - if (args[1] == "-vlog95" || args[1] == "-vlog2k") { if (use_verific) { args[0] = "verific"; From 487cb45b87ce1cbcc8c2b8127e37d85dd192dceb Mon Sep 17 00:00:00 2001 From: Niels Moseley Date: Wed, 27 Mar 2019 15:15:53 +0100 Subject: [PATCH 09/15] Liberty file parser now accepts superfluous ; --- passes/techmap/libparse.cc | 61 ++++++++++++++++++++++++++++++---- tests/liberty/normal.lib | 3 +- tests/liberty/processdefs.lib | 48 ++++++++++++++++++++++++++ tests/liberty/semicolextra.lib | 48 ++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 tests/liberty/processdefs.lib create mode 100644 tests/liberty/semicolextra.lib diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 8eadd8735..510a24c24 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -155,11 +155,13 @@ int LibertyParser::lexer(std::string &str) // check for a backslash if (c == '\\') { - c = f.get(); + c = f.get(); if (c == '\r') c = f.get(); - if (c == '\n') + if (c == '\n') { + line++; return lexer(str); + } f.unget(); return '\\'; } @@ -186,14 +188,39 @@ LibertyAst *LibertyParser::parse() int tok = lexer(str); - while (tok == 'n') + // there are liberty files in the while that + // have superfluous ';' at the end of + // a { ... }. We simply ignore a ';' here. + // and get to the next statement. + + while ((tok == 'n') || (tok == ';')) tok = lexer(str); if (tok == '}' || tok < 0) return NULL; - if (tok != 'v') - error(); + if (tok != 'v') { + std::string eReport; + switch(tok) + { + case 'n': + error("Unexpected newline."); + break; + case '[': + case ']': + case '}': + case '{': + case '\"': + case ':': + eReport = "Unexpected '"; + eReport += static_cast(tok); + eReport += "'."; + error(eReport); + break; + default: + error(); + } + } LibertyAst *ast = new LibertyAst; ast->id = str; @@ -282,8 +309,28 @@ LibertyAst *LibertyParser::parse() } continue; } - if (tok != 'v') - error(); + if (tok != 'v') { + std::string eReport; + switch(tok) + { + case 'n': + error("Unexpected newline."); + break; + case '[': + case ']': + case '}': + case '{': + case '\"': + case ':': + eReport = "Unexpected '"; + eReport += static_cast(tok); + eReport += "'."; + error(eReport); + break; + default: + error(); + } + } ast->args.push_back(arg); } continue; diff --git a/tests/liberty/normal.lib b/tests/liberty/normal.lib index 1474e2b59..4621194dd 100644 --- a/tests/liberty/normal.lib +++ b/tests/liberty/normal.lib @@ -142,8 +142,7 @@ library(supergate) { } /* D-type flip-flop with asynchronous reset and preset */ - cell (dff) - { + cell (dff) { area : 6; ff("IQ", "IQN") { next_state : "D"; diff --git a/tests/liberty/processdefs.lib b/tests/liberty/processdefs.lib new file mode 100644 index 000000000..37a6bbaf8 --- /dev/null +++ b/tests/liberty/processdefs.lib @@ -0,0 +1,48 @@ +/********************************************/ +/* */ +/* Supergate cell library for Bench marking */ +/* */ +/* Symbiotic EDA GmbH / Moseley Instruments */ +/* Niels A. Moseley */ +/* */ +/* Process: none */ +/* */ +/* Date : 25-03-2019 */ +/* Version: 1.0 */ +/* */ +/********************************************/ + +library(processdefs) { + technology (cmos); + revision : 1.0; + + time_unit : "1ps"; + pulling_resistance_unit : "1kohm"; + voltage_unit : "1V"; + current_unit : "1uA"; + + capacitive_load_unit(1,ff); + + default_inout_pin_cap : 7.0; + default_input_pin_cap : 7.0; + default_output_pin_cap : 0.0; + default_fanout_load : 1.0; + + default_wire_load_capacitance : 0.1; + default_wire_load_resistance : 1.0e-3; + default_wire_load_area : 0.0; + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 1.2; + + delay_model : generic_cmos; + + define_cell_area(bond_pads,pad_slots) + input_voltage(cmos) { + vil : 0.3 * VDD ; + vih : 0.7 * VDD ; + vimin : -0.5 ; + vimax : VDD + 0.5 ; + } +} diff --git a/tests/liberty/semicolextra.lib b/tests/liberty/semicolextra.lib new file mode 100644 index 000000000..0144fa3ac --- /dev/null +++ b/tests/liberty/semicolextra.lib @@ -0,0 +1,48 @@ +/* + + Test case for https://www.reddit.com/r/yosys/comments/b5texg/yosys_fails_to_parse_apparentlycorrect_liberty/ + + fall_constraint (SETUP_HOLD) formatting. + +*/ + +library(supergate) { + technology (cmos); + revision : 1.0; + + cell (DFF) { + cell_footprint : dff; + area : 50; + pin(D) { + direction : input; + capacitance : 0.002; + timing() { + related_pin : "CK"; + timing_type : setup_rising; + + fall_constraint (SETUP_HOLD) { values ("0.4000, 0.3000, 0.2000, 0.1000, 0.0000", \ + "0.4000, 0.3000, 0.2000, 0.1000, 0.000", \ + "0.5000, 0.4000, 0.3000, 0.2000, 0.0000", \ + "0.7000, 0.6000, 0.5000, 0.4000, 0.2000", \ + "1.0000, 1.0000, 0.9000, 0.8000, 0.6000"); } ; + } + } + + pin(CK) { + direction : input; + clock : true; + capacitance : 0.00290; + } + + ff(IQ,IQN) { + clocked_on : "CK"; + next_state : "D"; + } + pin(Q) { + direction : output; + capacitance : 0.003; + max_capacitance : 0.3; + } + cell_leakage_power : 0.3; + } +} From ee130f67cdd2ea40defa873e9a6511329850bf8e Mon Sep 17 00:00:00 2001 From: Niels Moseley Date: Wed, 27 Mar 2019 15:16:19 +0100 Subject: [PATCH 10/15] Liberty file parser now accepts superfluous ; --- tests/liberty/semicolextra.lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/liberty/semicolextra.lib b/tests/liberty/semicolextra.lib index 0144fa3ac..6a7fa77cc 100644 --- a/tests/liberty/semicolextra.lib +++ b/tests/liberty/semicolextra.lib @@ -24,7 +24,7 @@ library(supergate) { "0.4000, 0.3000, 0.2000, 0.1000, 0.000", \ "0.5000, 0.4000, 0.3000, 0.2000, 0.0000", \ "0.7000, 0.6000, 0.5000, 0.4000, 0.2000", \ - "1.0000, 1.0000, 0.9000, 0.8000, 0.6000"); } ; + "1.0000, 1.0000, 0.9000, 0.8000, 0.6000"); }; } } From 263ab60b43f3994e83bfa46b793669147d765bcc Mon Sep 17 00:00:00 2001 From: Niels Moseley Date: Wed, 27 Mar 2019 15:17:58 +0100 Subject: [PATCH 11/15] Liberty file parser now accepts superfluous ; --- passes/techmap/libparse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 510a24c24..991cc4498 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -188,7 +188,7 @@ LibertyAst *LibertyParser::parse() int tok = lexer(str); - // there are liberty files in the while that + // there are liberty files in the wild that // have superfluous ';' at the end of // a { ... }. We simply ignore a ';' here. // and get to the next statement. From 60594ad40cb51f6cf8a5e6ce61377b3405b873a8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 27 Mar 2019 17:19:14 +0000 Subject: [PATCH 12/15] memory_bram: Reset make_transp when growing read ports Signed-off-by: David Shah --- passes/memory/memory_bram.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index c38eabaee..85ed1c053 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -641,6 +641,7 @@ grow_read_ports:; pi.sig_data = SigSpec(); pi.sig_en = SigSpec(); pi.make_outreg = false; + pi.make_transp = false; } new_portinfos.push_back(pi); if (pi.dupidx == dup_count-1) { From 584d2030bf53c703febe8fda9cae73c72416c6cc Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 29 Mar 2019 16:32:44 +0100 Subject: [PATCH 13/15] Build Verilog parser with -DYYMAXDEPTH=100000, fixes #906 Signed-off-by: Clifford Wolf --- frontends/verilog/Makefile.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index dbaace585..0a1f97ac0 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -14,6 +14,8 @@ frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/verilog/verilog_lexer.cc $< +frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=100000 + OBJS += frontends/verilog/verilog_parser.tab.o OBJS += frontends/verilog/verilog_lexer.o OBJS += frontends/verilog/preproc.o From 73b87e780798fe2c7958b75e4dfddc0dc2169d20 Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Mon, 1 Apr 2019 15:02:12 -0700 Subject: [PATCH 14/15] Refine memory support to deal with general Verilog memory definitions. --- backends/firrtl/firrtl.cc | 203 ++++++++++++++++++++++++++++++++------ 1 file changed, 173 insertions(+), 30 deletions(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index eef6401b2..ed6e9f8ee 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -106,6 +106,95 @@ struct FirrtlWorker RTLIL::Design *design; std::string indent; + // Define read/write ports and memories. + // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction. + // For the moment, we don't handle $readmemh or $readmemb. + // These will be part of a subsequent PR. + struct read_port { + string name; + bool clk_enable; + bool clk_parity; + bool transparent; + RTLIL::SigSpec clk; + RTLIL::SigSpec ena; + RTLIL::SigSpec addr; + read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) { + // Current (3/13/2019) conventions: + // generate a constant 0 for clock and a constant 1 for enable if they are undefined. + if (!clk.is_fully_def()) + this->clk = SigSpec(RTLIL::Const(0, 1)); + if (!ena.is_fully_def()) + this->ena = SigSpec(RTLIL::Const(1, 1)); + } + string gen_read(const char * indent) { + string addr_expr = make_expr(addr); + string ena_expr = make_expr(ena); + string clk_expr = make_expr(clk); + string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); + string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); + string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); + return addr_str + ena_str + clk_str; + } + }; + struct write_port : read_port { + RTLIL::SigSpec mask; + write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) { + if (!clk.is_fully_def()) + this->clk = SigSpec(RTLIL::Const(0)); + if (!ena.is_fully_def()) + this->ena = SigSpec(RTLIL::Const(0)); + if (!mask.is_fully_def()) + this->ena = SigSpec(RTLIL::Const(1)); + } + string gen_read(const char * /* indent */) { + log_error("gen_read called on write_port: %s\n", name.c_str()); + return stringf("gen_read called on write_port: %s\n", name.c_str()); + } + string gen_write(const char * indent) { + string addr_expr = make_expr(addr); + string ena_expr = make_expr(ena); + string clk_expr = make_expr(clk); + string mask_expr = make_expr(mask); + string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str()); + string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); + string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); + string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); + return addr_str + ena_str + clk_str + mask_str; + } + }; + /* Memories defined within this module. */ + struct memory { + string name; // memory name + int abits; // number of address bits + int size; // size (in units) of the memory + int width; // size (in bits) of each element + int read_latency; + int write_latency; + vector read_ports; + vector write_ports; + std::string init_file; + std::string init_file_srcFileSpec; + memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {} + memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} + void add_memory_read_port(read_port &rp) { + read_ports.push_back(rp); + } + void add_memory_write_port(write_port &wp) { + write_ports.push_back(wp); + } + void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { + this->init_file = init_file; + this->init_file_srcFileSpec = init_file_srcFileSpec; + } + + }; + dict memories; + + void register_memory(memory &m) + { + memories[m.name] = m; + } + void register_reverse_wire_map(string id, SigSpec sig) { for (int i = 0; i < GetSize(sig); i++) @@ -116,7 +205,7 @@ struct FirrtlWorker { } - string make_expr(const SigSpec &sig) + static string make_expr(const SigSpec &sig) { string expr; @@ -515,6 +604,7 @@ struct FirrtlWorker int abits = cell->parameters.at("\\ABITS").as_int(); int width = cell->parameters.at("\\WIDTH").as_int(); int size = cell->parameters.at("\\SIZE").as_int(); + memory m(mem_id, abits, size, width); int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); @@ -531,33 +621,24 @@ struct FirrtlWorker if (offset != 0) log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell)); - cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str())); - cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width)); - cell_exprs.push_back(stringf(" depth => %d\n", size)); - - for (int i = 0; i < rd_ports; i++) - cell_exprs.push_back(stringf(" reader => r%d\n", i)); - - for (int i = 0; i < wr_ports; i++) - cell_exprs.push_back(stringf(" writer => w%d\n", i)); - - cell_exprs.push_back(stringf(" read-latency => 0\n")); - cell_exprs.push_back(stringf(" write-latency => 1\n")); - cell_exprs.push_back(stringf(" read-under-write => undefined\n")); - for (int i = 0; i < rd_ports; i++) { if (rd_clk_enable[i] != State::S0) log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits); SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width); - string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits)); - - cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); - cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i)); - cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i)); - - register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig); + string addr_expr = make_expr(addr_sig); + string name(stringf("%s.r%d", m.name.c_str(), i)); + bool clk_enable = false; + bool clk_parity = true; + bool transparency = false; + SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); + SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1); + read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig); + m.add_memory_read_port(rp); + cell_exprs.push_back(rp.gen_read(indent.c_str())); + register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig); } for (int i = 0; i < wr_ports; i++) @@ -568,9 +649,16 @@ struct FirrtlWorker if (wr_clk_polarity[i] != State::S1) log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits)); - string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width)); - string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i)); + string name(stringf("%s.w%d", m.name.c_str(), i)); + bool clk_enable = true; + bool clk_parity = true; + bool transparency = false; + SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits); + string addr_expr = make_expr(addr_sig); + SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width); + string data_expr = make_expr(data_sig); + SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i); + string clk_expr = make_expr(clk_sig); SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width); string wen_expr = make_expr(wen_sig[0]); @@ -579,13 +667,50 @@ struct FirrtlWorker if (wen_sig[0] != wen_sig[i]) log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i)); - cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str())); + SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); + write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig); + m.add_memory_write_port(wp); + cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str())); + cell_exprs.push_back(wp.gen_write(indent.c_str())); } + register_memory(m); + continue; + } + if (cell->type.in("$memwr", "$memrd", "$meminit")) + { + std::string cell_type = fid(cell->type); + std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string()); + memory *mp = nullptr; + if (cell->type == "$meminit" ) { + log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); + } else { + // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition. + auto addrSig = cell->getPort("\\ADDR"); + auto dataSig = cell->getPort("\\DATA"); + auto enableSig = cell->getPort("\\EN"); + auto clockSig = cell->getPort("\\CLK"); + Const clk_enable = cell->parameters.at("\\CLK_ENABLE"); + Const clk_polarity = cell->parameters.at("\\CLK_POLARITY"); + + mp = &memories.at(mem_id); + int portNum = 0; + bool transparency = false; + string data_expr = make_expr(dataSig); + if (cell->type.in("$memwr")) { + portNum = (int) mp->write_ports.size(); + write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig); + mp->add_memory_write_port(wp); + cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str())); + cell_exprs.push_back(wp.gen_write(indent.c_str())); + } else if (cell->type.in("$memrd")) { + portNum = (int) mp->read_ports.size(); + read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig); + mp->add_memory_read_port(rp); + cell_exprs.push_back(rp.gen_read(indent.c_str())); + register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig); + } + } continue; } @@ -763,6 +888,24 @@ struct FirrtlWorker f << stringf("\n"); + // If we have any memory definitions, output them. + for (auto kv : memories) { + memory m = kv.second; + f << stringf(" mem %s:\n", m.name.c_str()); + f << stringf(" data-type => UInt<%d>\n", m.width); + f << stringf(" depth => %d\n", m.size); + for (int i = 0; i < (int) m.read_ports.size(); i += 1) { + f << stringf(" reader => r%d\n", i); + } + for (int i = 0; i < (int) m.write_ports.size(); i += 1) { + f << stringf(" writer => w%d\n", i); + } + f << stringf(" read-latency => %d\n", m.read_latency); + f << stringf(" write-latency => %d\n", m.write_latency); + f << stringf(" read-under-write => undefined\n"); + } + f << stringf("\n"); + for (auto str : cell_exprs) f << str; From 6acbc016f43b1464e6322b895f16d01ed51eea18 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 2 Apr 2019 19:47:50 +0100 Subject: [PATCH 15/15] memory_bram: Consider read enable for address expansion register Signed-off-by: David Shah --- passes/memory/memory_bram.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 85ed1c053..804aa21f9 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -957,6 +957,8 @@ grow_read_ports:; SigSpec addr_ok_q = addr_ok; if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) { addr_ok_q = module->addWire(NEW_ID); + if (!pi.sig_en.empty()) + addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en); module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); }