Merge remote-tracking branch 'origin/master' into xc7dsp

This commit is contained in:
Eddie Hung 2019-07-10 15:58:01 -07:00
commit cea7441d8a
34 changed files with 738 additions and 275 deletions

View File

@ -21,13 +21,15 @@
// https://stackoverflow.com/a/46137633 // https://stackoverflow.com/a/46137633
#ifdef _MSC_VER #ifdef _MSC_VER
#include <stdlib.h> #include <stdlib.h>
#define __builtin_bswap32 _byteswap_ulong #define bswap32 _byteswap_ulong
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <libkern/OSByteOrder.h> #include <libkern/OSByteOrder.h>
#define __builtin_bswap32 OSSwapInt32 #define bswap32 OSSwapInt32
#elif !defined(__GNUC__) #elif defined(__GNUC__)
#define bswap32 __builtin_bswap32
#else
#include <cstdint> #include <cstdint>
inline uint32_t __builtin_bswap32(uint32_t x) inline static uint32_t bswap32(uint32_t x)
{ {
// https://stackoverflow.com/a/27796212 // https://stackoverflow.com/a/27796212
register uint32_t value = number_to_be_reversed; register uint32_t value = number_to_be_reversed;
@ -138,6 +140,7 @@ struct XAigerWriter
{ {
pool<SigBit> undriven_bits; pool<SigBit> undriven_bits;
pool<SigBit> unused_bits; pool<SigBit> unused_bits;
pool<SigBit> keep_bits;
// promote public wires // promote public wires
for (auto wire : module->wires()) for (auto wire : module->wires())
@ -168,6 +171,9 @@ struct XAigerWriter
unused_bits.insert(bit); unused_bits.insert(bit);
} }
if (keep)
keep_bits.insert(bit);
if (wire->port_input || keep) { if (wire->port_input || keep) {
if (bit != wirebit) if (bit != wirebit)
alias_map[bit] = wirebit; alias_map[bit] = wirebit;
@ -255,10 +261,11 @@ struct XAigerWriter
} }
} }
else { else {
bool cell_known = cell->known();
for (const auto &c : cell->connections()) { for (const auto &c : cell->connections()) {
if (c.second.is_fully_const()) continue; if (c.second.is_fully_const()) continue;
auto is_input = cell->input(c.first); auto is_input = !cell_known || cell->input(c.first);
auto is_output = cell->output(c.first); auto is_output = !cell_known || cell->output(c.first);
if (!is_input && !is_output) if (!is_input && !is_output)
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
@ -266,12 +273,15 @@ struct XAigerWriter
for (auto b : c.second.bits()) { for (auto b : c.second.bits()) {
Wire *w = b.wire; Wire *w = b.wire;
if (!w) continue; if (!w) continue;
if (!w->port_output) { if (!w->port_output || !cell_known) {
SigBit I = sigmap(b); SigBit I = sigmap(b);
if (I != b) if (I != b)
alias_map[b] = I; alias_map[b] = I;
output_bits.insert(b); output_bits.insert(b);
unused_bits.erase(b); unused_bits.erase(b);
if (!cell_known)
keep_bits.insert(b);
} }
} }
} }
@ -424,7 +434,7 @@ struct XAigerWriter
auto jt = input_bits.find(b); auto jt = input_bits.find(b);
if (jt != input_bits.end()) { if (jt != input_bits.end()) {
log_assert(b.wire->attributes.count("\\keep")); log_assert(keep_bits.count(O));
input_bits.erase(b); input_bits.erase(b);
} }
} }
@ -444,7 +454,7 @@ struct XAigerWriter
// with $inout.out suffix, make it a PO driven by the existing inout, and // with $inout.out suffix, make it a PO driven by the existing inout, and
// inherit existing inout's drivers // inherit existing inout's drivers
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit)) if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|| wire->attributes.count("\\keep")) { || keep_bits.count(bit)) {
RTLIL::IdString wire_name = wire->name.str() + "$inout.out"; RTLIL::IdString wire_name = wire->name.str() + "$inout.out";
RTLIL::Wire *new_wire = module->wire(wire_name); RTLIL::Wire *new_wire = module->wire(wire_name);
if (!new_wire) if (!new_wire)

View File

@ -204,6 +204,11 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
{ {
for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) {
f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str());
dump_const(f, ait->second);
f << stringf("\n");
}
f << stringf("%s case ", indent.c_str()); f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) { for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0) if (i > 0)

View File

@ -364,20 +364,22 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
} }
} }
void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false) void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool as_comment = false)
{ {
if (noattr) if (noattr)
return; return;
if (attr2comment)
as_comment = true;
for (auto it = attributes.begin(); it != attributes.end(); ++it) { for (auto it = attributes.begin(); it != attributes.end(); ++it) {
f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
f << stringf(" = "); f << stringf(" = ");
if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
f << stringf(" 0 "); f << stringf(" 0 ");
else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
f << stringf(" 1 "); f << stringf(" 1 ");
else else
dump_const(f, it->second, -1, 0, false, attr2comment); dump_const(f, it->second, -1, 0, false, as_comment);
f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); f << stringf(" %s%c", as_comment ? "*/" : "*)", term);
} }
} }
@ -1492,12 +1494,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
return; return;
} }
dump_attributes(f, indent, sw->attributes);
f << stringf("%s" "casez (", indent.c_str()); f << stringf("%s" "casez (", indent.c_str());
dump_sigspec(f, sw->signal); dump_sigspec(f, sw->signal);
f << stringf(")\n"); f << stringf(")\n");
bool got_default = false; bool got_default = false;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*as_comment=*/true);
if ((*it)->compare.size() == 0) { if ((*it)->compare.size() == 0) {
if (got_default) if (got_default)
continue; continue;
@ -1662,7 +1666,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
} }
} }
dump_attributes(f, indent, module->attributes, '\n', true); dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true);
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
bool keep_running = true; bool keep_running = true;
for (int port_id = 1; keep_running; port_id++) { for (int port_id = 1; keep_running; port_id++) {

View File

@ -504,6 +504,7 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::CaseRule *backup_case = current_case; RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule; current_case = new RTLIL::CaseRule;
current_case->attributes["\\src"] = stringf("%s:%d", child->filename.c_str(), child->linenum);
last_generated_case = current_case; last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
for (auto node : child->children) { for (auto node : child->children) {

View File

@ -282,14 +282,14 @@ proc_stmt:
} case_body sync_list TOK_END EOL; } case_body sync_list TOK_END EOL;
switch_stmt: switch_stmt:
attr_list TOK_SWITCH sigspec EOL { TOK_SWITCH sigspec EOL {
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
rule->signal = *$3; rule->signal = *$2;
rule->attributes = attrbuf; rule->attributes = attrbuf;
switch_stack.back()->push_back(rule); switch_stack.back()->push_back(rule);
attrbuf.clear(); attrbuf.clear();
delete $3; delete $2;
} switch_body TOK_END EOL; } attr_list switch_body TOK_END EOL;
attr_list: attr_list:
/* empty */ | /* empty */ |
@ -298,9 +298,11 @@ attr_list:
switch_body: switch_body:
switch_body TOK_CASE { switch_body TOK_CASE {
RTLIL::CaseRule *rule = new RTLIL::CaseRule; RTLIL::CaseRule *rule = new RTLIL::CaseRule;
rule->attributes = attrbuf;
switch_stack.back()->back()->cases.push_back(rule); switch_stack.back()->back()->cases.push_back(rule);
switch_stack.push_back(&rule->switches); switch_stack.push_back(&rule->switches);
case_stack.push_back(rule); case_stack.push_back(rule);
attrbuf.clear();
} compare_list EOL case_body { } compare_list EOL case_body {
switch_stack.pop_back(); switch_stack.pop_back();
case_stack.pop_back(); case_stack.pop_back();
@ -319,12 +321,15 @@ compare_list:
/* empty */; /* empty */;
case_body: case_body:
case_body attr_stmt |
case_body switch_stmt | case_body switch_stmt |
case_body assign_stmt | case_body assign_stmt |
/* empty */; /* empty */;
assign_stmt: assign_stmt:
TOK_ASSIGN sigspec sigspec EOL { TOK_ASSIGN sigspec sigspec EOL {
if (attrbuf.size() != 0)
rtlil_frontend_ilang_yyerror("dangling attribute");
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
delete $2; delete $2;
delete $3; delete $3;

View File

@ -1021,13 +1021,8 @@ list_of_specparam_assignments:
specparam_assignment: specparam_assignment:
ignspec_id '=' constant_mintypmax_expression ; ignspec_id '=' constant_mintypmax_expression ;
/* ignspec_opt_cond:
pulsestyle_declaration : TOK_IF '(' ignspec_expr ')' | /* empty */;
;
showcancelled_declaration :
;
*/
path_declaration : path_declaration :
simple_path_declaration ';' simple_path_declaration ';'
@ -1036,8 +1031,8 @@ path_declaration :
; ;
simple_path_declaration : simple_path_declaration :
parallel_path_description '=' path_delay_value | ignspec_opt_cond parallel_path_description '=' path_delay_value |
full_path_description '=' path_delay_value ignspec_opt_cond full_path_description '=' path_delay_value
; ;
path_delay_value : path_delay_value :
@ -1047,32 +1042,20 @@ path_delay_value :
; ;
list_of_path_delay_extra_expressions : list_of_path_delay_extra_expressions :
/* ',' path_delay_expression | ',' path_delay_expression list_of_path_delay_extra_expressions;
t_path_delay_expression
| trise_path_delay_expression ',' tfall_path_delay_expression specify_edge_identifier :
| trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression TOK_POSEDGE | TOK_NEGEDGE ;
| t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression
| t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression ','
t0x_path_delay_expression ',' tx1_path_delay_expression ',' t1x_path_delay_expression ','
tx0_path_delay_expression ',' txz_path_delay_expression ',' tzx_path_delay_expression
*/
',' path_delay_expression
| ',' path_delay_expression ',' path_delay_expression
| ',' path_delay_expression ',' path_delay_expression ','
path_delay_expression ',' path_delay_expression ',' path_delay_expression
| ',' path_delay_expression ',' path_delay_expression ','
path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
path_delay_expression ',' path_delay_expression ',' path_delay_expression
;
parallel_path_description : parallel_path_description :
'(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' ; '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' |
'(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor opt_polarity_operator ':' ignspec_expr ')' ')' |
'(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr ')' ')' ;
full_path_description : full_path_description :
'(' list_of_path_inputs '*' '>' list_of_path_outputs ')' ; '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' |
'(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs opt_polarity_operator ':' ignspec_expr ')' ')' |
'(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs TOK_POS_INDEXED ignspec_expr ')' ')' ;
// This was broken into 2 rules to solve shift/reduce conflicts // This was broken into 2 rules to solve shift/reduce conflicts
list_of_path_inputs : list_of_path_inputs :
@ -1112,56 +1095,6 @@ system_timing_args :
system_timing_arg | system_timing_arg |
system_timing_args ',' system_timing_arg ; system_timing_args ',' system_timing_arg ;
/*
t_path_delay_expression :
path_delay_expression;
trise_path_delay_expression :
path_delay_expression;
tfall_path_delay_expression :
path_delay_expression;
tz_path_delay_expression :
path_delay_expression;
t01_path_delay_expression :
path_delay_expression;
t10_path_delay_expression :
path_delay_expression;
t0z_path_delay_expression :
path_delay_expression;
tz1_path_delay_expression :
path_delay_expression;
t1z_path_delay_expression :
path_delay_expression;
tz0_path_delay_expression :
path_delay_expression;
t0x_path_delay_expression :
path_delay_expression;
tx1_path_delay_expression :
path_delay_expression;
t1x_path_delay_expression :
path_delay_expression;
tx0_path_delay_expression :
path_delay_expression;
txz_path_delay_expression :
path_delay_expression;
tzx_path_delay_expression :
path_delay_expression;
*/
path_delay_expression : path_delay_expression :
ignspec_constant_expression; ignspec_constant_expression;

View File

@ -1327,7 +1327,7 @@ public:
#endif #endif
}; };
struct RTLIL::CaseRule struct RTLIL::CaseRule : public RTLIL::AttrObject
{ {
std::vector<RTLIL::SigSpec> compare; std::vector<RTLIL::SigSpec> compare;
std::vector<RTLIL::SigSig> actions; std::vector<RTLIL::SigSig> actions;

View File

@ -1273,6 +1273,10 @@ struct ScriptCmdPass : public Pass {
log("If only one label is specified (without ':') then only the block\n"); log("If only one label is specified (without ':') then only the block\n");
log("marked with that label (until the next label) is executed.\n"); log("marked with that label (until the next label) is executed.\n");
log("\n"); log("\n");
log("In \"-scriptwire\" mode, the commands on the selected wire(s) will be executed\n");
log("in the scope of (and thus, relative to) the wires' owning module(s). This\n");
log("'-module' mode can be exited by using the 'cd' command.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {

View File

@ -331,8 +331,9 @@ to update {\tt \textbackslash{}q}.
An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and
exactly one RTLIL::CaseRule object, which is called the {\it root case}. exactly one RTLIL::CaseRule object, which is called the {\it root case}.
An RTLIL::SyncRule object contains an (optional) synchronization condition An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or
(signal and edge-type) and zero or more assignments (RTLIL::SigSig). more assignments (RTLIL::SigSig). The {\tt always} synchronization condition is used to break combinatorial
loops when a latch should be inferred instead.
An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig) An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig)
and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a

View File

@ -779,6 +779,9 @@ class WClass:
#if self.link_type != link_types.pointer: #if self.link_type != link_types.pointer:
text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
text += "\n\t\t\tif(ref == nullptr){"
text += "\n\t\t\t\tthrow std::runtime_error(\"" + self.name + " does not exist.\");"
text += "\n\t\t\t}"
text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
if self.link_type == link_types.pointer: if self.link_type == link_types.pointer:
text += "\n\t\t\tret->ref_obj = ref;" text += "\n\t\t\tret->ref_obj = ref;"

View File

@ -51,14 +51,14 @@ struct BugpointPass : public Pass {
log(" only consider crashes that place this string in the log file.\n"); log(" only consider crashes that place this string in the log file.\n");
log("\n"); log("\n");
log(" -fast\n"); log(" -fast\n");
log(" run `clean -purge` after each minimization step. converges faster, but\n"); log(" run `proc_clean; clean -purge` after each minimization step. converges\n");
log(" produces larger testcases, and may fail to produce any testcase at all if\n"); log(" faster, but produces larger testcases, and may fail to produce any\n");
log(" the crash is related to dangling wires.\n"); log(" testcase at all if the crash is related to dangling wires.\n");
log("\n"); log("\n");
log(" -clean\n"); log(" -clean\n");
log(" run `clean -purge` before checking testcase and after finishing. produces\n"); log(" run `proc_clean; clean -purge` before checking testcase and after\n");
log(" smaller and more useful testcases, but may fail to produce any testcase\n"); log(" finishing. produces smaller and more useful testcases, but may fail to\n");
log(" at all if the crash is related to dangling wires.\n"); log(" produce any testcase at all if the crash is related to dangling wires.\n");
log("\n"); log("\n");
log(" -modules\n"); log(" -modules\n");
log(" try to remove modules.\n"); log(" try to remove modules.\n");
@ -72,6 +72,12 @@ struct BugpointPass : public Pass {
log(" -connections\n"); log(" -connections\n");
log(" try to reconnect ports to 'x.\n"); log(" try to reconnect ports to 'x.\n");
log("\n"); log("\n");
log(" -assigns\n");
log(" try to remove process assigns from cases.\n");
log("\n");
log(" -updates\n");
log(" try to remove process updates from syncs.\n");
log("\n");
} }
bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script) bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script)
@ -110,6 +116,7 @@ struct BugpointPass : public Pass {
RTLIL::Design *design_copy = new RTLIL::Design; RTLIL::Design *design_copy = new RTLIL::Design;
for (auto &it : design->modules_) for (auto &it : design->modules_)
design_copy->add(it.second->clone()); design_copy->add(it.second->clone());
Pass::call(design_copy, "proc_clean -quiet");
Pass::call(design_copy, "clean -purge"); Pass::call(design_copy, "clean -purge");
if (do_delete) if (do_delete)
@ -117,7 +124,7 @@ struct BugpointPass : public Pass {
return design_copy; return design_copy;
} }
RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections) RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections, bool assigns, bool updates)
{ {
RTLIL::Design *design_copy = new RTLIL::Design; RTLIL::Design *design_copy = new RTLIL::Design;
for (auto &it : design->modules_) for (auto &it : design->modules_)
@ -225,6 +232,59 @@ struct BugpointPass : public Pass {
} }
} }
} }
if (assigns)
{
for (auto mod : design_copy->modules())
{
if (mod->get_blackbox_attribute())
continue;
for (auto &pr : mod->processes)
{
vector<RTLIL::CaseRule*> cases = {&pr.second->root_case};
while (!cases.empty())
{
RTLIL::CaseRule *cs = cases[0];
cases.erase(cases.begin());
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
{
if (index++ == seed)
{
log("Trying to remove assign %s %s in %s.%s.\n", log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
cs->actions.erase(it);
return design_copy;
}
}
for (auto &sw : cs->switches)
cases.insert(cases.end(), sw->cases.begin(), sw->cases.end());
}
}
}
}
if (updates)
{
for (auto mod : design_copy->modules())
{
if (mod->get_blackbox_attribute())
continue;
for (auto &pr : mod->processes)
{
for (auto &sy : pr.second->syncs)
{
for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it)
{
if (index++ == seed)
{
log("Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
sy->actions.erase(it);
return design_copy;
}
}
}
}
}
}
return NULL; return NULL;
} }
@ -232,7 +292,7 @@ struct BugpointPass : public Pass {
{ {
string yosys_cmd = "yosys", script, grep; string yosys_cmd = "yosys", script, grep;
bool fast = false, clean = false; bool fast = false, clean = false;
bool modules = false, ports = false, cells = false, connections = false, has_part = false; bool modules = false, ports = false, cells = false, connections = false, assigns = false, updates = false, has_part = false;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
@ -277,6 +337,16 @@ struct BugpointPass : public Pass {
has_part = true; has_part = true;
continue; continue;
} }
if (args[argidx] == "-assigns") {
assigns = true;
has_part = true;
continue;
}
if (args[argidx] == "-updates") {
updates = true;
has_part = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -290,6 +360,8 @@ struct BugpointPass : public Pass {
ports = true; ports = true;
cells = true; cells = true;
connections = true; connections = true;
assigns = true;
updates = true;
} }
if (!design->full_selection()) if (!design->full_selection())
@ -305,7 +377,7 @@ struct BugpointPass : public Pass {
bool found_something = false, stage2 = false; bool found_something = false, stage2 = false;
while (true) while (true)
{ {
if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections)) if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections, assigns, updates))
{ {
simplified = clean_design(simplified, fast, /*do_delete=*/true); simplified = clean_design(simplified, fast, /*do_delete=*/true);

View File

@ -1,5 +1,6 @@
OBJS += passes/proc/proc.o OBJS += passes/proc/proc.o
OBJS += passes/proc/proc_prune.o
OBJS += passes/proc/proc_clean.o OBJS += passes/proc/proc_clean.o
OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_rmdead.o
OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_init.o
@ -7,4 +8,3 @@ OBJS += passes/proc/proc_arst.o
OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_mux.o
OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dlatch.o
OBJS += passes/proc/proc_dff.o OBJS += passes/proc/proc_dff.o

View File

@ -37,6 +37,7 @@ struct ProcPass : public Pass {
log("\n"); log("\n");
log(" proc_clean\n"); log(" proc_clean\n");
log(" proc_rmdead\n"); log(" proc_rmdead\n");
log(" proc_prune\n");
log(" proc_init\n"); log(" proc_init\n");
log(" proc_arst\n"); log(" proc_arst\n");
log(" proc_mux\n"); log(" proc_mux\n");
@ -83,6 +84,7 @@ struct ProcPass : public Pass {
Pass::call(design, "proc_clean"); Pass::call(design, "proc_clean");
if (!ifxmode) if (!ifxmode)
Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_rmdead");
Pass::call(design, "proc_prune");
Pass::call(design, "proc_init"); Pass::call(design, "proc_init");
if (global_arst.empty()) if (global_arst.empty())
Pass::call(design, "proc_arst"); Pass::call(design, "proc_arst");

View File

@ -172,7 +172,7 @@ restart_proc_arst:
sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
} }
for (auto &action : sync->actions) { for (auto &action : sync->actions) {
RTLIL::SigSpec rspec = action.second; RTLIL::SigSpec rspec = assign_map(action.second);
RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size());
for (int i = 0; i < GetSize(rspec); i++) for (int i = 0; i < GetSize(rspec); i++)
if (rspec[i].wire == NULL) if (rspec[i].wire == NULL)

View File

@ -143,7 +143,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool quiet)
{ {
int count = 0; int count = 0;
bool did_something = true; bool did_something = true;
@ -160,7 +160,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
did_something = false; did_something = false;
proc_clean_case(&proc->root_case, did_something, count, -1); proc_clean_case(&proc->root_case, did_something, count, -1);
} }
if (count > 0) if (count > 0 && !quiet)
log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str()); log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str());
total_count += count; total_count += count;
} }
@ -171,7 +171,10 @@ struct ProcCleanPass : public Pass {
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n"); log("\n");
log(" proc_clean [selection]\n"); log(" proc_clean [options] [selection]\n");
log("\n");
log(" -quiet\n");
log(" do not print any messages.\n");
log("\n"); log("\n");
log("This pass removes empty parts of processes and ultimately removes a process\n"); log("This pass removes empty parts of processes and ultimately removes a process\n");
log("if it contains only empty structures.\n"); log("if it contains only empty structures.\n");
@ -180,9 +183,20 @@ struct ProcCleanPass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
int total_count = 0; int total_count = 0;
bool quiet = false;
if (find(args.begin(), args.end(), "-quiet") == args.end())
log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
extra_args(args, 1, design); size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-quiet") {
quiet = true;
continue;
}
}
extra_args(args, argidx, design);
for (auto mod : design->modules()) { for (auto mod : design->modules()) {
std::vector<RTLIL::IdString> delme; std::vector<RTLIL::IdString> delme;
@ -191,9 +205,10 @@ struct ProcCleanPass : public Pass {
for (auto &proc_it : mod->processes) { for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second)) if (!design->selected(mod, proc_it.second))
continue; continue;
proc_clean(mod, proc_it.second, total_count); proc_clean(mod, proc_it.second, total_count, quiet);
if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 &&
proc_it.second->root_case.actions.size() == 0) { proc_it.second->root_case.actions.size() == 0) {
if (!quiet)
log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str());
delme.push_back(proc_it.first); delme.push_back(proc_it.first);
} }
@ -204,6 +219,7 @@ struct ProcCleanPass : public Pass {
} }
} }
if (!quiet)
log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
} }
} ProcCleanPass; } ProcCleanPass;

View File

@ -26,21 +26,7 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc)
{
log_assert(rule.compare.size() == 0);
while (1) {
RTLIL::SigSpec tmp = sig;
for (auto &it : rule.actions)
tmp.replace(it.first, it.second);
if (tmp == sig)
break;
sig = tmp;
}
}
void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
{ {
bool found_init = false; bool found_init = false;
@ -53,9 +39,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
for (auto &action : sync->actions) for (auto &action : sync->actions)
{ {
RTLIL::SigSpec lhs = action.first; RTLIL::SigSpec lhs = action.first;
RTLIL::SigSpec rhs = action.second; RTLIL::SigSpec rhs = sigmap(action.second);
proc_get_const(rhs, proc->root_case);
if (!rhs.is_fully_const()) if (!rhs.is_fully_const())
log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));
@ -120,10 +104,12 @@ struct ProcInitPass : public Pass {
extra_args(args, 1, design); extra_args(args, 1, design);
for (auto mod : design->modules()) for (auto mod : design->modules())
if (design->selected(mod)) if (design->selected(mod)) {
SigMap sigmap(mod);
for (auto &proc_it : mod->processes) for (auto &proc_it : mod->processes)
if (design->selected(mod, proc_it.second)) if (design->selected(mod, proc_it.second))
proc_init(mod, proc_it.second); proc_init(mod, sigmap, proc_it.second);
}
} }
} ProcInitPass; } ProcInitPass;

View File

@ -144,7 +144,13 @@ struct SnippetSwCache
} }
}; };
RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode) void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs)
{
cell->attributes = sw->attributes;
cell->add_strpool_attribute("\\src", cs->get_strpool_attribute("\\src"));
}
RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{ {
std::stringstream sstr; std::stringstream sstr;
sstr << "$procmux$" << (autoidx++); sstr << "$procmux$" << (autoidx++);
@ -173,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
{ {
// create compare cell // create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq");
eq_cell->attributes = sw->attributes; apply_attrs(eq_cell, sw, cs);
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0);
@ -199,7 +205,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// reduce cmp vector to one logic signal // reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or"); RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or");
any_cell->attributes = sw->attributes; apply_attrs(any_cell, sw, cs);
any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width);
@ -212,7 +218,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(ctrl_wire); return RTLIL::SigSpec(ctrl_wire);
} }
RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{ {
log_assert(when_signal.size() == else_signal.size()); log_assert(when_signal.size() == else_signal.size());
@ -224,7 +230,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return when_signal; return when_signal;
// compare results // compare results
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
if (ctrl_sig.size() == 0) if (ctrl_sig.size() == 0)
return when_signal; return when_signal;
log_assert(ctrl_sig.size() == 1); log_assert(ctrl_sig.size() == 1);
@ -234,7 +240,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// create the multiplexer itself // create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux"); RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux");
mux_cell->attributes = sw->attributes; apply_attrs(mux_cell, sw, cs);
mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size()); mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size());
mux_cell->setPort("\\A", else_signal); mux_cell->setPort("\\A", else_signal);
@ -246,7 +252,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(result_wire); return RTLIL::SigSpec(result_wire);
} }
void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{ {
log_assert(last_mux_cell != NULL); log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size());
@ -254,7 +260,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
if (when_signal == last_mux_cell->getPort("\\A")) if (when_signal == last_mux_cell->getPort("\\A"))
return; return;
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
log_assert(ctrl_sig.size() == 1); log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = "$pmux"; last_mux_cell->type = "$pmux";
@ -395,9 +401,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
RTLIL::CaseRule *cs2 = sw->cases[case_idx]; RTLIL::CaseRule *cs2 = sw->cases[case_idx];
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode);
else else
result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode);
} }
} }

158
passes/proc/proc_prune.cc Normal file
View File

@ -0,0 +1,158 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2019 whitequark <whitequark@whitequark.org>
*
* 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/log.h"
#include <stdlib.h>
#include <stdio.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct PruneWorker
{
RTLIL::Module *module;
SigMap sigmap;
int removed_count = 0, promoted_count = 0;
PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected)
{
pool<RTLIL::SigBit> all_assigned;
bool full_case = sw->get_bool_attribute("\\full_case");
bool first = true;
for (auto it : sw->cases) {
if (it->compare.empty())
full_case = true;
pool<RTLIL::SigBit> case_assigned = do_case(it, assigned, affected);
if (first) {
first = false;
all_assigned = case_assigned;
} else {
for (auto &bit : all_assigned)
if (!case_assigned[bit])
all_assigned.erase(bit);
}
}
if (full_case)
assigned.insert(all_assigned.begin(), all_assigned.end());
return assigned;
}
pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected,
bool root = false)
{
for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) {
pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned, affected);
assigned.insert(sw_assigned.begin(), sw_assigned.end());
}
pool<RTLIL::SigSig> remove;
for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) {
RTLIL::SigSpec lhs = sigmap(it->first);
bool redundant = true;
for (auto &bit : lhs) {
if (bit.wire && !assigned[bit]) {
redundant = false;
break;
}
}
if (redundant) {
removed_count++;
remove.insert(*it);
} else {
if (root) {
bool promotable = true;
for (auto &bit : lhs) {
if (bit.wire && affected[bit]) {
promotable = false;
break;
}
}
if (promotable) {
promoted_count++;
module->connect(*it);
remove.insert(*it);
}
}
for (auto &bit : lhs)
if (bit.wire)
assigned.insert(bit);
for (auto &bit : lhs)
if (bit.wire)
affected.insert(bit);
}
}
for (auto it = cs->actions.begin(); it != cs->actions.end(); ) {
if (remove[*it]) {
it = cs->actions.erase(it);
} else it++;
}
return assigned;
}
void do_process(RTLIL::Process *pr)
{
pool<RTLIL::SigBit> affected;
do_case(&pr->root_case, {}, affected, /*root=*/true);
}
};
struct ProcPrunePass : public Pass {
ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" proc_prune [selection]\n");
log("\n");
log("This pass identifies assignments in processes that are always overwritten by\n");
log("a later assignment to the same signal and removes them.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int total_removed_count = 0, total_promoted_count = 0;
log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n");
extra_args(args, 1, design);
for (auto mod : design->modules()) {
if (!design->selected(mod))
continue;
PruneWorker worker(mod);
for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second))
continue;
worker.do_process(proc_it.second);
}
total_removed_count += worker.removed_count;
total_promoted_count += worker.promoted_count;
}
log("Removed %d redundant assignment%s.\n",
total_removed_count, total_removed_count == 1 ? "" : "s");
log("Promoted %d assignment%s to connection%s.\n",
total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s");
}
} ProcPrunePass;
PRIVATE_NAMESPACE_END

View File

@ -253,6 +253,13 @@ struct Clk2fflogicPass : public Pass {
SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
Const rstval = cell->parameters["\\ARST_VALUE"]; Const rstval = cell->parameters["\\ARST_VALUE"];
Wire *past_arst = module->addWire(NEW_ID);
module->addFf(NEW_ID, arst, past_arst);
if (cell->parameters["\\ARST_POLARITY"].as_bool())
arst = module->LogicOr(NEW_ID, arst, past_arst);
else
arst = module->LogicAnd(NEW_ID, arst, past_arst);
if (cell->parameters["\\ARST_POLARITY"].as_bool()) if (cell->parameters["\\ARST_POLARITY"].as_bool())
module->addMux(NEW_ID, qval, rstval, arst, sig_q); module->addMux(NEW_ID, qval, rstval, arst, sig_q);
else else

View File

@ -632,10 +632,15 @@ struct MuxcoverPass : public Pass {
log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
log("\n"); log("\n");
log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n"); log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n");
log(" Use the specified types of MUXes (with optional integer costs). If none\n"); log(" Cover $_MUX_ trees using the specified types of MUXes (with optional\n");
log(" of these options are given, the effect is the same as if all of them are.\n"); log(" integer costs). If none of these options are given, the effect is the\n");
log(" Default costs: $_MUX_ = %d, $_MUX4_ = %d,\n", COST_MUX2, COST_MUX4); log(" same as if all of them are.\n");
log(" $_MUX8_ = %d, $_MUX16_ = %d\n", COST_MUX8, COST_MUX16); log(" Default costs: $_MUX4_ = %d, $_MUX8_ = %d, \n", COST_MUX4, COST_MUX8);
log(" $_MUX16_ = %d\n", COST_MUX16);
log("\n");
log(" -mux2=cost\n");
log(" Use the specified cost for $_MUX_ cells when making covering decisions.\n");
log(" Default cost: $_MUX_ = %d\n", COST_MUX2);
log("\n"); log("\n");
log(" -dmux=cost\n"); log(" -dmux=cost\n");
log(" Use the specified cost for $_MUX_ cells used in decoders.\n"); log(" Use the specified cost for $_MUX_ cells used in decoders.\n");
@ -661,6 +666,7 @@ struct MuxcoverPass : public Pass {
bool nodecode = false; bool nodecode = false;
bool nopartial = false; bool nopartial = false;
int cost_dmux = COST_DMUX; int cost_dmux = COST_DMUX;
int cost_mux2 = COST_MUX2;
int cost_mux4 = COST_MUX4; int cost_mux4 = COST_MUX4;
int cost_mux8 = COST_MUX8; int cost_mux8 = COST_MUX8;
int cost_mux16 = COST_MUX16; int cost_mux16 = COST_MUX16;
@ -669,11 +675,15 @@ struct MuxcoverPass : public Pass {
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
const auto &arg = args[argidx]; const auto &arg = args[argidx];
if (arg.size() >= 6 && arg.substr(0,6) == "-mux2=") {
cost_mux2 = std::stoi(arg.substr(6));
continue;
}
if (arg.size() >= 5 && arg.substr(0,5) == "-mux4") { if (arg.size() >= 5 && arg.substr(0,5) == "-mux4") {
use_mux4 = true; use_mux4 = true;
if (arg.size() > 5) { if (arg.size() > 5) {
if (arg[5] != '=') break; if (arg[5] != '=') break;
cost_mux4 = atoi(arg.substr(6).c_str()); cost_mux4 = std::stoi(arg.substr(6));
} }
continue; continue;
} }
@ -681,7 +691,7 @@ struct MuxcoverPass : public Pass {
use_mux8 = true; use_mux8 = true;
if (arg.size() > 5) { if (arg.size() > 5) {
if (arg[5] != '=') break; if (arg[5] != '=') break;
cost_mux8 = atoi(arg.substr(6).c_str()); cost_mux8 = std::stoi(arg.substr(6));
} }
continue; continue;
} }
@ -689,12 +699,12 @@ struct MuxcoverPass : public Pass {
use_mux16 = true; use_mux16 = true;
if (arg.size() > 6) { if (arg.size() > 6) {
if (arg[6] != '=') break; if (arg[6] != '=') break;
cost_mux16 = atoi(arg.substr(7).c_str()); cost_mux16 = std::stoi(arg.substr(7));
} }
continue; continue;
} }
if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") { if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") {
cost_dmux = atoi(arg.substr(6).c_str()); cost_dmux = std::stoi(arg.substr(6));
continue; continue;
} }
if (arg == "-nodecode") { if (arg == "-nodecode") {
@ -722,6 +732,7 @@ struct MuxcoverPass : public Pass {
worker.use_mux8 = use_mux8; worker.use_mux8 = use_mux8;
worker.use_mux16 = use_mux16; worker.use_mux16 = use_mux16;
worker.cost_dmux = cost_dmux; worker.cost_dmux = cost_dmux;
worker.cost_mux2 = cost_mux2;
worker.cost_mux4 = cost_mux4; worker.cost_mux4 = cost_mux4;
worker.cost_mux8 = cost_mux8; worker.cost_mux8 = cost_mux8;
worker.cost_mux16 = cost_mux16; worker.cost_mux16 = cost_mux16;

View File

@ -649,10 +649,13 @@ struct TechmapWorker
unique_bit_id[bit] = unique_bit_id_counter++; unique_bit_id[bit] = unique_bit_id_counter++;
} }
// Find highest bit set
int bits = 0; int bits = 0;
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
if (((unique_bit_id_counter-1) & (1 << i)) != 0) if (((unique_bit_id_counter-1) & (1 << i)) != 0)
bits = i; bits = i;
// Increment index by one to get number of bits
bits++;
if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_")) if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_"))
parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits; parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits;

View File

@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite * yosys -- Yosys Open SYnthesis Suite
* *
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2018 Clifford Wolf <dave@ds0.me> * Copyright (C) 2018 David Shah <dave@ds0.me>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -207,6 +207,9 @@ struct SynthEcp5Pass : public ScriptPass
if (!design->full_selection()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); log_cmd_error("This command only operates on fully selected designs!\n");
if (abc9 && retime)
log_cmd_error("-retime option not currently compatible with -abc9!\n");
log_header(design, "Executing SYNTH_ECP5 pass.\n"); log_header(design, "Executing SYNTH_ECP5 pass.\n");
log_push(); log_push();

View File

@ -227,6 +227,9 @@ struct SynthIce40Pass : public ScriptPass
if (device_opt != "hx" && device_opt != "lp" && device_opt !="u") if (device_opt != "hx" && device_opt != "lp" && device_opt !="u")
log_cmd_error("Invalid or no device specified: '%s'\n", device_opt.c_str()); log_cmd_error("Invalid or no device specified: '%s'\n", device_opt.c_str());
if (abc == "abc9" && retime)
log_cmd_error("-retime option not currently compatible with -abc9!\n");
log_header(design, "Executing SYNTH_ICE40 pass.\n"); log_header(design, "Executing SYNTH_ICE40 pass.\n");
log_push(); log_push();
@ -296,7 +299,7 @@ struct SynthIce40Pass : public ScriptPass
run("techmap"); run("techmap");
else else
run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); run("techmap -map +/techmap.v -map +/ice40/arith_map.v");
if ((retime || help_mode) && abc != "abc9") if (retime || help_mode)
run(abc + " -dff", "(only if -retime)"); run(abc + " -dff", "(only if -retime)");
run("ice40_opt"); run("ice40_opt");
} }

View File

@ -48,6 +48,8 @@ struct SynthIntelPass : public ScriptPass {
log(" -vqm <file>\n"); log(" -vqm <file>\n");
log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n");
log(" output file is omitted if this parameter is not specified.\n"); log(" output file is omitted if this parameter is not specified.\n");
log(" Note that this backend has not been tested and is likely incompatible\n");
log(" with recent versions of Quartus.\n");
log("\n"); log("\n");
log(" -vpr <file>\n"); log(" -vpr <file>\n");
log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n");
@ -108,6 +110,7 @@ struct SynthIntelPass : public ScriptPass {
} }
if (args[argidx] == "-vqm" && argidx + 1 < args.size()) { if (args[argidx] == "-vqm" && argidx + 1 < args.size()) {
vout_file = args[++argidx]; vout_file = args[++argidx];
log_warning("The Quartus backend has not been tested recently and is likely incompatible with modern versions of Quartus.\n");
continue; continue;
} }
if (args[argidx] == "-vpr" && argidx + 1 < args.size()) { if (args[argidx] == "-vpr" && argidx + 1 < args.size()) {

View File

@ -56,7 +56,6 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
localparam [DEPTH-1:0] INIT_R = brev(INIT); localparam [DEPTH-1:0] INIT_R = brev(INIT);
parameter _TECHMAP_CONSTMSK_L_ = 0; parameter _TECHMAP_CONSTMSK_L_ = 0;
parameter _TECHMAP_CONSTVAL_L_ = 0;
wire CE; wire CE;
generate generate
@ -119,26 +118,33 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
else else
\$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q)); \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q));
end end
else if (DEPTH <= 129 && ~&_TECHMAP_CONSTMSK_L_) begin // For fixed length, if just 1 over a convenient value, decompose
// Handle cases where fixed-length depth is else if (DEPTH <= 129 && &_TECHMAP_CONSTMSK_L_) begin
// just 1 over a convenient value wire T;
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-1), .INIT(INIT[DEPTH-1:1]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(D), .L({32{1'b1}}), .E(E), .Q(T));
\$__XILINX_SHREG_ #(.DEPTH(1), .INIT(INIT[0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(T), .L(L), .E(E), .Q(Q));
end
// For variable length, if just 1 over a convenient value, then bump up one more
else if (DEPTH < 129 && ~&_TECHMAP_CONSTMSK_L_)
\$__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)); \$__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 else begin
localparam lower_clog2 = $clog2((DEPTH+1)/2); localparam depth0 = 128;
localparam lower_depth = 2 ** lower_clog2; localparam num_srl128 = DEPTH / depth0;
wire T0, T1, T2, T3; localparam depthN = DEPTH % depth0;
if (&_TECHMAP_CONSTMSK_L_) begin wire [num_srl128 + (depthN > 0 ? 1 : 0) - 1:0] T;
\$__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)); wire [num_srl128 + (depthN > 0 ? 1 : 0) :0] S;
\$__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)); assign S[0] = D;
end genvar i;
else begin for (i = 0; i < num_srl128; i++)
\$__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(depth0), .INIT(INIT[DEPTH-1-i*depth0-:depth0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(S[i]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[i]), .SO(S[i+1]));
\$__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; if (depthN > 0)
end \$__XILINX_SHREG_ #(.DEPTH(depthN), .INIT(INIT[depthN-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(S[num_srl128]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[num_srl128]));
if (DEPTH == 2 * lower_depth)
assign SO = T3; if (&_TECHMAP_CONSTMSK_L_)
assign Q = T[num_srl128 + (depthN > 0 ? 1 : 0) - 1];
else
assign Q = T[L[DEPTH-1:$clog2(depth0)]];
end end
endgenerate endgenerate
endmodule endmodule
@ -193,45 +199,89 @@ module \$__XILINX_SHIFTX (A, B, Y);
else if (A_WIDTH < `MIN_MUX_INPUTS) begin else if (A_WIDTH < `MIN_MUX_INPUTS) begin
wire _TECHMAP_FAIL_ = 1; wire _TECHMAP_FAIL_ = 1;
end end
else if (A_WIDTH <= 2 ** 3) begin else if (A_WIDTH == 2) begin
localparam a_width0 = 2 ** 2; MUXF7 fpga_hard_mux (.I0(A[0]), .I1(A[1]), .S(B[0]), .O(Y));
localparam a_widthN = A_WIDTH - a_width0;
wire T0, T1;
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux (.A(A[a_width0-1:0]), .B(B[2-1:0]), .Y(T0));
if (a_widthN > 1)
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux_last (.A(A[A_WIDTH-1:a_width0]), .B(B[$clog2(a_widthN)-1:0]), .Y(T1));
else
assign T1 = A[A_WIDTH-1];
MUXF7 fpga_hard_mux (.I0(T0), .I1(T1), .S(B[2]), .O(Y));
end end
else if (A_WIDTH <= 2 ** 4) begin else if (A_WIDTH <= 4) begin
localparam a_width0 = 2 ** 2; wire [4-1:0] Ax;
localparam num_mux8 = A_WIDTH / a_width0; if (A_WIDTH == 4)
localparam a_widthN = A_WIDTH % a_width0; assign Ax = A;
wire [a_width0-1:0] T;
for (i = 0; i < a_width0; i++)
if (i < num_mux8)
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_mux (.A(A[i*a_width0+:a_width0]), .B(B[2-1:0]), .Y(T[i]));
else if (i == num_mux8 && a_widthN > 1)
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_mux_last (.A(A[A_WIDTH-1-:a_widthN]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[i]));
else else
assign T[i] = A[A_WIDTH-1]; // Rather than extend with 1'bx which gets flattened to 1'b0
\$__XILINX_MUXF78 fpga_hard_mux (.I0(T[0]), .I1(T[1]), .I2(T[2]), .I3(T[3]), .S0(B[2]), .S1(B[3]), .O(Y)); // causing the "don't care" status to get lost, extend with
// the same driver of F7B.I0 so that we can optimise F7B away
// later
assign Ax = {A[1], A};
\$__XILINX_MUXF78 fpga_hard_mux (.I0(Ax[0]), .I1(Ax[2]), .I2(Ax[1]), .I3(Ax[3]), .S0(B[1]), .S1(B[0]), .O(Y));
end
// Note that the following decompositions are 'backwards' in that
// the LSBs are placed on the hard resources, and the soft resources
// are used for MSBs.
// This has the effect of more effectively utilising the hard mux;
// take for example a 5:1 multiplexer, currently this would map as:
//
// A[0] \___ __ A[0] \__ __
// A[4] / \| \ whereas the more A[1] / \| \
// A[1] _____| | obvious mapping A[2] \___| |
// A[2] _____| |-- of MSBs to hard A[3] / | |__
// A[3]______| | resources would A[4] ____| |
// |__/ lead to: 1'bx ____| |
// || |__/
// || ||
// B[1:0] B[1:2]
//
// Expectation would be that the 'forward' mapping (right) is more
// area efficient (consider a 9:1 multiplexer using 2x4:1 multiplexers
// on its I0 and I1 inputs, and A[8] and 1'bx on its I2 and I3 inputs)
// but that the 'backwards' mapping (left) is more delay efficient
// since smaller LUTs are faster than wider ones.
else if (A_WIDTH <= 8) begin
wire [8-1:0] Ax = {{{8-A_WIDTH}{1'bx}}, A};
wire T0 = B[2] ? Ax[4] : Ax[0];
wire T1 = B[2] ? Ax[5] : Ax[1];
wire T2 = B[2] ? Ax[6] : Ax[2];
wire T3 = B[2] ? Ax[7] : Ax[3];
\$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y));
end
else if (A_WIDTH <= 16) begin
wire [16-1:0] Ax = {{{16-A_WIDTH}{1'bx}}, A};
wire T0 = B[2] ? B[3] ? Ax[12] : Ax[4]
: B[3] ? Ax[ 8] : Ax[0];
wire T1 = B[2] ? B[3] ? Ax[13] : Ax[5]
: B[3] ? Ax[ 9] : Ax[1];
wire T2 = B[2] ? B[3] ? Ax[14] : Ax[6]
: B[3] ? Ax[10] : Ax[2];
wire T3 = B[2] ? B[3] ? Ax[15] : Ax[7]
: B[3] ? Ax[11] : Ax[3];
\$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y));
end end
else begin else begin
localparam a_width0 = 2 ** 4; localparam num_mux16 = (A_WIDTH+15) / 16;
localparam num_mux16 = A_WIDTH / a_width0; localparam clog2_num_mux16 = $clog2(num_mux16);
localparam a_widthN = A_WIDTH % a_width0; wire [num_mux16-1:0] T;
wire [num_mux16 + (a_widthN > 0 ? 1 : 0) - 1:0] T; wire [num_mux16*16-1:0] Ax = {{(num_mux16*16-A_WIDTH){1'bx}}, A};
for (i = 0; i < num_mux16; i++) for (i = 0; i < num_mux16; i++)
\$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(4), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux (.A(A[i*a_width0+:a_width0]), .B(B[4-1:0]), .Y(T[i])); \$__XILINX_SHIFTX #(
if (a_widthN > 0) begin .A_SIGNED(A_SIGNED),
if (a_widthN > 1) .B_SIGNED(B_SIGNED),
\$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux_last (.A(A[A_WIDTH-1-:a_widthN]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[num_mux16])); .A_WIDTH(16),
else .B_WIDTH(4),
assign T[num_mux16] = A[A_WIDTH-1]; .Y_WIDTH(Y_WIDTH)
end ) fpga_mux (
\$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(num_mux16 + (a_widthN > 0 ? 1 : 0)), .B_WIDTH(B_WIDTH-4), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(T), .B(B[B_WIDTH-1:4]), .Y(Y)); .A(Ax[i*16+:16]),
.B(B[3:0]),
.Y(T[i])
);
\$__XILINX_SHIFTX #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(num_mux16),
.B_WIDTH(clog2_num_mux16),
.Y_WIDTH(Y_WIDTH)
) _TECHMAP_REPLACE_ (
.A(T),
.B(B[B_WIDTH-1-:clog2_num_mux16]),
.Y(Y));
end end
endgenerate endgenerate
endmodule endmodule
@ -251,15 +301,32 @@ module _90__XILINX_SHIFTX (A, B, Y);
\$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B), .Y(Y)); \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B), .Y(Y));
endmodule endmodule
module \$_MUX_ (A, B, S, Y);
input A, B, S;
output Y;
generate
if (`MIN_MUX_INPUTS == 2)
\$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(2), .B_WIDTH(1), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({B,A}), .B(S), .Y(Y));
else
wire _TECHMAP_FAIL_ = 1;
endgenerate
endmodule
module \$_MUX4_ (A, B, C, D, S, T, Y);
input A, B, C, D, S, T;
output Y;
\$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(4), .B_WIDTH(2), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({D,C,B,A}), .B({T,S}), .Y(Y));
endmodule
module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y); module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y);
input A, B, C, D, E, F, G, H, S, T, U; input A, B, C, D, E, F, G, H, S, T, U;
output Y; output Y;
\$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(8), .B_WIDTH(3), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({H,G,F,E,D,C,B,A}), .B({U,T,S}), .Y(Y)); \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(8), .B_WIDTH(3), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({H,G,F,E,D,C,B,A}), .B({U,T,S}), .Y(Y));
endmodule endmodule
module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y); module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y);
input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V; input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V;
output Y; output Y;
\$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(16), .B_WIDTH(4), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A}), .B({V,U,T,S}), .Y(Y)); \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(16), .B_WIDTH(4), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A}), .B({V,U,T,S}), .Y(Y));
endmodule endmodule
`endif `endif

View File

@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite * yosys -- Yosys Open SYnthesis Suite
* *
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* (C) 2019 Eddie Hung <eddie@fpgeh.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -25,7 +26,7 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
#define XC7_WIRE_DELAY "300" // Number with which ABC will map a 6-input gate #define XC7_WIRE_DELAY 300 // Number with which ABC will map a 6-input gate
// to one LUT6 (instead of a LUT5 + LUT2) // to one LUT6 (instead of a LUT5 + LUT2)
struct SynthXilinxPass : public ScriptPass struct SynthXilinxPass : public ScriptPass
@ -81,8 +82,8 @@ struct SynthXilinxPass : public ScriptPass
log(" do not use DSP48E1s to implement multipliers and associated logic\n"); log(" do not use DSP48E1s to implement multipliers and associated logic\n");
log("\n"); log("\n");
log(" -widemux <int>\n"); log(" -widemux <int>\n");
log(" enable inference of hard multiplexer resources (MuxFx) for muxes at or\n"); log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at or\n");
log(" above this number of inputs (minimum value 5).\n"); log(" above this number of inputs (minimum value 2, recommended value >= 5).\n");
log(" default: 0 (no inference)\n"); log(" default: 0 (no inference)\n");
log("\n"); log("\n");
log(" -run <from_label>:<to_label>\n"); log(" -run <from_label>:<to_label>\n");
@ -198,7 +199,7 @@ struct SynthXilinxPass : public ScriptPass
continue; continue;
} }
if (args[argidx] == "-widemux" && argidx+1 < args.size()) { if (args[argidx] == "-widemux" && argidx+1 < args.size()) {
widemux = atoi(args[++argidx].c_str()); widemux = std::stoi(args[++argidx]);
continue; continue;
} }
if (args[argidx] == "-abc9") { if (args[argidx] == "-abc9") {
@ -214,14 +215,17 @@ struct SynthXilinxPass : public ScriptPass
extra_args(args, argidx, design); extra_args(args, argidx, design);
if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6s") if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6s")
log_cmd_error("Invalid Xilinx -family setting: %s\n", family.c_str()); log_cmd_error("Invalid Xilinx -family setting: '%s'.\n", family.c_str());
if (widemux != 0 && widemux < 5) if (widemux != 0 && widemux < 2)
log_cmd_error("-widemux value must be 0 or >= 5.\n"); log_cmd_error("-widemux value must be 0 or >= 2.\n");
if (!design->full_selection()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); log_cmd_error("This command only operates on fully selected designs!\n");
if (abc9 && retime)
log_cmd_error("-retime option not currently compatible with -abc9!\n");
log_header(design, "Executing SYNTH_XILINX pass.\n"); log_header(design, "Executing SYNTH_XILINX pass.\n");
log_push(); log_push();
@ -254,24 +258,12 @@ struct SynthXilinxPass : public ScriptPass
run("opt_clean"); run("opt_clean");
run("check"); run("check");
run("opt"); run("opt");
run("wreduce"); if (help_mode)
run("wreduce [-keepdc]", "(option for '-widemux')");
else
run("wreduce" + std::string(widemux > 0 ? " -keepdc" : ""));
run("peepopt"); run("peepopt");
run("opt_clean"); run("opt_clean");
run("share");
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
run("opt_expr");
run("opt_clean");
if (!nodsp || help_mode) {
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 -D DSP_NAME=$__MUL25X18");
run("clean");
run("techmap -map +/xilinx/dsp_map.v");
}
run("alumacc");
run("opt");
run("fsm");
run("opt -fast");
run("memory -nomap");
run("opt_clean");
if (widemux > 0 || help_mode) if (widemux > 0 || help_mode)
run("muxpack", " ('-widemux' only)"); run("muxpack", " ('-widemux' only)");
@ -280,8 +272,26 @@ struct SynthXilinxPass : public ScriptPass
// cells for identifying variable-length shift registers, // cells for identifying variable-length shift registers,
// so attempt to convert $pmux-es to the former // so attempt to convert $pmux-es to the former
// Also: wide multiplexer inference benefits from this too // Also: wide multiplexer inference benefits from this too
if (!(nosrl && widemux == 0) || help_mode) if (!(nosrl && widemux == 0) || help_mode) {
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux' < 5)"); run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
run("clean", " (skip if '-nosrl' and '-widemux=0')");
}
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6");
if (!nodsp || help_mode) {
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=25 -D DSP_B_MAXWIDTH=18 -D DSP_NAME=$__MUL25X18");
run("clean");
run("techmap -map +/xilinx/dsp_map.v");
}
run("alumacc");
run("share");
run("opt");
run("fsm");
run("opt -fast");
run("memory -nomap");
run("opt_clean");
} }
if (check_label("bram", "(skip if '-nobram')")) { if (check_label("bram", "(skip if '-nobram')")) {
@ -299,6 +309,10 @@ struct SynthXilinxPass : public ScriptPass
} }
if (check_label("fine")) { if (check_label("fine")) {
if (widemux > 0)
run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover
// performs less efficiently
else
run("opt -fast -full"); run("opt -fast -full");
run("memory_map"); run("memory_map");
run("dffsr2dff"); run("dffsr2dff");
@ -309,23 +323,24 @@ struct SynthXilinxPass : public ScriptPass
} }
else if (widemux > 0) { else if (widemux > 0) {
run("simplemap t:$mux"); run("simplemap t:$mux");
std::string muxcover_args = " -nodecode"; constexpr int cost_mux2 = 100;
std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2);
switch (widemux) { switch (widemux) {
// NB: Cost of mux2 is 100; mux8 should cost between 3 and 4 case 2: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2+1, cost_mux2+2, cost_mux2+3); break;
// of those so that 4:1 muxes and below are implemented case 3:
// out of mux2s case 4: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-2, cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break;
case 5: muxcover_args += " -mux8=350 -mux16=400"; break; case 5:
case 6: muxcover_args += " -mux8=450 -mux16=500"; break; case 6:
case 7: muxcover_args += " -mux8=550 -mux16=600"; break; case 7:
case 8: muxcover_args += " -mux8=650 -mux16=700"; break; case 8: muxcover_args += stringf(" -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break;
case 9: muxcover_args += " -mux16=750"; break; case 9:
case 10: muxcover_args += " -mux16=850"; break; case 10:
case 11: muxcover_args += " -mux16=950"; break; case 11:
case 12: muxcover_args += " -mux16=1050"; break; case 12:
case 13: muxcover_args += " -mux16=1150"; break; case 13:
case 14: muxcover_args += " -mux16=1250"; break; case 14:
case 15: muxcover_args += " -mux16=1350"; break; case 15:
default: muxcover_args += " -mux16=1450"; break; default: muxcover_args += stringf(" -mux16=%d", cost_mux2*(widemux-1)-1); break;
} }
run("muxcover " + muxcover_args); run("muxcover " + muxcover_args);
} }
@ -368,14 +383,14 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("map_luts")) { if (check_label("map_luts")) {
run("opt_expr -mux_undef"); run("opt_expr -mux_undef");
if (help_mode) if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(skip if 'nowidelut', only for '-retime')"); run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')");
else if (abc9) { else if (abc9) {
if (family != "xc7") if (family != "xc7")
log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n"); log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n");
if (nowidelut) if (nowidelut)
run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::string(XC7_WIRE_DELAY) + string(retime ? " -dff" : "")); run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY));
else else
run("abc9 -lut +/xilinx/abc_xc7.lut -box +/xilinx/abc_xc7.box -W " + std::string(XC7_WIRE_DELAY) + string(retime ? " -dff" : "")); run("abc9 -lut +/xilinx/abc_xc7.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY));
} }
else { else {
if (nowidelut) if (nowidelut)

3
tests/simple_abc9/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.v
*.log
*.out

View File

@ -3,3 +3,7 @@ initial o = 1'b0;
always @* always @*
o <= ~o; o <= ~o;
endmodule endmodule
module abc9_test028(input i, output o);
unknown u(~i, o);
endmodule

View File

@ -1,4 +1,6 @@
read_verilog abc9.v read_verilog abc9.v
design -save read
hierarchy -top abc9_test027
proc proc
design -save gold design -save gold
@ -12,3 +14,11 @@ design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -show-ports miter sat -verify -prove-asserts -show-ports miter
design -load read
hierarchy -top abc9_test028
proc
abc9 -lut 4
select -assert-count 1 t:$lut r:LUT=1 r:WIDTH=1 %i %i
select -assert-count 1 t:unknown
select -assert-none t:$lut t:unknown %% t: %D

11
tests/various/async.sh Normal file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -ex
../../yosys -q -o async_syn.v -p 'synth; rename uut syn' async.v
../../yosys -q -o async_prp.v -p 'prep; rename uut prp' async.v
../../yosys -q -o async_a2s.v -p 'prep; async2sync; rename uut a2s' async.v
../../yosys -q -o async_ffl.v -p 'prep; clk2fflogic; rename uut ffl' async.v
iverilog -o async_sim -DTESTBENCH async.v async_???.v
vvp -N async_sim > async.out
tail async.out
grep PASS async.out
rm -f async_???.v async_sim async.out async.vcd

108
tests/various/async.v Normal file
View File

@ -0,0 +1,108 @@
`define MAXQ 2
module uut (
input clk,
input d, r, e,
output [`MAXQ:0] q
);
reg q0;
always @(posedge clk) begin
if (r)
q0 <= 0;
else if (e)
q0 <= d;
end
reg q1;
always @(posedge clk, posedge r) begin
if (r)
q1 <= 0;
else if (e)
q1 <= d;
end
reg q2;
always @(posedge clk, negedge r) begin
if (!r)
q2 <= 0;
else if (!e)
q2 <= d;
end
assign q = {q2, q1, q0};
endmodule
`ifdef TESTBENCH
module \$ff #(
parameter integer WIDTH = 1
) (
input [WIDTH-1:0] D,
output reg [WIDTH-1:0] Q
);
wire sysclk = testbench.sysclk;
always @(posedge sysclk)
Q <= D;
endmodule
module testbench;
reg sysclk;
always #5 sysclk = (sysclk === 1'b0);
reg clk;
always @(posedge sysclk) clk = (clk === 1'b0);
reg d, r, e;
wire [`MAXQ:0] q_uut;
uut uut (.clk(clk), .d(d), .r(r), .e(e), .q(q_uut));
wire [`MAXQ:0] q_syn;
syn syn (.clk(clk), .d(d), .r(r), .e(e), .q(q_syn));
wire [`MAXQ:0] q_prp;
prp prp (.clk(clk), .d(d), .r(r), .e(e), .q(q_prp));
wire [`MAXQ:0] q_a2s;
a2s a2s (.clk(clk), .d(d), .r(r), .e(e), .q(q_a2s));
wire [`MAXQ:0] q_ffl;
ffl ffl (.clk(clk), .d(d), .r(r), .e(e), .q(q_ffl));
task printq;
reg [5*8-1:0] msg;
begin
msg = "OK";
if (q_uut !== q_syn) msg = "SYN";
if (q_uut !== q_prp) msg = "PRP";
if (q_uut !== q_a2s) msg = "A2S";
if (q_uut !== q_ffl) msg = "FFL";
$display("%6t %b %b %b %b %b %s", $time, q_uut, q_syn, q_prp, q_a2s, q_ffl, msg);
if (msg != "OK") $finish;
end
endtask
initial if(0) begin
$dumpfile("async.vcd");
$dumpvars(0, testbench);
end
initial begin
@(posedge clk);
d <= 0;
r <= 0;
e <= 0;
@(posedge clk);
e <= 1;
@(posedge clk);
e <= 0;
repeat (10000) begin
@(posedge clk);
printq;
d <= $random;
r <= $random;
e <= $random;
end
$display("PASS");
$finish;
end
endmodule
`endif

View File

@ -4,11 +4,9 @@ for x in *.ys; do
echo "Running $x.." echo "Running $x.."
../../yosys -ql ${x%.ys}.log $x ../../yosys -ql ${x%.ys}.log $x
done done
# Run any .sh files in this directory (with the exception of the file - run-test.sh for s in *.sh; do
shell_tests=$(echo *.sh | sed -e 's/run-test.sh//') if [ "$s" != "run-test.sh" ]; then
if [ "$shell_tests" ]; then
for s in $shell_tests; do
echo "Running $s.." echo "Running $s.."
bash $s bash $s
done fi
fi done

View File

@ -7,9 +7,11 @@ module test (
if (EN) Q <= D; if (EN) Q <= D;
specify specify
if (EN) (CLK *> (Q : D)) = (1, 2:3:4); `ifndef SKIP_UNSUPPORTED_IGN_PARSER_CONSTRUCTS
if (EN) (posedge CLK *> (Q : D)) = (1, 2:3:4);
$setup(D, posedge CLK &&& EN, 5); $setup(D, posedge CLK &&& EN, 5);
$hold(posedge CLK, D &&& EN, 6); $hold(posedge CLK, D &&& EN, 6);
`endif
endspecify endspecify
endmodule endmodule
@ -28,3 +30,10 @@ module test2 (
(B => Q) = 1.5; (B => Q) = 1.5;
endspecify endspecify
endmodule endmodule
module issue01144(input clk, d, output q);
specify
(posedge clk => (q +: d)) = (3,1);
(posedge clk *> (q +: d)) = (3,1);
endspecify
endmodule

View File

@ -54,3 +54,5 @@ equiv_struct
equiv_induct -seq 5 equiv_induct -seq 5
equiv_status -assert equiv_status -assert
design -reset design -reset
read_verilog -DSKIP_UNSUPPORTED_IGN_PARSER_CONSTRUCTS specify.v