From f6ff311a1dc9876911594328350e2d3fc62a5535 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 20 Nov 2019 12:54:10 +0100 Subject: [PATCH 01/14] Do not rename VHDL entities to "entity(impl)" when they are top modules Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 11 +++++++---- frontends/verific/verific.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index a5c4aa26a..c2086afa4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -784,7 +784,7 @@ void VerificImporter::merge_past_ffs(pool &candidates) merge_past_ffs_clock(it.second, it.first.first, it.first.second); } -void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set &nl_todo) +void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set &nl_todo, bool norename) { std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name(); std::string module_name = netlist_name; @@ -792,7 +792,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (nl->IsOperator()) { module_name = "$verific$" + module_name; } else { - if (*nl->Name()) { + if (!norename && *nl->Name()) { module_name += "("; module_name += nl->Name(); module_name += ")"; @@ -1899,7 +1899,7 @@ void verific_import(Design *design, const std::map &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 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 &candidates, SigBit clock, bool clock_pol); void merge_past_ffs(pool &candidates); - void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set &nl_todo); + void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set &nl_todo, bool norename = false); }; void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst); From 55bda2b2c693a7ff79da545e7b52901de00df475 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 20 Nov 2019 12:56:31 +0100 Subject: [PATCH 02/14] Correctly treat empty modules as blackboxes in Verific Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index c2086afa4..de41e1a5c 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"); From 9e4801cca7bf346ac4d2ca0feaacda1e4bb27ac2 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 21 Nov 2019 20:27:19 +0000 Subject: [PATCH 03/14] sv: Correct parsing of always_comb, always_ff and always_latch Signed-off-by: David Shah --- frontends/verilog/verilog_lexer.l | 6 ++--- frontends/verilog/verilog_parser.y | 39 ++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) 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 range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id -%type opt_signed opt_property unique_case_attr +%type opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff %type attr case_attr %type 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); From ca99b1ee8dca6d49d79576e19d35111b4ad5ea45 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 21 Nov 2019 20:46:41 +0000 Subject: [PATCH 04/14] proc_dlatch: Add error handling for incorrect always_(ff|latch|comb) usage Signed-off-by: David Shah --- passes/proc/proc_dlatch.cc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) 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; From 49b670ca38988bcce453125166528b32e16f7bb4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 21 Nov 2019 21:06:28 +0000 Subject: [PATCH 05/14] sv: Add tests for SV always types Signed-off-by: David Shah --- tests/various/svalways.sh | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100755 tests/various/svalways.sh 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 - <&1 | grep -F ":3: ERROR: syntax error, unexpected '@'" > /dev/null + +# Incorrect use of always_comb +((../../yosys -f "verilog -sv" -qp proc -|| true) <&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) <&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) <&1 | grep -F "ERROR: Found non edge/level sensitive event in always_ff process" > /dev/null From b60f32c6ecc27e0fa1f81a1055cfd1105ed647bd Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 12:46:19 +0000 Subject: [PATCH 06/14] Update CHANGELOG and README Signed-off-by: David Shah --- CHANGELOG | 2 ++ README.md | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 1fc139d49..a49c27b05 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,8 @@ 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) Yosys 0.8 .. Yosys 0.9 ---------------------- diff --git a/README.md b/README.md index db7810cb4..e46971526 100644 --- a/README.md +++ b/README.md @@ -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 From 1d098b719513ef2fa701b88b9b56c6e989384c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= Date: Fri, 22 Nov 2019 12:10:57 +0100 Subject: [PATCH 07/14] gowin: Add missing .gitignore entries --- techlibs/gowin/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 techlibs/gowin/.gitignore 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 From e110df9c484d5c87429c55da1c1d83fd509a78b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= Date: Fri, 22 Nov 2019 12:15:33 +0100 Subject: [PATCH 08/14] gowin: Remove show command from tests. --- tests/arch/gowin/mux.ys | 1 - 1 file changed, 1 deletion(-) 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 From 6af0d03faede4a8d87292a76ae70dd1362dfcbb3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 22 Nov 2019 15:52:21 +0100 Subject: [PATCH 09/14] Add Verific SVA support for "always" properties Signed-off-by: Clifford Wolf --- frontends/verific/verificsva.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 909e9b4f1..225fd3e4a 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1590,15 +1590,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); From e93e4a7a2c6875e87b7e2635470cf02aa45af23a Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 22 Nov 2019 16:00:07 +0100 Subject: [PATCH 10/14] Improve handling of verific primitives in "verific -import -V" mode Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index de41e1a5c..843e7b9b4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -789,7 +789,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se 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 (!norename && *nl->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()) { From db323685a4357ae0a04a8def9de29ef3a8ba16c2 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 22 Nov 2019 16:11:56 +0100 Subject: [PATCH 11/14] Add Verific support for SVA nexttime properties Signed-off-by: Clifford Wolf --- frontends/verific/verificsva.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 225fd3e4a..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"); From 03fb92ed6f1821386e5572f35b70c09cacc7b66b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 22 Nov 2019 16:58:49 +0100 Subject: [PATCH 12/14] Add "opt_mem" pass Signed-off-by: Clifford Wolf --- passes/memory/memory.cc | 2 + passes/opt/Makefile.inc | 1 + passes/opt/opt_mem.cc | 143 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 passes/opt/opt_mem.cc 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 + * + * 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> memrd, memwr, meminit; + pool 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 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 From 856a3dc98dcebdfdd331a5394a4556c00b4fcb8c Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 22 Nov 2019 15:33:51 -0800 Subject: [PATCH 13/14] New 'clkpart' to {,un}partition design according to clock/enable --- passes/techmap/clkpart.cc | 268 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 passes/techmap/clkpart.cc diff --git a/passes/techmap/clkpart.cc b/passes/techmap/clkpart.cc new file mode 100644 index 000000000..4fa729250 --- /dev/null +++ b/passes/techmap/clkpart.cc @@ -0,0 +1,268 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * 2019 Eddie Hung + * + * 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 args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing CLKPART pass (TODO).\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 all_cells = mod->selected_cells(); + std::set unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set expand_queue, next_expand_queue; + std::set expand_queue_up, next_expand_queue_up; + std::set expand_queue_down, next_expand_queue_down; + + typedef tuple clkdomain_t; + std::map> assigned_cells; + std::map assigned_cells_reverse; + + std::map> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map> 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))); + + for (auto &it : assigned_cells) { + RTLIL::Selection sel(false); + sel.selected_members[mod->name] = pool(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 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 From 450ad0e9ba031fbeef904746ca773e3b0e21af8f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 22 Nov 2019 15:35:08 -0800 Subject: [PATCH 14/14] Add to CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index a49c27b05..d9d261fbc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -53,6 +53,7 @@ Yosys 0.9 .. Yosys 0.9-dev - 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 ----------------------