diff options
38 files changed, 714 insertions, 98 deletions
@@ -16,6 +16,7 @@ Yosys 0.8 .. Yosys 0.8-dev - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass + - "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx" Yosys 0.7 .. Yosys 0.8 @@ -312,6 +312,10 @@ Verilog Attributes and non-standard features passes to identify input and output ports of cells. The Verilog backend also does not output blackbox modules on default. +- The ``whitebox`` attribute on modules triggers the same behavior as + ``blackbox``, but is for whitebox modules, i.e. library modules that + contain a behavioral model of the cell type. + - The ``dynports`` attribute is used by the Verilog front-end to mark modules that have ports with a width that depends on a parameter. diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 0db5ff27c..b6dbd84cb 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -140,7 +140,7 @@ struct BlifDumper return "subckt"; if (!design->modules_.count(RTLIL::escape_id(cell_type))) return "gate"; - if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox")) + if (design->modules_.at(RTLIL::escape_id(cell_type))->get_blackbox_attribute()) return "gate"; return "subckt"; } @@ -196,7 +196,7 @@ struct BlifDumper } f << stringf("\n"); - if (module->get_bool_attribute("\\blackbox")) { + if (module->get_blackbox_attribute()) { f << stringf(".blackbox\n"); f << stringf(".end\n"); return; @@ -640,7 +640,7 @@ struct BlifBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode) + if (module->get_blackbox_attribute() && !config.blackbox_mode) continue; if (module->processes.size() != 0) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 7e30b67af..6d9469538 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -178,7 +178,7 @@ struct EdifBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (top_module_name.empty()) @@ -192,7 +192,7 @@ struct EdifBackend : public Backend { for (auto cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { + if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) lib_cell_ports[cell->type][p.first] = GetSize(p.second); @@ -302,7 +302,7 @@ struct EdifBackend : public Backend { *f << stringf(" (technology (numberDefinition))\n"); for (auto module : sorted_modules) { - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index 2eb08dbe9..b0e3cd252 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -127,7 +127,7 @@ struct IntersynthBackend : public Backend { RTLIL::Module *module = module_it.second; SigMap sigmap(module); - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0) continue; diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 688535f33..e318a4051 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -1543,7 +1543,7 @@ struct Smt2Backend : public Backend { for (auto module : sorted_modules) { - if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn()) + if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn()) continue; log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index f379c9c48..d75456c1b 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -739,7 +739,7 @@ struct SmvBackend : public Backend { pool<Module*> modules; for (auto module : design->modules()) - if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn()) + if (!module->get_blackbox_attribute() && !module->has_memories_warn() && !module->has_processes_warn()) modules.insert(module); if (template_f.is_open()) diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index b6a3f1e77..6738a4bbd 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -212,7 +212,7 @@ struct SpiceBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (module->processes.size() != 0) diff --git a/backends/table/table.cc b/backends/table/table.cc index b75169ea4..796f18059 100644 --- a/backends/table/table.cc +++ b/backends/table/table.cc @@ -67,7 +67,7 @@ struct TableBackend : public Backend { for (auto module : design->modules()) { - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 83d83f488..855409d0b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1770,7 +1770,7 @@ struct VerilogBackend : public Backend { *f << stringf("/* Generated by %s */\n", yosys_version_str); for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) { - if (it->second->get_bool_attribute("\\blackbox") != blackboxes) + if (it->second->get_blackbox_attribute() != blackboxes) continue; if (selected && !design->selected_whole_module(it->first)) { if (design->selected_module(it->first)) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index d48996167..720b3f3d1 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -46,7 +46,7 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_wb, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; @@ -956,7 +956,18 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast log("--- END OF AST DUMP ---\n"); } + if (flag_wb) { + if (!ast->attributes.count("\\whitebox")) + goto blackbox_module; + AstNode *n = ast->attributes.at("\\whitebox"); + if (n->type != AST_CONSTANT) + log_file_error(ast->filename, ast->linenum, "Whitebox attribute with non-constant value!\n"); + if (!n->asBool()) + goto blackbox_module; + } + if (flag_lib) { + blackbox_module: std::vector<AstNode*> new_children; for (auto child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) { @@ -970,6 +981,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast } } ast->children.swap(new_children); + if (ast->attributes.count("\\whitebox")) { + delete ast->attributes.at("\\whitebox"); + ast->attributes.erase("\\whitebox"); + } ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); } @@ -1010,6 +1025,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module->nomem2reg = flag_nomem2reg; current_module->mem2reg = flag_mem2reg; current_module->lib = flag_lib; + current_module->wb = flag_wb; current_module->noopt = flag_noopt; current_module->icells = flag_icells; current_module->autowire = flag_autowire; @@ -1026,7 +1042,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast // create AstModule instances for all modules in the AST tree and add them to 'design' void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, - bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) + bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool wb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; @@ -1040,6 +1056,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; + flag_wb = wb; flag_noopt = noopt; flag_icells = icells; flag_autowire = autowire; @@ -1374,6 +1391,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; + flag_wb = wb; flag_noopt = noopt; flag_icells = icells; flag_autowire = autowire; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index ddd59d4be..610e00fbf 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -283,13 +283,13 @@ namespace AST // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, - bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); + bool nomem2reg, bool mem2reg, bool lib, bool wb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, lib, wb, noopt, icells, autowire; ~AstModule() YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 504f8b3f3..4e2c5abb5 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -148,6 +148,10 @@ struct VerilogFrontend : public Frontend { log(" -lib\n"); log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); log("\n"); + log(" -wb\n"); + log(" like -lib, except do not touch modules with the whitebox\n"); + log(" attribute set. This also implies -DBLACKBOX.\n"); + log("\n"); log(" -noopt\n"); log(" don't perform basic optimizations (such as const folding) in the\n"); log(" high-level front-end.\n"); @@ -228,6 +232,7 @@ struct VerilogFrontend : public Frontend { norestrict_mode = false; assume_asserts_mode = false; lib_mode = false; + wb_mode = false; default_nettype_wire = true; log_header(design, "Executing Verilog-2005 frontend.\n"); @@ -329,11 +334,16 @@ struct VerilogFrontend : public Frontend { flag_nodpi = true; continue; } - if (arg == "-lib") { + if (arg == "-lib" && !wb_mode) { lib_mode = true; defines_map["BLACKBOX"] = string(); continue; } + if (arg == "-wb" && !lib_mode) { + wb_mode = true; + defines_map["BLACKBOX"] = string(); + continue; + } if (arg == "-noopt") { flag_noopt = true; continue; @@ -429,7 +439,7 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, wb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 523bbc897..b5cf70c57 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -72,6 +72,9 @@ namespace VERILOG_FRONTEND // running in -lib mode extern bool lib_mode; + // running in -wb mode + extern bool wb_mode; + // lexer input stream extern std::istream *lexin; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 52685f637..122eb1230 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND { std::vector<char> case_type_stack; bool do_not_require_port_stubs; bool default_nettype_wire; - bool sv_mode, formal_mode, lib_mode; + bool sv_mode, formal_mode, lib_mode, wb_mode; bool noassert_mode, noassume_mode, norestrict_mode; bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; @@ -1906,7 +1906,7 @@ basic_expr: if ($4->substr(0, 1) != "'") frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. (<expr>)<constval> , while %s is not a sized constant.", $4->c_str()); AstNode *bits = $2; - AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); if (val == NULL) log_error("Value conversion failed: `%s'\n", $4->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1917,7 +1917,7 @@ basic_expr: frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. <ID>\'d0, while %s is not a sized constant.", $2->c_str()); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; - AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); if (val == NULL) log_error("Value conversion failed: `%s'\n", $2->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1925,14 +1925,14 @@ basic_expr: delete $2; } | TOK_CONSTVAL TOK_CONSTVAL { - $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); if ($$ == NULL || (*$2)[0] != '\'') log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); delete $1; delete $2; } | TOK_CONSTVAL { - $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 9ae20a317..f6f08bb9e 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -207,9 +207,12 @@ bool RTLIL::Const::is_fully_undef() const return true; } -void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id) +void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) { - attributes[id] = RTLIL::Const(1); + if (value) + attributes[id] = RTLIL::Const(1); + else if (attributes.count(id)) + attributes.erase(id); } bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const @@ -589,7 +592,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules() const std::vector<RTLIL::Module*> result; result.reserve(modules_.size()); for (auto &it : modules_) - if (selected_module(it.first) && !it.second->get_bool_attribute("\\blackbox")) + if (selected_module(it.first) && !it.second->get_blackbox_attribute()) result.push_back(it.second); return result; } @@ -599,7 +602,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const std::vector<RTLIL::Module*> result; result.reserve(modules_.size()); for (auto &it : modules_) - if (selected_whole_module(it.first) && !it.second->get_bool_attribute("\\blackbox")) + if (selected_whole_module(it.first) && !it.second->get_blackbox_attribute()) result.push_back(it.second); return result; } @@ -609,7 +612,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const std::vector<RTLIL::Module*> result; result.reserve(modules_.size()); for (auto &it : modules_) - if (it.second->get_bool_attribute("\\blackbox")) + if (it.second->get_blackbox_attribute()) continue; else if (selected_whole_module(it.first)) result.push_back(it.second); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index fb045bc72..330a81c3b 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -566,9 +566,13 @@ struct RTLIL::AttrObject { dict<RTLIL::IdString, RTLIL::Const> attributes; - void set_bool_attribute(RTLIL::IdString id); + void set_bool_attribute(RTLIL::IdString id, bool value=true); bool get_bool_attribute(RTLIL::IdString id) const; + bool get_blackbox_attribute(bool ignore_wb=false) const { + return get_bool_attribute("\\blackbox") || (!ignore_wb && get_bool_attribute("\\whitebox")); + } + void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data); void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data); pool<string> get_strpool_attribute(RTLIL::IdString id) const; diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index cfccca966..af6f7043d 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -71,7 +71,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n RTLIL::Module *mod = design->modules_.at(it.second->type); if (!design->selected_whole_module(mod->name)) continue; - if (mod->get_bool_attribute("\\blackbox")) + if (mod->get_blackbox_attribute()) continue; if (it.second->hasPort(name)) continue; diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 606276e64..4b22f6d2d 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -128,7 +128,7 @@ struct BugpointPass : public Pass { { for (auto &it : design_copy->modules_) { - if (it.second->get_bool_attribute("\\blackbox")) + if (it.second->get_blackbox_attribute()) continue; if (index++ == seed) @@ -143,7 +143,7 @@ struct BugpointPass : public Pass { { for (auto mod : design_copy->modules()) { - if (mod->get_bool_attribute("\\blackbox")) + if (mod->get_blackbox_attribute()) continue; for (auto wire : mod->wires()) @@ -168,7 +168,7 @@ struct BugpointPass : public Pass { { for (auto mod : design_copy->modules()) { - if (mod->get_bool_attribute("\\blackbox")) + if (mod->get_blackbox_attribute()) continue; for (auto &it : mod->cells_) @@ -186,7 +186,7 @@ struct BugpointPass : public Pass { { for (auto mod : design_copy->modules()) { - if (mod->get_bool_attribute("\\blackbox")) + if (mod->get_blackbox_attribute()) continue; for (auto cell : mod->cells()) diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index d38a6b3da..b9fcc3e7a 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -128,6 +128,45 @@ struct SetattrPass : public Pass { } } SetattrPass; +struct WbflipPass : public Pass { + WbflipPass() : Pass("wbflip", "flip the whitebox attribute") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" wbflip [selection]\n"); + log("\n"); + log("Flip the whitebox attribute on selected cells. I.e. if it's set, unset it, and\n"); + log("vice-versa. Blackbox cells are not effected by this command.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + // if (arg == "-mod") { + // flag_mod = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (Module *module : design->modules()) + { + if (!design->selected(module)) + continue; + + if (module->get_bool_attribute("\\blackbox")) + continue; + + module->set_bool_attribute("\\whitebox", !module->get_bool_attribute("\\whitebox")); + } + } +} WbflipPass; + struct SetparamPass : public Pass { SetparamPass() : Pass("setparam", "set/unset parameters on objects") { } void help() YS_OVERRIDE diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 0eadd904a..cf729215f 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -574,7 +574,7 @@ struct ShowWorker if (!design->selected_module(module->name)) continue; if (design->selected_whole_module(module->name)) { - if (module->get_bool_attribute("\\blackbox")) { + if (module->get_blackbox_attribute()) { // log("Skipping blackbox module %s.\n", id2cstr(module->name)); continue; } else @@ -790,7 +790,7 @@ struct ShowPass : public Pass { if (format != "ps" && format != "dot") { int modcount = 0; for (auto &mod_it : design->modules_) { - if (mod_it.second->get_bool_attribute("\\blackbox")) + if (mod_it.second->get_blackbox_attribute()) continue; if (mod_it.second->cells_.empty() && mod_it.second->connections().empty()) continue; diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc index 86550a69b..e5dda9c24 100644 --- a/passes/equiv/equiv_opt.cc +++ b/passes/equiv/equiv_opt.cc @@ -134,7 +134,7 @@ struct EquivOptPass:public ScriptPass opts = " -map <filename> ..."; else opts = techmap_opts; - run("techmap -D EQUIV -autoproc" + opts); + run("techmap -wb -D EQUIV -autoproc" + opts); } if (check_label("prove")) { diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 88c339e8c..b8ff99884 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -346,9 +346,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } RTLIL::Module *mod = design->modules_[cell->type]; - if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { + if (design->modules_.at(cell->type)->get_blackbox_attribute()) { if (flag_simcheck) - log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox module.\n", + log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n", cell->type.c_str(), module->name.c_str(), cell->name.c_str()); continue; } @@ -451,7 +451,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString:: if (indent == 0) log("Top module: %s\n", mod->name.c_str()); - else if (!mod->get_bool_attribute("\\blackbox")) + else if (!mod->get_blackbox_attribute()) log("Used module: %*s%s\n", indent, "", mod->name.c_str()); used.insert(mod); @@ -491,7 +491,7 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) int del_counter = 0; for (auto mod : del_modules) { - if (!purge_lib && mod->get_bool_attribute("\\blackbox")) + if (!purge_lib && mod->get_blackbox_attribute()) continue; log("Removing unused module `%s'.\n", mod->name.c_str()); design->modules_.erase(mod->name); @@ -910,7 +910,7 @@ struct HierarchyPass : public Pass { if (m == nullptr) continue; - if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { + if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { IdString new_m_name = m->derive(design, cell->parameters, true); if (new_m_name.empty()) continue; diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc index e6154e94f..ad3220918 100644 --- a/passes/hierarchy/uniquify.cc +++ b/passes/hierarchy/uniquify.cc @@ -75,7 +75,7 @@ struct UniquifyPass : public Pass { if (tmod == nullptr) continue; - if (tmod->get_bool_attribute("\\blackbox")) + if (tmod->get_blackbox_attribute()) continue; if (tmod->get_bool_attribute("\\unique") && newname == tmod->name) diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 1329c1fef..aac0b121c 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -108,6 +108,7 @@ struct SigSnippets struct SnippetSwCache { + dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache; dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; const SigSnippets *snippets; int current_snippet; @@ -268,6 +269,49 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } +const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw) +{ + if (!swcache.full_case_bits_cache.count(sw)) + { + pool<SigBit> bits; + + if (sw->get_bool_attribute("\\full_case")) + { + bool first_case = true; + + for (auto cs : sw->cases) + { + pool<SigBit> case_bits; + + for (auto it : cs->actions) { + for (auto bit : it.first) + case_bits.insert(bit); + } + + for (auto it : cs->switches) { + for (auto bit : get_full_case_bits(swcache, it)) + case_bits.insert(bit); + } + + if (first_case) { + first_case = false; + bits = case_bits; + } else { + pool<SigBit> new_bits; + for (auto bit : bits) + if (case_bits.count(bit)) + new_bits.insert(bit); + bits.swap(new_bits); + } + } + } + + bits.swap(swcache.full_case_bits_cache[sw]); + } + + return swcache.full_case_bits_cache.at(sw); +} + RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { @@ -337,6 +381,12 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d } } + // mask default bits that are irrelevant because the output is driven by a full case + const pool<SigBit> &full_case_bits = get_full_case_bits(swcache, sw); + for (int i = 0; i < GetSize(sig); i++) + if (full_case_bits.count(sig[i])) + result[i] = State::Sx; + // evaluate in reverse order to give the first entry the top priority RTLIL::SigSpec initial_val = result; RTLIL::Cell *last_mux_cell = NULL; diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 7c334e661..4f40be446 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -28,7 +28,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) +void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) { BitPatternPool pool(sw->signal); @@ -56,11 +56,16 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) } for (auto switch_it : sw->cases[i]->switches) - proc_rmdead(switch_it, counter); + proc_rmdead(switch_it, counter, full_case_counter); if (is_default) pool.take_all(); } + + if (pool.empty() && !sw->get_bool_attribute("\\full_case")) { + sw->set_bool_attribute("\\full_case"); + full_case_counter++; + } } struct ProcRmdeadPass : public Pass { @@ -87,12 +92,15 @@ struct ProcRmdeadPass : public Pass { for (auto &proc_it : mod->processes) { if (!design->selected(mod, proc_it.second)) continue; - int counter = 0; + int counter = 0, full_case_counter = 0; for (auto switch_it : proc_it.second->root_case.switches) - proc_rmdead(switch_it, counter); + proc_rmdead(switch_it, counter, full_case_counter); if (counter > 0) log("Removed %d dead cases from process %s in module %s.\n", counter, - proc_it.first.c_str(), log_id(mod)); + log_id(proc_it.first), log_id(mod)); + if (full_case_counter > 0) + log("Marked %d switch rules as full_case in process %s in module %s.\n", + full_case_counter, log_id(proc_it.first), log_id(mod)); total_counter += counter; } } diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index d37f1b126..1a886af70 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -254,7 +254,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (flag_flatten) { log_push(); - Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;"); + Pass::call_on_module(design, miter_module, "flatten -wb; opt_expr -keepdc -undriven;;"); log_pop(); } } @@ -308,7 +308,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL if (flag_flatten) { log_push(); - Pass::call_on_module(design, module, "flatten;;"); + Pass::call_on_module(design, module, "flatten -wb;;"); log_pop(); } @@ -385,7 +385,7 @@ struct MiterPass : public Pass { log(" also create an 'assert' cell that checks if trigger is always low.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); log("\n"); log(" miter -assert [options] module [miter_name]\n"); @@ -399,7 +399,7 @@ struct MiterPass : public Pass { log(" keep module output ports.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cf9e198ad..81df499da 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -37,6 +37,7 @@ OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o OBJS += passes/techmap/dff2dffs.o OBJS += passes/techmap/flowmap.o +OBJS += passes/techmap/pmux2shiftx.o endif GENFILES += passes/techmap/techmap.inc diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 274177a68..b5c0498d0 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -664,7 +664,7 @@ struct DfflibmapPass : public Pass { logmap_all(); for (auto &it : design->modules_) - if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox")) + if (design->selected(it.second) && !it.second->get_blackbox_attribute()) dfflibmap(design, it.second, prepare_mode); cell_mappings.clear(); diff --git a/passes/techmap/pmux2shiftx.cc b/passes/techmap/pmux2shiftx.cc new file mode 100644 index 000000000..6ffc27a4c --- /dev/null +++ b/passes/techmap/pmux2shiftx.cc @@ -0,0 +1,81 @@ +/* + * 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 Pmux2ShiftxPass : public Pass { + Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" pmux2shiftx [selection]\n"); + log("\n"); + log("This pass transforms $pmux cells to $shiftx cells.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing PMUX2SHIFTX pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + { + if (cell->type != "$pmux") + continue; + + // Create a new encoder, out of a $pmux, that takes + // the existing pmux's 'S' input and transforms it + // back into a binary value + RTLIL::SigSpec shiftx_a; + RTLIL::SigSpec pmux_s; + + int s_width = cell->getParam("\\S_WIDTH").as_int(); + if (!cell->getPort("\\A").is_fully_undef()) { + ++s_width; + shiftx_a.append(cell->getPort("\\A")); + pmux_s.append(module->Not(NEW_ID, module->ReduceOr(NEW_ID, cell->getPort("\\S")))); + } + const int clog2width = ceil(log2(s_width)); + + RTLIL::SigSpec pmux_b; + for (int i = s_width-1; i >= 0; i--) + pmux_b.append(RTLIL::Const(i, clog2width)); + shiftx_a.append(cell->getPort("\\B")); + pmux_s.append(cell->getPort("\\S")); + + RTLIL::SigSpec pmux_y = module->addWire(NEW_ID, clog2width); + module->addPmux(NEW_ID, RTLIL::Const(RTLIL::Sx, clog2width), pmux_b, pmux_s, pmux_y); + module->addShiftx(NEW_ID, shiftx_a, pmux_y, cell->getPort("\\Y")); + module->remove(cell); + } + } +} Pmux2ShiftxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index f20863ba0..ec43b5654 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -26,7 +26,9 @@ PRIVATE_NAMESPACE_BEGIN struct ShregmapTech { virtual ~ShregmapTech() { } - virtual bool analyze(vector<int> &taps) = 0; + virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {} + virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {} + virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0; virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; }; @@ -54,7 +56,7 @@ struct ShregmapOptions struct ShregmapTechGreenpak4 : ShregmapTech { - bool analyze(vector<int> &taps) + bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) { if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { taps.clear(); @@ -91,6 +93,197 @@ struct ShregmapTechGreenpak4 : ShregmapTech } }; +struct ShregmapTechXilinx7 : ShregmapTech +{ + dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset; + dict<SigBit, SigSpec> sigbit_to_eq_input; + const ShregmapOptions &opts; + + ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {} + + virtual void init(const Module* module, const SigMap &sigmap) override + { + for (const auto &i : module->cells_) { + auto cell = i.second; + if (cell->type == "$shiftx") { + if (cell->getParam("\\Y_WIDTH") != 1) continue; + int j = 0; + for (auto bit : sigmap(cell->getPort("\\A"))) + sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0); + log_assert(j == cell->getParam("\\A_WIDTH").as_int()); + } + else if (cell->type == "$mux") { + int j = 0; + for (auto bit : sigmap(cell->getPort("\\A"))) + sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++); + j = 0; + for (auto bit : sigmap(cell->getPort("\\B"))) + sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++); + } + else if (cell->type == "$pmux") { + if (!cell->get_bool_attribute("\\shiftx_compatible")) continue; + int width = cell->getParam("\\WIDTH").as_int(); + int j = 0; + for (auto bit : sigmap(cell->getPort("\\A"))) + sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++); + j = cell->getParam("\\S_WIDTH").as_int(); + int k = 0; + for (auto bit : sigmap(cell->getPort("\\B"))) { + sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j, k++); + if (k == width) { + k = 0; + --j; + } + } + log_assert(j == 0); + } + else if (cell->type == "$eq") { + auto b_wire = cell->getPort("\\B"); + // Keep track of $eq cells that compare against the value 1 + // in anticipation that they drive the select (S) port of a $pmux + if (b_wire.is_fully_const() && b_wire.as_int() == 1) { + auto y_wire = sigmap(cell->getPort("\\Y").as_bit()); + sigbit_to_eq_input[y_wire] = cell->getPort("\\A"); + } + } + } + } + + virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override + { + auto it = sigbit_to_shiftx_offset.find(bit); + if (it == sigbit_to_shiftx_offset.end()) + return; + if (cell) { + if (cell->type == "$shiftx" && port == "\\A") + return; + if (cell->type == "$pmux" && (port == "\\A" || port == "\\B")) + return; + if (cell->type == "$mux" && (port == "\\A" || port == "\\B")) + return; + } + sigbit_to_shiftx_offset.erase(it); + } + + virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override + { + if (GetSize(taps) == 1) + return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]); + + if (taps.back() < opts.minlen-1) + return false; + + Cell *shiftx = nullptr; + int group = 0; + for (int i = 0; i < GetSize(taps); ++i) { + auto it = sigbit_to_shiftx_offset.find(qbits[i]); + if (it == sigbit_to_shiftx_offset.end()) + return false; + + // Check taps are sequential + if (i != taps[i]) + return false; + // Check taps are not connected to a shift register, + // or sequential to the same shift register + if (i == 0) { + int offset; + std::tie(shiftx,offset,group) = it->second; + if (offset != i) + return false; + } + else { + Cell *shiftx_ = std::get<0>(it->second); + if (shiftx_ != shiftx) + return false; + int offset = std::get<1>(it->second); + if (offset != i) + return false; + int group_ = std::get<2>(it->second); + if (group_ != group) + return false; + } + } + log_assert(shiftx); + + // Only map if $shiftx exclusively covers the shift register + if (shiftx->type == "$shiftx") { + if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int()) + return false; + } + else if (shiftx->type == "$pmux") { + if (GetSize(taps) != shiftx->getParam("\\S_WIDTH").as_int() + 1) + return false; + } + else if (shiftx->type == "$mux") { + if (GetSize(taps) != 2) + return false; + } + else log_abort(); + + return true; + } + + virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override + { + const auto &tap = *taps.begin(); + auto bit = tap.second; + + auto it = sigbit_to_shiftx_offset.find(bit); + log_assert(it != sigbit_to_shiftx_offset.end()); + + auto newcell = cell->module->addCell(NEW_ID, "$__XILINX_SHREG_"); + newcell->set_src_attribute(cell->get_src_attribute()); + newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH")); + newcell->setParam("\\INIT", cell->getParam("\\INIT")); + newcell->setParam("\\CLKPOL", cell->getParam("\\CLKPOL")); + newcell->setParam("\\ENPOL", cell->getParam("\\ENPOL")); + + newcell->setPort("\\C", cell->getPort("\\C")); + newcell->setPort("\\D", cell->getPort("\\D")); + if (cell->hasPort("\\E")) + newcell->setPort("\\E", cell->getPort("\\E")); + + Cell* shiftx = std::get<0>(it->second); + RTLIL::SigSpec l_wire, q_wire; + if (shiftx->type == "$shiftx") { + l_wire = shiftx->getPort("\\B"); + q_wire = shiftx->getPort("\\Y"); + shiftx->setPort("\\Y", cell->module->addWire(NEW_ID)); + } + else if (shiftx->type == "$pmux") { + // If the 'A' port is fully undef, then opt_expr -mux_undef + // has not been applied, so find the second-to-last bit of + // the 'S' port (corresponding to $eq cell comparing for 1) + // otherwise use the last bit of 'S' + const auto& s_wire_bits = shiftx->getPort("\\S").bits(); + SigBit s1; + if (shiftx->getPort("\\A").is_fully_undef()) + s1 = s_wire_bits[s_wire_bits.size() - 2]; + else + s1 = s_wire_bits[s_wire_bits.size() - 1]; + RTLIL::SigSpec y_wire = shiftx->getPort("\\Y"); + l_wire = sigbit_to_eq_input.at(s1); + log_assert(l_wire.size() == ceil(log2(taps.size()))); + int group = std::get<2>(it->second); + q_wire = y_wire[group]; + y_wire[group] = cell->module->addWire(NEW_ID); + shiftx->setPort("\\Y", y_wire); + } + else if (shiftx->type == "$mux") { + l_wire = shiftx->getPort("\\S"); + q_wire = shiftx->getPort("\\Y"); + shiftx->setPort("\\Y", cell->module->addWire(NEW_ID)); + } + else log_abort(); + + newcell->setPort("\\Q", q_wire); + newcell->setPort("\\L", l_wire); + + return false; + } +}; + + struct ShregmapWorker { Module *module; @@ -113,8 +306,10 @@ struct ShregmapWorker for (auto wire : module->wires()) { if (wire->port_output || wire->get_bool_attribute("\\keep")) { - for (auto bit : sigmap(wire)) + for (auto bit : sigmap(wire)) { sigbit_with_non_chain_users.insert(bit); + if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {}); + } } if (wire->attributes.count("\\init")) { @@ -152,8 +347,10 @@ struct ShregmapWorker for (auto conn : cell->connections()) if (cell->input(conn.first)) - for (auto bit : sigmap(conn.second)) + for (auto bit : sigmap(conn.second)) { sigbit_with_non_chain_users.insert(bit); + if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first); + } } } @@ -258,7 +455,7 @@ struct ShregmapWorker if (taps.empty() || taps.back() < depth-1) taps.push_back(depth-1); - if (opts.tech->analyze(taps)) + if (opts.tech->analyze(taps, qbits)) break; taps.pop_back(); @@ -377,6 +574,9 @@ struct ShregmapWorker ShregmapWorker(Module *module, const ShregmapOptions &opts) : module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) { + if (opts.tech) + opts.tech->init(module, sigmap); + make_sigbit_chain_next_prev(); find_chain_start_cells(); @@ -501,6 +701,12 @@ struct ShregmapPass : public Pass { clkpol = "pos"; opts.zinit = true; opts.tech = new ShregmapTechGreenpak4; + } + else if (tech == "xilinx") { + opts.init = true; + opts.params = true; + enpol = "any_or_none"; + opts.tech = new ShregmapTechXilinx7(opts); } else { argidx--; break; diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 660b60601..f3da80c66 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -599,7 +599,7 @@ struct SimplemapPass : public Pass { simplemap_get_mappers(mappers); for (auto mod : design->modules()) { - if (!design->selected(mod)) + if (!design->selected(mod) || mod->get_blackbox_attribute()) continue; std::vector<RTLIL::Cell*> cells = mod->cells(); for (auto cell : cells) { diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index d0e5e2236..ee319b6e6 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -84,6 +84,7 @@ struct TechmapWorker bool flatten_mode; bool recursive_mode; bool autoproc_mode; + bool ignore_wb; TechmapWorker() { @@ -92,6 +93,7 @@ struct TechmapWorker flatten_mode = false; recursive_mode = false; autoproc_mode = false; + ignore_wb = false; } std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose) @@ -383,7 +385,7 @@ struct TechmapWorker { std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping"; - if (!design->selected(module)) + if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb)) return false; bool log_continue = false; @@ -472,7 +474,7 @@ struct TechmapWorker RTLIL::Module *tpl = map->modules_[tpl_name]; std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end()); - if (tpl->get_bool_attribute("\\blackbox")) + if (tpl->get_blackbox_attribute(ignore_wb)) continue; if (!flatten_mode) @@ -925,6 +927,9 @@ struct TechmapPass : public Pass { log(" -autoproc\n"); log(" Automatically call \"proc\" on implementations that contain processes.\n"); log("\n"); + log(" -wb\n"); + log(" Ignore the 'whitebox' attribute on cell implementations.\n"); + log("\n"); log(" -assert\n"); log(" this option will cause techmap to exit with an error if it can't map\n"); log(" a selected cell. only cell types that end on an underscore are accepted\n"); @@ -1068,6 +1073,10 @@ struct TechmapPass : public Pass { worker.autoproc_mode = true; continue; } + if (args[argidx] == "-wb") { + worker.ignore_wb = true; + continue; + } break; } extra_args(args, argidx, design); @@ -1145,7 +1154,7 @@ struct FlattenPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" flatten [selection]\n"); + log(" flatten [options] [selection]\n"); log("\n"); log("This pass flattens the design by replacing cells by their implementation. This\n"); log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); @@ -1154,17 +1163,29 @@ struct FlattenPass : public Pass { log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); log("flattened by this command.\n"); log("\n"); + log(" -wb\n"); + log(" Ignore the 'whitebox' attribute on cell implementations.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); - extra_args(args, 1, design); - TechmapWorker worker; worker.flatten_mode = true; + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-wb") { + worker.ignore_wb = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; for (auto module : design->modules()) celltypeMap[module->name].insert(module->name); @@ -1209,7 +1230,7 @@ struct FlattenPass : public Pass { dict<RTLIL::IdString, RTLIL::Module*> new_modules; for (auto mod : vector<Module*>(design->modules())) - if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) { + if (used_modules[mod->name] || mod->get_blackbox_attribute(worker.ignore_wb)) { new_modules[mod->name] = mod; } else { log("Deleting now unused module %s.\n", log_id(mod)); diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index d5801c0fc..704ab21b1 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -17,4 +17,130 @@ * */ -// Empty for now +module \$__SHREG_ (input C, input D, input E, output Q); + parameter DEPTH = 0; + parameter [DEPTH-1:0] INIT = 0; + parameter CLKPOL = 1; + parameter ENPOL = 2; + + \$__XILINX_SHREG_ #(.DEPTH(DEPTH), .INIT(INIT), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(DEPTH-1), .E(E), .Q(Q)); +endmodule + +module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO); + parameter DEPTH = 0; + parameter [DEPTH-1:0] INIT = 0; + parameter CLKPOL = 1; + parameter ENPOL = 2; + + // shregmap's INIT parameter shifts out LSB first; + // however Xilinx expects MSB first + function [DEPTH-1:0] brev; + input [DEPTH-1:0] din; + integer i; + begin + for (i = 0; i < DEPTH; i=i+1) + brev[i] = din[DEPTH-1-i]; + end + endfunction + localparam [DEPTH-1:0] INIT_R = brev(INIT); + + parameter _TECHMAP_CONSTMSK_L_ = 0; + parameter _TECHMAP_CONSTVAL_L_ = 0; + + wire CE; + generate + if (ENPOL == 0) + assign CE = ~E; + else if (ENPOL == 1) + assign CE = E; + else + assign CE = 1'b1; + if (DEPTH == 1) begin + if (CLKPOL) + FDRE #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0)); + else + FDRE_1 #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0)); + end else + if (DEPTH <= 16) begin + SRL16E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A0(L[0]), .A1(L[1]), .A2(L[2]), .A3(L[3]), .CE(CE), .CLK(C), .D(D), .Q(Q)); + end else + if (DEPTH > 17 && DEPTH <= 32) begin + SRLC32E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(Q)); + end else + if (DEPTH > 33 && DEPTH <= 64) begin + wire T0, T1, T2; + SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-32), .INIT(INIT[DEPTH-32-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L), .E(E), .Q(T2)); + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T2; + else + MUXF7 fpga_mux_0 (.O(Q), .I0(T0), .I1(T2), .S(L[5])); + end else + if (DEPTH > 65 && DEPTH <= 96) begin + wire T0, T1, T2, T3, T4, T5, T6; + SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-64), .INIT(INIT[DEPTH-64-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_2 (.C(C), .D(T3), .L(L[4:0]), .E(E), .Q(T4)); + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T4; + else begin + MUXF7 fpga_mux_0 (.O(T5), .I0(T0), .I1(T2), .S(L[5])); + MUXF7 fpga_mux_1 (.O(T6), .I0(T4), .I1(1'b0 /* unused */), .S(L[5])); + MUXF8 fpga_mux_2 (.O(Q), .I0(T5), .I1(T6), .S(L[6])); + end + end else + if (DEPTH > 97 && DEPTH < 128) begin + wire T0, T1, T2, T3, T4, T5, T6, T7, T8; + SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); + SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5)); + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-96), .INIT(INIT[DEPTH-96-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_3 (.C(C), .D(T5), .L(L[4:0]), .E(E), .Q(T6)); + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T6; + else begin + MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5])); + MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5])); + MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6])); + end + end + else if (DEPTH == 128) begin + wire T0, T1, T2, T3, T4, T5, T6; + SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); + SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5)); + SRLC32E #(.INIT(INIT_R[128-1:96]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_3 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T5), .Q(T6), .Q31(SO)); + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T6; + else begin + wire T7, T8; + MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5])); + MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5])); + MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6])); + end + end + else if (DEPTH <= 129 && ~&_TECHMAP_CONSTMSK_L_) begin + // Handle cases where fixed-length depth is + // just 1 over a convenient value + \$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q)); + end + else begin + localparam lower_clog2 = $clog2((DEPTH+1)/2); + localparam lower_depth = 2 ** lower_clog2; + wire T0, T1, T2, T3; + if (&_TECHMAP_CONSTMSK_L_) begin + \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(lower_depth-1), .E(E), .Q(T0)); + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T0), .L(DEPTH-lower_depth-1), .E(E), .Q(Q), .SO(T3)); + end + else begin + \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(L[lower_clog2-1:0]), .E(E), .Q(T0), .SO(T1)); + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L[lower_clog2-1:0]), .E(E), .Q(T2), .SO(T3)); + assign Q = L[lower_clog2] ? T2 : T0; + end + if (DEPTH == 2 * lower_depth) + assign SO = T3; + end + endgenerate +endmodule + +`ifndef SRL_ONLY +`endif diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 0c8f282a4..3a4540b83 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -308,3 +308,42 @@ module RAM128X1D ( wire clk = WCLK ^ IS_WCLK_INVERTED; always @(posedge clk) if (WE) mem[A] <= D; endmodule + +module SRL16E ( + output Q, + input A0, A1, A2, A3, CE, CLK, D +); + parameter [15:0] INIT = 16'h0000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + + reg [15:0] r = INIT; + assign Q = r[{A3,A2,A1,A0}]; + generate + if (IS_CLK_INVERTED) begin + always @(negedge CLK) if (CE) r <= { r[14:0], D }; + end + else + always @(posedge CLK) if (CE) r <= { r[14:0], D }; + endgenerate +endmodule + +module SRLC32E ( + output Q, + output Q31, + input [4:0] A, + input CE, CLK, D +); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + + reg [31:0] r = INIT; + assign Q31 = r[31]; + assign Q = r[A]; + generate + if (IS_CLK_INVERTED) begin + always @(negedge CLK) if (CE) r <= { r[30:0], D }; + end + else + always @(posedge CLK) if (CE) r <= { r[30:0], D }; + endgenerate +endmodule diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh index 8b06c3155..8e39b440d 100644 --- a/techlibs/xilinx/cells_xtra.sh +++ b/techlibs/xilinx/cells_xtra.sh @@ -135,8 +135,8 @@ function xtract_cell_decl() xtract_cell_decl ROM256X1 xtract_cell_decl ROM32X1 xtract_cell_decl ROM64X1 - xtract_cell_decl SRL16E - xtract_cell_decl SRLC32E + #xtract_cell_decl SRL16E + #xtract_cell_decl SRLC32E xtract_cell_decl STARTUPE2 "(* keep *)" xtract_cell_decl USR_ACCESSE2 xtract_cell_decl XADC diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index 4fb6798be..fbcc74682 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -3809,22 +3809,6 @@ module ROM64X1 (...); input A0, A1, A2, A3, A4, A5; endmodule -module SRL16E (...); - parameter [15:0] INIT = 16'h0000; - parameter [0:0] IS_CLK_INVERTED = 1'b0; - output Q; - input A0, A1, A2, A3, CE, CLK, D; -endmodule - -module SRLC32E (...); - parameter [31:0] INIT = 32'h00000000; - parameter [0:0] IS_CLK_INVERTED = 1'b0; - output Q; - output Q31; - input [4:0] A; - input CE, CLK, D; -endmodule - (* keep *) module STARTUPE2 (...); parameter PROG_USR = "FALSE"; diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 397c83ac6..57bde998f 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -64,10 +64,13 @@ struct SynthXilinxPass : public Pass log(" (this feature is experimental and incomplete)\n"); log("\n"); log(" -nobram\n"); - log(" disable infering of block rams\n"); + log(" disable inference of block rams\n"); log("\n"); log(" -nodram\n"); - log(" disable infering of distributed rams\n"); + log(" disable inference of distributed rams\n"); + log("\n"); + log(" -nosrl\n"); + log(" disable inference of shift registers\n"); log("\n"); log(" -run <from_label>:<to_label>\n"); log(" only run the commands between the labels (see below). an empty\n"); @@ -110,20 +113,23 @@ struct SynthXilinxPass : public Pass log(" dffsr2dff\n"); log(" dff2dffe\n"); log(" opt -full\n"); + log(" simplemap t:$dff t:$dffe (without '-nosrl' only)\n"); + log(" shregmap -tech xilinx -minlen 3 (without '-nosrl' only)\n"); log(" techmap -map +/techmap.v -map +/xilinx/arith_map.v\n"); log(" opt -fast\n"); log("\n"); + log(" map_cells:\n"); + log(" techmap -map +/techmap.v -map +/xilinx/cells_map.v\n"); + log(" clean\n"); + log("\n"); log(" map_luts:\n"); log(" techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?\n"); log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n"); log(" clean\n"); - log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v"); - log("\n"); - log(" map_cells:\n"); - log(" techmap -map +/xilinx/cells_map.v\n"); + log(" shregmap -minlen 3 -init -params -enpol any_or_none (without '-nosrl' only)\n"); + log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v"); log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n"); log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n"); - log(" clean\n"); log("\n"); log(" check:\n"); log(" hierarchy -check\n"); @@ -148,6 +154,7 @@ struct SynthXilinxPass : public Pass bool vpr = false; bool nobram = false; bool nodram = false; + bool nosrl = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -192,6 +199,10 @@ struct SynthXilinxPass : public Pass nodram = true; continue; } + if (args[argidx] == "-nosrl") { + nosrl = true; + continue; + } break; } extra_args(args, argidx, design); @@ -256,6 +267,11 @@ struct SynthXilinxPass : public Pass Pass::call(design, "dff2dffe"); Pass::call(design, "opt -full"); + if (!nosrl) { + Pass::call(design, "simplemap t:$dff t:$dffe"); + Pass::call(design, "shregmap -tech xilinx -minlen 3"); + } + if (vpr) { Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); } else { @@ -266,20 +282,22 @@ struct SynthXilinxPass : public Pass Pass::call(design, "opt -fast"); } - if (check_label(active, run_from, run_to, "map_luts")) + if (check_label(active, run_from, run_to, "map_cells")) { - Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?"); - Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); + Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/cells_map.v"); Pass::call(design, "clean"); - Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v"); } - if (check_label(active, run_from, run_to, "map_cells")) + if (check_label(active, run_from, run_to, "map_luts")) { - Pass::call(design, "techmap -map +/xilinx/cells_map.v"); + Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?"); + Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); + Pass::call(design, "clean"); + if (!nosrl) + Pass::call(design, "shregmap -minlen 3 -init -params -enpol any_or_none"); + Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v"); Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT"); - Pass::call(design, "clean"); } if (check_label(active, run_from, run_to, "check")) |