diff options
-rw-r--r-- | CHANGELOG | 3 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | backends/aiger/xaiger.cc | 7 | ||||
-rw-r--r-- | frontends/verific/verific.cc | 17 | ||||
-rw-r--r-- | frontends/verific/verific.h | 2 | ||||
-rw-r--r-- | frontends/verific/verificsva.cc | 42 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 6 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 39 | ||||
-rw-r--r-- | passes/hierarchy/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/hierarchy/clkpart.cc | 269 | ||||
-rw-r--r-- | passes/hierarchy/submod.cc | 74 | ||||
-rw-r--r-- | passes/memory/memory.cc | 2 | ||||
-rw-r--r-- | passes/opt/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/opt/opt_mem.cc | 143 | ||||
-rw-r--r-- | passes/proc/proc_dlatch.cc | 20 | ||||
-rw-r--r-- | passes/techmap/abc9.cc | 2 | ||||
-rw-r--r-- | techlibs/gowin/.gitignore | 2 | ||||
-rw-r--r-- | tests/arch/gowin/mux.ys | 1 | ||||
-rw-r--r-- | tests/simple_abc9/abc9.v | 12 | ||||
-rw-r--r-- | tests/various/submod.ys | 50 | ||||
-rwxr-xr-x | tests/various/svalways.sh | 63 |
21 files changed, 702 insertions, 59 deletions
@@ -51,6 +51,9 @@ Yosys 0.9 .. Yosys 0.9-dev - "synth_ice40 -dsp" to infer DSP blocks - Added latch support to synth_xilinx - Added "check -mapped" + - Added checking of SystemVerilog always block types (always_comb, + always_latch and always_ff) + - Added "clkpart" pass Yosys 0.8 .. Yosys 0.9 ---------------------- @@ -371,6 +371,11 @@ Verilog Attributes and non-standard features for example, to specify the clk-to-Q delay of a flip-flop for consideration during techmapping. +- The frontend sets attributes ``always_comb``, ``always_latch`` and + ``always_ff`` on processes derived from SystemVerilog style always blocks + according to the type of the always. These are checked for correctness in + ``proc_dlatch``. + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 5d125b653..de2f7dd73 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -545,15 +545,12 @@ struct XAigerWriter undriven_bits.erase(bit); if (!undriven_bits.empty() && !holes_mode) { - bool whole_module = module->design->selected_whole_module(module->name); undriven_bits.sort(); for (auto bit : undriven_bits) { - if (whole_module) - log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); + log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); input_bits.insert(bit); } - if (whole_module) - log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); + log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); } if (holes_mode) { diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index a5c4aa26a..843e7b9b4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -130,7 +130,7 @@ RTLIL::SigBit VerificImporter::net_map_at(Net *net) bool is_blackbox(Netlist *nl) { - if (nl->IsBlackBox()) + if (nl->IsBlackBox() || nl->IsEmptyBox()) return true; const char *attr = nl->GetAttValue("blackbox"); @@ -784,15 +784,15 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates) merge_past_ffs_clock(it.second, it.first.first, it.first.second); } -void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo) +void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool norename) { std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name(); std::string module_name = netlist_name; - if (nl->IsOperator()) { + if (nl->IsOperator() || nl->IsPrimitive()) { module_name = "$verific$" + module_name; } else { - if (*nl->Name()) { + if (!norename && *nl->Name()) { module_name += "("; module_name += nl->Name(); module_name += ")"; @@ -1409,7 +1409,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se std::string inst_type = inst->View()->Owner()->Name(); - if (inst->View()->IsOperator()) { + if (inst->View()->IsOperator() || inst->View()->IsPrimitive()) { inst_type = "$verific$" + inst_type; } else { if (*inst->View()->Name()) { @@ -1899,7 +1899,7 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par Netlist *nl = *nl_todo.begin(); if (nl_done.count(nl) == 0) { VerificImporter importer(false, false, false, false, false, false, false); - importer.import_netlist(design, nl, nl_todo); + importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == top); } nl_todo.erase(nl); nl_done.insert(nl); @@ -2373,6 +2373,8 @@ struct VerificPass : public Pass { if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0) cmd_error(args, argidx, "unknown option"); + std::set<std::string> top_mod_names; + if (mode_all) { log("Running hier_tree::ElaborateAll().\n"); @@ -2401,6 +2403,7 @@ struct VerificPass : public Pass { for (; argidx < GetSize(args); argidx++) { const char *name = args[argidx].c_str(); + top_mod_names.insert(name); VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); if (veri_lib) { @@ -2466,7 +2469,7 @@ struct VerificPass : public Pass { if (nl_done.count(nl) == 0) { VerificImporter importer(mode_gates, mode_keep, mode_nosva, mode_names, mode_verific, mode_autocover, mode_fullinit); - importer.import_netlist(design, nl, nl_todo); + importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->Owner()->Name())); } nl_todo.erase(nl); nl_done.insert(nl); diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index 5cbd78f7b..2ccfcd42c 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -93,7 +93,7 @@ struct VerificImporter void merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol); void merge_past_ffs(pool<RTLIL::Cell*> &candidates); - void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo); + void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo, bool norename = false); }; void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 909e9b4f1..49c0c40ac 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -36,6 +36,8 @@ // basic_property: // sequence // not basic_property +// nexttime basic_property +// nexttime[N] basic_property // sequence #-# basic_property // sequence #=# basic_property // basic_property or basic_property (cover only) @@ -1264,6 +1266,26 @@ struct VerificSvaImporter return node; } + if (inst->Type() == PRIM_SVA_NEXTTIME || inst->Type() == PRIM_SVA_S_NEXTTIME) + { + const char *sva_low_s = inst->GetAttValue("sva:low"); + const char *sva_high_s = inst->GetAttValue("sva:high"); + + int sva_low = atoi(sva_low_s); + int sva_high = atoi(sva_high_s); + log_assert(sva_low == sva_high); + + int node = start_node; + + for (int i = 0; i < sva_low; i++) { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + node = next_node; + } + + return parse_sequence(fsm, node, inst->GetInput()); + } + if (inst->Type() == PRIM_SVA_SEQ_CONCAT) { const char *sva_low_s = inst->GetAttValue("sva:low"); @@ -1590,15 +1612,25 @@ struct VerificSvaImporter Instance *consequent_inst = net_to_ast_driver(consequent_net); if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL || - consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH)) + consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH || + consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS)) { bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH; - Net *until_net = consequent_inst->GetInput2(); - consequent_net = consequent_inst->GetInput1(); - consequent_inst = net_to_ast_driver(consequent_net); + Net *until_net = nullptr; + if (consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS) + { + consequent_net = consequent_inst->GetInput(); + consequent_inst = net_to_ast_driver(consequent_net); + } + else + { + until_net = consequent_inst->GetInput2(); + consequent_net = consequent_inst->GetInput1(); + consequent_inst = net_to_ast_driver(consequent_net); + } - SigBit until_sig = parse_expression(until_net); + SigBit until_sig = until_net ? parse_expression(until_net) : RTLIL::S0; SigBit not_until_sig = module->Not(NEW_ID, until_sig); antecedent_fsm.createEdge(node, node, not_until_sig); diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 4acfb414d..c8984c2c4 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -188,9 +188,9 @@ YOSYS_NAMESPACE_END "unique0" { SV_KEYWORD(TOK_UNIQUE); } "priority" { SV_KEYWORD(TOK_PRIORITY); } -"always_comb" { SV_KEYWORD(TOK_ALWAYS); } -"always_ff" { SV_KEYWORD(TOK_ALWAYS); } -"always_latch" { SV_KEYWORD(TOK_ALWAYS); } +"always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } +"always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); } +"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); } /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 77f6d2051..daea3b43a 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -141,6 +141,7 @@ struct specify_rise_fall { %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL +%token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT %token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC %token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT @@ -156,7 +157,7 @@ struct specify_rise_fall { %type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id -%type <boolean> opt_signed opt_property unique_case_attr +%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff %type <al> attr case_attr %type <specify_target_ptr> specify_target @@ -1581,10 +1582,28 @@ cell_port: free_attr($1); }; +always_comb_or_latch: + TOK_ALWAYS_COMB { + $$ = false; + } | + TOK_ALWAYS_LATCH { + $$ = true; + }; + +always_or_always_ff: + TOK_ALWAYS { + $$ = false; + } | + TOK_ALWAYS_FF { + $$ = true; + }; + always_stmt: - attr TOK_ALWAYS { + attr always_or_always_ff { AstNode *node = new AstNode(AST_ALWAYS); append_attr(node, $1); + if ($2) + node->attributes[ID(always_ff)] = AstNode::mkconst_int(1, false); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } always_cond { @@ -1595,6 +1614,22 @@ always_stmt: ast_stack.pop_back(); ast_stack.pop_back(); } | + attr always_comb_or_latch { + AstNode *node = new AstNode(AST_ALWAYS); + append_attr(node, $1); + if ($2) + node->attributes[ID(always_latch)] = AstNode::mkconst_int(1, false); + else + node->attributes[ID(always_comb)] = AstNode::mkconst_int(1, false); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | attr TOK_INITIAL { AstNode *node = new AstNode(AST_INITIAL); append_attr(node, $1); diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index b3f139b72..ea809ec08 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -2,4 +2,5 @@ OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o +OBJS += passes/hierarchy/clkpart.o diff --git a/passes/hierarchy/clkpart.cc b/passes/hierarchy/clkpart.cc new file mode 100644 index 000000000..8f671c175 --- /dev/null +++ b/passes/hierarchy/clkpart.cc @@ -0,0 +1,269 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ClkPartPass : public Pass { + ClkPartPass() : Pass("clkpart", "partition design according to clock domain") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" clkpart [options] [selection]\n"); + log("\n"); + log("Partition the contents of selected modules according to the clock (and optionally\n"); + log("the enable) domains of its $_DFF* cells by extracting them into sub-modules,\n"); + log("using the `submod` command.\n"); + log("Sub-modules created by this command are marked with a 'clkpart' attribute.\n"); + log("\n"); + log(" -unpart\n"); + log(" undo this operation within the selected modules, by flattening those with\n"); + log(" a 'clkpart' attribute into those modules without this attribute.\n"); + log("\n"); + log(" -enable\n"); + log(" also consider enable domains.\n"); + log("\n"); + } + + bool unpart_mode, enable_mode; + + void clear_flags() YS_OVERRIDE + { + unpart_mode = false; + enable_mode = false; + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing CLKPART pass (partition design according to clock domain).\n"); + log_push(); + + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-unpart") { + unpart_mode = true; + continue; + } + if (args[argidx] == "-enable") { + enable_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (unpart_mode) + unpart(design); + else + part(design); + + log_pop(); + } + + void part(RTLIL::Design *design) + { + CellTypes ct(design); + SigMap assign_map; + + for (auto mod : design->selected_modules()) + { + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); + continue; + } + + assign_map.set(mod); + + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + std::map<clkdomain_t, vector<RTLIL::IdString>> assigned_cells; + std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } + } + + if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) + { + key = clkdomain_t(cell->type == ID($_DFF_P_), assign_map(cell->getPort(ID(C))), true, RTLIL::SigSpec()); + } + else + if (cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) + { + bool this_clk_pol = cell->type.in(ID($_DFFE_PN_), ID($_DFFE_PP_)); + bool this_en_pol = !enable_mode || cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)); + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort(ID(C))), this_en_pol, enable_mode ? assign_map(cell->getPort(ID(E))) : RTLIL::SigSpec()); + } + else + continue; + + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); + + assigned_cells[key].push_back(cell->name); + assigned_cells_reverse[cell] = key; + } + + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) + { + RTLIL::Cell *cell = *expand_queue_up.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c->name); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c->name); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); + } + } + + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c->name); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); + } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell->name); + assigned_cells_reverse[cell] = key; + } + + log_header(design, "Summary of detected clock domains:\n"); + for (auto &it : assigned_cells) + log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), + std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + if (assigned_cells.size() > 1) + for (auto &it : assigned_cells) { + RTLIL::Selection sel(false); + sel.selected_members[mod->name] = pool<IdString>(it.second.begin(), it.second.end()); + + RTLIL::IdString submod = stringf("%s.%s", mod->name.c_str(), NEW_ID.c_str()); + Pass::call_on_selection(design, sel, stringf("submod -name %s", submod.c_str())); + + design->module(submod)->set_bool_attribute(ID(clkpart)); + } + } + } + + void unpart(RTLIL::Design *design) + { + vector<Module*> keeped; + for (auto mod : design->selected_modules()) { + if (mod->get_bool_attribute(ID(clkpart))) + continue; + if (mod->get_bool_attribute(ID(keep_hierarchy))) + continue; + keeped.push_back(mod); + mod->set_bool_attribute(ID(keep_hierarchy)); + } + + Pass::call(design, "flatten"); + + for (auto mod : keeped) + mod->set_bool_attribute(ID(keep_hierarchy), false); + + } +} ClkPartPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index ec242aa1f..212932e46 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -20,6 +20,7 @@ #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/log.h" +#include "kernel/sigtools.h" #include <stdlib.h> #include <stdio.h> #include <set> @@ -32,6 +33,8 @@ struct SubmodWorker CellTypes ct; RTLIL::Design *design; RTLIL::Module *module; + SigMap sigmap; + pool<SigBit> outputs; bool copy_mode; std::string opt_name; @@ -44,44 +47,44 @@ struct SubmodWorker std::map<std::string, SubModule> submodules; - struct wire_flags_t { + struct bit_flags_t { RTLIL::Wire *new_wire; bool is_int_driven, is_int_used, is_ext_driven, is_ext_used; - wire_flags_t() : new_wire(NULL), is_int_driven(false), is_int_used(false), is_ext_driven(false), is_ext_used(false) { } + bit_flags_t() : new_wire(NULL), is_int_driven(false), is_int_used(false), is_ext_driven(false), is_ext_used(false) { } }; - std::map<RTLIL::Wire*, wire_flags_t> wire_flags; + std::map<SigBit, bit_flags_t> bit_flags; bool flag_found_something; - void flag_wire(RTLIL::Wire *wire, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) + void flag_bit(RTLIL::SigBit bit, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) { - if (wire_flags.count(wire) == 0) { + if (bit_flags.count(bit) == 0) { if (!create) return; - wire_flags[wire] = wire_flags_t(); + bit_flags[bit] = bit_flags_t(); } if (set_int_driven) - wire_flags[wire].is_int_driven = true; + bit_flags[bit].is_int_driven = true; if (set_int_used) - wire_flags[wire].is_int_used = true; + bit_flags[bit].is_int_used = true; if (set_ext_driven) - wire_flags[wire].is_ext_driven = true; + bit_flags[bit].is_ext_driven = true; if (set_ext_used) - wire_flags[wire].is_ext_used = true; + bit_flags[bit].is_ext_used = true; flag_found_something = true; } void flag_signal(const RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) { - for (auto &c : sig.chunks()) - if (c.wire != NULL) - flag_wire(c.wire, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used); + for (auto &b : sig) + if (b.wire != NULL) + flag_bit(b, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used); } void handle_submodule(SubModule &submod) { log("Creating submodule %s (%s) of module %s.\n", submod.name.c_str(), submod.full_name.c_str(), module->name.c_str()); - wire_flags.clear(); + bit_flags.clear(); for (RTLIL::Cell *cell : submod.cells) { if (ct.cell_known(cell->type)) { for (auto &conn : cell->connections()) @@ -114,18 +117,19 @@ struct SubmodWorker int auto_name_counter = 1; std::set<RTLIL::IdString> all_wire_names; - for (auto &it : wire_flags) { - all_wire_names.insert(it.first->name); + for (auto &it : bit_flags) { + all_wire_names.insert(it.first.wire->name); } - for (auto &it : wire_flags) + for (auto &it : bit_flags) { - RTLIL::Wire *wire = it.first; - wire_flags_t &flags = it.second; + const RTLIL::SigBit &bit = it.first; + RTLIL::Wire *wire = bit.wire; + bit_flags_t &flags = it.second; if (wire->port_input) flags.is_ext_driven = true; - if (wire->port_output) + if (outputs.count(bit)) flags.is_ext_used = true; bool new_wire_port_input = false; @@ -139,7 +143,11 @@ struct SubmodWorker if (flags.is_int_driven && flags.is_ext_driven) new_wire_port_input = true, new_wire_port_output = true; - std::string new_wire_name = wire->name.str(); + RTLIL::IdString new_wire_name; + if (GetSize(wire) == 1) + new_wire_name = wire->name; + else + new_wire_name = stringf("%s[%d]", wire->name.c_str(), bit.offset); if (new_wire_port_input || new_wire_port_output) { while (new_wire_name[0] == '$') { std::string next_wire_name = stringf("\\n%d", auto_name_counter++); @@ -150,10 +158,9 @@ struct SubmodWorker } } - RTLIL::Wire *new_wire = new_mod->addWire(new_wire_name, wire->width); + RTLIL::Wire *new_wire = new_mod->addWire(new_wire_name); new_wire->port_input = new_wire_port_input; new_wire->port_output = new_wire_port_output; - new_wire->start_offset = wire->start_offset; new_wire->attributes = wire->attributes; if (new_wire->port_input && new_wire->port_output) @@ -176,8 +183,8 @@ struct SubmodWorker for (auto &conn : new_cell->connections_) for (auto &bit : conn.second) if (bit.wire != NULL) { - log_assert(wire_flags.count(bit.wire) > 0); - bit.wire = wire_flags[bit.wire].new_wire; + log_assert(bit_flags.count(bit) > 0); + bit = bit_flags[bit].new_wire; } log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str()); if (!copy_mode) @@ -187,18 +194,18 @@ struct SubmodWorker if (!copy_mode) { RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); - for (auto &it : wire_flags) + for (auto &it : bit_flags) { - RTLIL::Wire *old_wire = it.first; + RTLIL::SigBit old_bit = it.first; RTLIL::Wire *new_wire = it.second.new_wire; if (new_wire->port_id > 0) - new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); + new_cell->setPort(new_wire->name, old_bit); } } } SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, std::string opt_name = std::string()) : - design(design), module(module), copy_mode(copy_mode), opt_name(opt_name) + design(design), module(module), sigmap(module), copy_mode(copy_mode), opt_name(opt_name) { if (!design->selected_whole_module(module->name) && opt_name.empty()) return; @@ -219,6 +226,15 @@ struct SubmodWorker ct.setup_stdcells_mem(); ct.setup_design(design); + for (auto port : module->ports) { + auto wire = module->wire(port); + if (!wire->port_output) + continue; + for (auto b : sigmap(wire)) + if (b.wire) + outputs.insert(b); + } + if (opt_name.empty()) { for (auto &it : module->wires_) diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 712bc2537..cee63bdd8 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -35,6 +35,7 @@ struct MemoryPass : public Pass { log("\n"); log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); + log(" opt_mem\n"); log(" memory_dff [-nordff] (-memx implies -nordff)\n"); log(" opt_clean\n"); log(" memory_share\n"); @@ -81,6 +82,7 @@ struct MemoryPass : public Pass { } extra_args(args, argidx, design); + Pass::call(design, "opt_mem"); Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff"); Pass::call(design, "opt_clean"); Pass::call(design, "memory_share"); diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index eb07e9452..002c1a6a1 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,6 +1,7 @@ OBJS += passes/opt/opt.o OBJS += passes/opt/opt_merge.o +OBJS += passes/opt/opt_mem.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc new file mode 100644 index 000000000..98d3551eb --- /dev/null +++ b/passes/opt/opt_mem.cc @@ -0,0 +1,143 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptMemWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap; + bool restart; + + dict<IdString, vector<IdString>> memrd, memwr, meminit; + pool<IdString> remove_mem, remove_cells; + + OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false) + { + for (auto &it : module->memories) + { + memrd[it.first]; + memwr[it.first]; + meminit[it.first]; + } + + for (auto cell : module->cells()) + { + if (cell->type == ID($memrd)) { + IdString id = cell->getParam(ID(MEMID)).decode_string(); + memrd.at(id).push_back(cell->name); + } + + if (cell->type == ID($memwr)) { + IdString id = cell->getParam(ID(MEMID)).decode_string(); + memwr.at(id).push_back(cell->name); + } + + if (cell->type == ID($meminit)) { + IdString id = cell->getParam(ID(MEMID)).decode_string(); + meminit.at(id).push_back(cell->name); + } + } + } + + ~OptMemWorker() + { + for (auto it : remove_mem) + { + for (auto cell_name : memrd[it]) + module->remove(module->cell(cell_name)); + for (auto cell_name : memwr[it]) + module->remove(module->cell(cell_name)); + for (auto cell_name : meminit[it]) + module->remove(module->cell(cell_name)); + + delete module->memories.at(it); + module->memories.erase(it); + } + + for (auto cell_name : remove_cells) + module->remove(module->cell(cell_name)); + } + + int run(RTLIL::Memory *mem) + { + if (restart || remove_mem.count(mem->name)) + return 0; + + if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) { + log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem)); + remove_mem.insert(mem->name); + return 1; + } + + return 0; + } +}; + +struct OptMemPass : public Pass { + OptMemPass() : Pass("opt_mem", "optimize memories") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem [options] [selection]\n"); + log("\n"); + log("This pass performs various optimizations on memories in the design.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_MEM pass (optimize memories).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-nomux") { + // mode_nomux = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + int total_count = 0; + for (auto module : design->selected_modules()) { + while (1) { + int cnt = 0; + OptMemWorker worker(module); + for (auto &it : module->memories) + if (module->selected(it.second)) + cnt += worker.run(it.second); + if (!cnt && !worker.restart) + break; + total_count += cnt; + } + } + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Performed a total of %d transformations.\n", total_count); + } +} OptMemPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index d9d5dfbed..a0c8351b6 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -349,6 +349,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) continue; } + if (proc->get_bool_attribute(ID(always_ff))) + log_error("Found non edge/level sensitive event in always_ff process `%s.%s'.\n", + db.module->name.c_str(), proc->name.c_str()); + for (auto ss : sr->actions) { db.sigmap.apply(ss.first); @@ -383,8 +387,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) int offset = 0; for (auto chunk : nolatches_bits.first.chunks()) { SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width); - log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", - db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + if (proc->get_bool_attribute(ID(always_latch))) + log_error("No latch inferred for signal `%s.%s' from always_latch process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + else + log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); db.module->connect(lhs, rhs); offset += chunk.width; } @@ -410,8 +418,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) cell->set_src_attribute(src); db.generated_dlatches.insert(cell); - log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", - db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); + if (proc->get_bool_attribute(ID(always_comb))) + log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + else + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); } offset += width; diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 8d4ff4025..4b6ec6e11 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -1165,7 +1165,7 @@ struct Abc9Pass : public Pass { for (auto bit : cell_to_bit_up[cell]) for (auto c : bit_to_cell_up[bit]) - if (unassigned_cells.count(c)) { + if (unassigned_cells.count(c) && !c->type.in("$__ABC9_FF_", "$__ABC9_ASYNC_")) { unassigned_cells.erase(c); next_expand_queue_up.insert(c); assigned_cells[key].insert(c->name); diff --git a/techlibs/gowin/.gitignore b/techlibs/gowin/.gitignore new file mode 100644 index 000000000..d6c48e90d --- /dev/null +++ b/techlibs/gowin/.gitignore @@ -0,0 +1,2 @@ +brams_init.mk +bram_init_*.vh diff --git a/tests/arch/gowin/mux.ys b/tests/arch/gowin/mux.ys index 4990be421..afad29a89 100644 --- a/tests/arch/gowin/mux.ys +++ b/tests/arch/gowin/mux.ys @@ -45,6 +45,5 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p cd mux16 # Constrain all select calls below inside the top module select -assert-count 20 t:IBUF select -assert-count 1 t:OBUF -show select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v index 65eb01338..99075d319 100644 --- a/tests/simple_abc9/abc9.v +++ b/tests/simple_abc9/abc9.v @@ -268,7 +268,7 @@ assign o = { 1'b1, 1'bx }; assign p = { 1'b1, 1'bx, 1'b0 }; endmodule -module abc9_test029(input clk1, clk2, input d, output reg q1, q2); +module abc9_test029(input clk1, clk2, d, output reg q1, q2); always @(posedge clk1) q1 <= d; always @(negedge clk2) q2 <= q1; endmodule @@ -284,3 +284,13 @@ always @(negedge clk or posedge r) if (r) q <= 1'b1; else q <= d; endmodule + +module abc9_test033(input clk, d, output reg q1, q2); +always @(posedge clk) q1 <= d; +always @(posedge clk) q2 <= q1; +endmodule + +module abc9_test034(input clk, d, output reg [1:0] q); +always @(posedge clk) q[0] <= d; +always @(negedge clk) q[1] <= q[0]; +endmodule diff --git a/tests/various/submod.ys b/tests/various/submod.ys new file mode 100644 index 000000000..7c6f555ac --- /dev/null +++ b/tests/various/submod.ys @@ -0,0 +1,50 @@ +read_verilog <<EOT +module top(input a, output [1:0] b); +wire c; +(* submod="bar" *) sub s1(a, c); +assign b[0] = c; +endmodule + +module sub(input a, output c); +assign c = a; +endmodule +EOT + +hierarchy -top top +proc +design -save gold + +submod +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + + +design -reset +read_verilog <<EOT +module top(input a, output [1:0] b); +(* submod="bar" *) sub s1(a, b[1]); +assign b[0] = 1'b0; +endmodule + +module sub(input a, output c); +assign c = a; +endmodule +EOT + +hierarchy -top top +proc +design -save gold + +submod +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter diff --git a/tests/various/svalways.sh b/tests/various/svalways.sh new file mode 100755 index 000000000..2cc09f801 --- /dev/null +++ b/tests/various/svalways.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +trap 'echo "ERROR in svalways.sh" >&2; exit 1' ERR + +# Good case +../../yosys -f "verilog -sv" -qp proc - <<EOT +module top(input clk, en, d, output reg p, q, r); + +always_ff @(posedge clk) + p <= d; + +always_comb + q = ~d; + +always_latch + if (en) r = d; + +endmodule +EOT + +# Incorrect always_comb syntax +((../../yosys -f "verilog -sv" -qp proc -|| true) <<EOT +module top(input d, output reg q); + +always_comb @(d) + q = ~d; + +endmodule +EOT +) 2>&1 | grep -F "<stdin>:3: ERROR: syntax error, unexpected '@'" > /dev/null + +# Incorrect use of always_comb +((../../yosys -f "verilog -sv" -qp proc -|| true) <<EOT +module top(input en, d, output reg q); + +always_comb + if (en) q = d; + +endmodule +EOT +) 2>&1 | grep -F "ERROR: Latch inferred for signal \`\\top.\\q' from always_comb process" > /dev/null + +# Incorrect use of always_latch +((../../yosys -f "verilog -sv" -qp proc -|| true) <<EOT +module top(input en, d, output reg q); + +always_latch + q = !d; + +endmodule +EOT +) 2>&1 | grep -F "ERROR: No latch inferred for signal \`\\top.\\q' from always_latch process" > /dev/null + +# Incorrect use of always_ff +((../../yosys -f "verilog -sv" -qp proc -|| true) <<EOT +module top(input en, d, output reg q); + +always_ff @(*) + q = !d; + +endmodule +EOT +) 2>&1 | grep -F "ERROR: Found non edge/level sensitive event in always_ff process" > /dev/null |