mirror of https://github.com/YosysHQ/yosys.git
Merge remote-tracking branch 'origin/master' into xc7dsp
This commit is contained in:
commit
cea7441d8a
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.v
|
||||||
|
*.log
|
||||||
|
*.out
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue