3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-12 20:18:20 +00:00

Merge remote-tracking branch 'origin/master' into clifford/specify

This commit is contained in:
Eddie Hung 2019-05-03 15:05:57 -07:00
commit d9c4644e88
40 changed files with 964 additions and 438 deletions

View file

@ -294,7 +294,7 @@ endif
PY_WRAPPER_FILE = kernel/python_wrappers PY_WRAPPER_FILE = kernel/python_wrappers
OBJS += $(PY_WRAPPER_FILE).o OBJS += $(PY_WRAPPER_FILE).o
PY_GEN_SCRIPT= py_wrap_generator PY_GEN_SCRIPT= py_wrap_generator
PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
endif endif
ifeq ($(ENABLE_READLINE),1) ifeq ($(ENABLE_READLINE),1)
@ -550,9 +550,9 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
$(Q) mkdir -p $(dir $@) $(Q) mkdir -p $(dir $@)
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P - $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
$(Q) mkdir -p $(dir $@) $(Q) mkdir -p $(dir $@)
$(P) python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" $(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
%.o: %.cpp %.o: %.cpp
$(Q) mkdir -p $(dir $@) $(Q) mkdir -p $(dir $@)
@ -685,7 +685,7 @@ ifeq ($(ENABLE_LIBYOSYS),1)
ifeq ($(ENABLE_PYOSYS),1) ifeq ($(ENABLE_PYOSYS),1)
$(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys $(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys
$(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys $(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys
$(INSTALL_SUDO) cp __init__.py $(PYTHON_DESTDIR)/pyosys $(INSTALL_SUDO) cp misc/__init__.py $(PYTHON_DESTDIR)/pyosys
endif endif
endif endif

View file

@ -163,31 +163,61 @@ struct FirrtlWorker
} }
}; };
/* Memories defined within this module. */ /* Memories defined within this module. */
struct memory { struct memory {
string name; // memory name Cell *pCell; // for error reporting
int abits; // number of address bits string name; // memory name
int size; // size (in units) of the memory int abits; // number of address bits
int width; // size (in bits) of each element int size; // size (in units) of the memory
int read_latency; int width; // size (in bits) of each element
int write_latency; int read_latency;
vector<read_port> read_ports; int write_latency;
vector<write_port> write_ports; vector<read_port> read_ports;
std::string init_file; vector<write_port> write_ports;
std::string init_file_srcFileSpec; std::string init_file;
memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {} std::string init_file_srcFileSpec;
memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} string srcLine;
void add_memory_read_port(read_port &rp) { memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
read_ports.push_back(rp); // Provide defaults for abits or size if one (but not the other) is specified.
} if (this->abits == 0 && this->size != 0) {
void add_memory_write_port(write_port &wp) { this->abits = ceil_log2(this->size);
write_ports.push_back(wp); } else if (this->abits != 0 && this->size == 0) {
} this->size = 1 << this->abits;
void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { }
this->init_file = init_file; // Sanity-check this construction.
this->init_file_srcFileSpec = init_file_srcFileSpec; if (this->name == "") {
log_error("Nameless memory%s\n", this->atLine());
}
if (this->abits == 0 && this->size == 0) {
log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
}
if (this->width == 0) {
log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
}
} }
// We need a default constructor for the dict insert.
memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
}; const char *atLine() {
if (srcLine == "") {
if (pCell) {
auto p = pCell->attributes.find("\\src");
srcLine = " at " + p->second.decode_string();
}
}
return srcLine.c_str();
}
void add_memory_read_port(read_port &rp) {
read_ports.push_back(rp);
}
void add_memory_write_port(write_port &wp) {
write_ports.push_back(wp);
}
void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
this->init_file = init_file;
this->init_file_srcFileSpec = init_file_srcFileSpec;
}
};
dict<string, memory> memories; dict<string, memory> memories;
void register_memory(memory &m) void register_memory(memory &m)
@ -604,7 +634,7 @@ struct FirrtlWorker
int abits = cell->parameters.at("\\ABITS").as_int(); int abits = cell->parameters.at("\\ABITS").as_int();
int width = cell->parameters.at("\\WIDTH").as_int(); int width = cell->parameters.at("\\WIDTH").as_int();
int size = cell->parameters.at("\\SIZE").as_int(); int size = cell->parameters.at("\\SIZE").as_int();
memory m(mem_id, abits, size, width); memory m(cell, mem_id, abits, size, width);
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
@ -681,6 +711,8 @@ struct FirrtlWorker
{ {
std::string cell_type = fid(cell->type); std::string cell_type = fid(cell->type);
std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string()); std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
int abits = cell->parameters.at("\\ABITS").as_int();
int width = cell->parameters.at("\\WIDTH").as_int();
memory *mp = nullptr; memory *mp = nullptr;
if (cell->type == "$meminit" ) { if (cell->type == "$meminit" ) {
log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
@ -693,6 +725,11 @@ struct FirrtlWorker
Const clk_enable = cell->parameters.at("\\CLK_ENABLE"); Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
Const clk_polarity = cell->parameters.at("\\CLK_POLARITY"); Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
// Do we already have an entry for this memory?
if (memories.count(mem_id) == 0) {
memory m(cell, mem_id, abits, 0, width);
register_memory(m);
}
mp = &memories.at(mem_id); mp = &memories.at(mem_id);
int portNum = 0; int portNum = 0;
bool transparency = false; bool transparency = false;
@ -890,7 +927,7 @@ struct FirrtlWorker
// If we have any memory definitions, output them. // If we have any memory definitions, output them.
for (auto kv : memories) { for (auto kv : memories) {
memory m = kv.second; memory &m = kv.second;
f << stringf(" mem %s:\n", m.name.c_str()); f << stringf(" mem %s:\n", m.name.c_str());
f << stringf(" data-type => UInt<%d>\n", m.width); f << stringf(" data-type => UInt<%d>\n", m.width);
f << stringf(" depth => %d\n", m.size); f << stringf(" depth => %d\n", m.size);

View file

@ -645,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (!id_ast->children[0]->range_valid) if (!id_ast->children[0]->range_valid)
log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str()); log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
if (children.size() > 1)
range = children[1];
} else } else
log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str()); log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
if (range) { if (range) {

View file

@ -1172,6 +1172,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
varbuf->children[0] = buf; varbuf->children[0] = buf;
} }
#if 0
if (type == AST_FOR) {
AstNode *buf = next_ast->clone();
delete buf->children[1];
buf->children[1] = varbuf->children[0]->clone();
current_block->children.insert(current_block->children.begin() + current_block_idx++, buf);
}
#endif
current_scope[varbuf->str] = backup_scope_varbuf; current_scope[varbuf->str] = backup_scope_varbuf;
delete varbuf; delete varbuf;
delete_children(); delete_children();
@ -1598,6 +1607,7 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_tmp->str] = wire_tmp; current_scope[wire_tmp->str] = wire_tmp;
wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false); wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { } while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
wire_tmp->is_logic = true;
AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
wire_tmp_id->str = wire_tmp->str; wire_tmp_id->str = wire_tmp->str;

View file

@ -246,8 +246,6 @@ struct VerilogFrontend : public Frontend {
specify_mode = false; specify_mode = false;
default_nettype_wire = true; default_nettype_wire = true;
log_header(design, "Executing Verilog-2005 frontend.\n");
args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
size_t argidx; size_t argidx;
@ -423,6 +421,8 @@ struct VerilogFrontend : public Frontend {
} }
extra_args(f, filename, args, argidx); extra_args(f, filename, args, argidx);
log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str());
log("Parsing %s%s input from `%s' to AST representation.\n", log("Parsing %s%s input from `%s' to AST representation.\n",
formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());

View file

@ -529,13 +529,13 @@ int main(int argc, char **argv)
log_error("Can't open dependencies file for writing: %s\n", strerror(errno)); log_error("Can't open dependencies file for writing: %s\n", strerror(errno));
bool first = true; bool first = true;
for (auto fn : yosys_output_files) { for (auto fn : yosys_output_files) {
fprintf(f, "%s%s", first ? "" : " ", fn.c_str()); fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str());
first = false; first = false;
} }
fprintf(f, ":"); fprintf(f, ":");
for (auto fn : yosys_input_files) { for (auto fn : yosys_input_files) {
if (yosys_output_files.count(fn) == 0) if (yosys_output_files.count(fn) == 0)
fprintf(f, " %s", fn.c_str()); fprintf(f, " %s", escape_filename_spaces(fn).c_str());
} }
fprintf(f, "\n"); fprintf(f, "\n");
} }

View file

@ -3496,7 +3496,7 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
pack(); pack();
other.pack(); other.pack();
if (chunks_.size() != chunks_.size()) if (chunks_.size() != other.chunks_.size())
return false; return false;
updhash(); updhash();

View file

@ -482,6 +482,20 @@ void remove_directory(std::string dirname)
#endif #endif
} }
std::string escape_filename_spaces(const std::string& filename)
{
std::string out;
out.reserve(filename.size());
for (auto c : filename)
{
if (c == ' ')
out += "\\ ";
else
out.push_back(c);
}
return out;
}
int GetSize(RTLIL::Wire *wire) int GetSize(RTLIL::Wire *wire)
{ {
return wire->width; return wire->width;

View file

@ -257,6 +257,7 @@ std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX");
bool check_file_exists(std::string filename, bool is_exec = false); bool check_file_exists(std::string filename, bool is_exec = false);
bool is_absolute_path(std::string filename); bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname); void remove_directory(std::string dirname);
std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); } template<typename T> int GetSize(const T &obj) { return obj.size(); }
int GetSize(RTLIL::Wire *wire); int GetSize(RTLIL::Wire *wire);

View file

@ -291,7 +291,7 @@ struct QwpWorker
// gaussian elimination // gaussian elimination
for (int i = 0; i < N; i++) for (int i = 0; i < N; i++)
{ {
if (config.verbose && ((i+1) % (N/15)) == 0) if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
// find best row // find best row

View file

@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass
log(" useful for handling architecture-specific primitives.\n"); log(" useful for handling architecture-specific primitives.\n");
log("\n"); log("\n");
log(" -assert\n"); log(" -assert\n");
log(" produce an error if the circuits are not equivalent\n"); log(" produce an error if the circuits are not equivalent.\n");
log("\n");
log(" -undef\n");
log(" enable modelling of undef states during equiv_induct.\n");
log("\n"); log("\n");
log("The following commands are executed by this verification command:\n"); log("The following commands are executed by this verification command:\n");
help_script(); help_script();
@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass
} }
std::string command, techmap_opts; std::string command, techmap_opts;
bool assert; bool assert, undef;
void clear_flags() YS_OVERRIDE void clear_flags() YS_OVERRIDE
{ {
command = ""; command = "";
techmap_opts = ""; techmap_opts = "";
assert = false; assert = false;
undef = false;
} }
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass
assert = true; assert = true;
continue; continue;
} }
if (args[argidx] == "-undef") {
undef = true;
continue;
}
break; break;
} }
@ -139,7 +147,12 @@ struct EquivOptPass:public ScriptPass
if (check_label("prove")) { if (check_label("prove")) {
run("equiv_make gold gate equiv"); run("equiv_make gold gate equiv");
run("equiv_induct equiv"); if (help_mode)
run("equiv_induct [-undef] equiv");
else if (undef)
run("equiv_induct -undef equiv");
else
run("equiv_induct equiv");
if (help_mode) if (help_mode)
run("equiv_status [-assert] equiv"); run("equiv_status [-assert] equiv");
else if (assert) else if (assert)

View file

@ -274,37 +274,53 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
std::vector<RTLIL::Wire*> maybe_del_wires; std::vector<RTLIL::Wire*> maybe_del_wires;
for (auto wire : module->wires()) for (auto wire : module->wires())
{ {
SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
log_assert(GetSize(s1) == GetSize(s2));
bool maybe_del = false;
if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep"))
assign_map.apply(s2); maybe_del = true;
if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
maybe_del_wires.push_back(wire);
} else {
log_assert(GetSize(s1) == GetSize(s2));
RTLIL::SigSig new_conn;
for (int i = 0; i < GetSize(s1); i++)
if (s1[i] != s2[i]) {
new_conn.first.append_bit(s1[i]);
new_conn.second.append_bit(s2[i]);
}
if (new_conn.first.size() > 0) {
used_signals.add(new_conn.first);
used_signals.add(new_conn.second);
module->connect(new_conn);
}
}
} else { } else {
if (!used_signals.check_any(RTLIL::SigSpec(wire))) if (!used_signals.check_any(s2))
maybe_del_wires.push_back(wire); maybe_del = true;
} }
RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); if (maybe_del) {
if (!used_signals_nodrivers.check_any(sig)) { maybe_del_wires.push_back(wire);
} else {
Const initval;
if (wire->attributes.count("\\init"))
initval = wire->attributes.at("\\init");
if (GetSize(initval) != GetSize(wire))
initval.bits.resize(GetSize(wire), State::Sx);
RTLIL::SigSig new_conn;
for (int i = 0; i < GetSize(s1); i++)
if (s1[i] != s2[i]) {
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
s2[i] = initval[i];
initval[i] = State::Sx;
}
new_conn.first.append_bit(s1[i]);
new_conn.second.append_bit(s2[i]);
}
if (new_conn.first.size() > 0) {
if (initval.is_fully_undef())
wire->attributes.erase("\\init");
else
wire->attributes.at("\\init") = initval;
used_signals.add(new_conn.first);
used_signals.add(new_conn.second);
module->connect(new_conn);
}
}
if (!used_signals_nodrivers.check_all(s2)) {
std::string unused_bits; std::string unused_bits;
for (int i = 0; i < GetSize(sig); i++) { for (int i = 0; i < GetSize(s2); i++) {
if (sig[i].wire == NULL) if (s2[i].wire == NULL)
continue; continue;
if (!used_signals_nodrivers.check(sig[i])) { if (!used_signals_nodrivers.check(s2[i])) {
if (!unused_bits.empty()) if (!unused_bits.empty())
unused_bits += " "; unused_bits += " ";
unused_bits += stringf("%d", i); unused_bits += stringf("%d", i);
@ -323,14 +339,40 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
pool<RTLIL::Wire*> del_wires; pool<RTLIL::Wire*> del_wires;
int del_wires_count = 0; int del_wires_count = 0;
for (auto wire : maybe_del_wires) for (auto wire : maybe_del_wires) {
if (!used_signals.check_any(RTLIL::SigSpec(wire))) { SigSpec s1 = SigSpec(wire);
if (check_public_name(wire->name) && verbose) { if (used_signals.check_any(s1)) {
SigSpec s2 = assign_map(s1);
Const initval;
if (wire->attributes.count("\\init"))
initval = wire->attributes.at("\\init");
if (GetSize(initval) != GetSize(wire))
initval.bits.resize(GetSize(wire), State::Sx);
RTLIL::SigSig new_conn;
for (int i = 0; i < GetSize(s1); i++)
if (s1[i] != s2[i]) {
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
s2[i] = initval[i];
initval[i] = State::Sx;
}
new_conn.first.append_bit(s1[i]);
new_conn.second.append_bit(s2[i]);
}
if (new_conn.first.size() > 0) {
if (initval.is_fully_undef())
wire->attributes.erase("\\init");
else
wire->attributes.at("\\init") = initval;
module->connect(new_conn);
}
} else {
if (ys_debug() || (check_public_name(wire->name) && verbose)) {
log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); log_debug(" removing unused non-port wire %s.\n", wire->name.c_str());
} }
del_wires.insert(wire); del_wires.insert(wire);
del_wires_count++; del_wires_count++;
} }
}
module->remove(del_wires); module->remove(del_wires);
count_rm_wires += del_wires.size(); count_rm_wires += del_wires.size();
@ -483,6 +525,9 @@ struct OptCleanPass : public Pass {
ct_all.setup(design); ct_all.setup(design);
count_rm_cells = 0;
count_rm_wires = 0;
for (auto module : design->selected_whole_modules_warn()) { for (auto module : design->selected_whole_modules_warn()) {
if (module->has_processes_warn()) if (module->has_processes_warn())
continue; continue;
@ -548,7 +593,7 @@ struct CleanPass : public Pass {
for (auto module : design->selected_whole_modules()) { for (auto module : design->selected_whole_modules()) {
if (module->has_processes()) if (module->has_processes())
continue; continue;
rmunused_module(module, purge_mode, false, false); rmunused_module(module, purge_mode, ys_debug(), false);
} }
log_suppressed(); log_suppressed();

View file

@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
SigPool used_signals; SigPool used_signals;
SigPool all_signals; SigPool all_signals;
dict<SigBit, pair<Wire*, State>> initbits;
pool<Wire*> revisit_initwires;
for (auto cell : module->cells()) for (auto cell : module->cells())
for (auto &conn : cell->connections()) { for (auto &conn : cell->connections()) {
if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
} }
for (auto wire : module->wires()) { for (auto wire : module->wires()) {
if (wire->attributes.count("\\init")) {
SigSpec sig = sigmap(wire);
Const initval = wire->attributes.at("\\init");
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
if (initval[i] == State::S0 || initval[i] == State::S1)
initbits[sig[i]] = make_pair(wire, initval[i]);
}
}
if (wire->port_input) if (wire->port_input)
driven_signals.add(sigmap(wire)); driven_signals.add(sigmap(wire));
if (wire->port_output) if (wire->port_output || wire->get_bool_attribute("\\keep"))
used_signals.add(sigmap(wire)); used_signals.add(sigmap(wire));
all_signals.add(sigmap(wire)); all_signals.add(sigmap(wire));
} }
@ -67,10 +78,43 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
if (sig.size() == 0) if (sig.size() == 0)
continue; continue;
log_debug("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c)); Const val(RTLIL::State::Sx, GetSize(sig));
module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); for (int i = 0; i < GetSize(sig); i++) {
SigBit bit = sigmap(sig[i]);
auto cursor = initbits.find(bit);
if (cursor != initbits.end()) {
revisit_initwires.insert(cursor->second.first);
val[i] = cursor->second.second;
}
}
log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
module->connect(sig, val);
did_something = true; did_something = true;
} }
if (!revisit_initwires.empty())
{
SigMap sm2(module);
for (auto wire : revisit_initwires) {
SigSpec sig = sm2(wire);
Const initval = wire->attributes.at("\\init");
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
if (SigBit(initval[i]) == sig[i])
initval[i] = State::Sx;
}
if (initval.is_fully_undef()) {
log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
wire->attributes.erase("\\init");
did_something = true;
} else if (initval != wire->attributes.at("\\init")) {
log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
wire->attributes["\\init"] = initval;
did_something = true;
}
}
}
} }
void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)

View file

@ -180,6 +180,8 @@ struct WreduceWorker
} }
auto info = mi.query(sig_q[i]); auto info = mi.query(sig_q[i]);
if (info == nullptr)
return;
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
remove_init_bits.insert(sig_q[i]); remove_init_bits.insert(sig_q[i]);
sig_d.remove(i); sig_d.remove(i);
@ -529,6 +531,42 @@ struct WreducePass : public Pass {
module->connect(sig, Const(0, GetSize(sig))); module->connect(sig, Const(0, GetSize(sig)));
} }
} }
if (c->type.in("$div", "$mod", "$pow"))
{
SigSpec A = c->getPort("\\A");
int original_a_width = GetSize(A);
if (c->getParam("\\A_SIGNED").as_bool()) {
while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
A.remove(GetSize(A)-1, 1);
} else {
while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
A.remove(GetSize(A)-1, 1);
}
if (original_a_width != GetSize(A)) {
log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
c->setPort("\\A", A);
c->setParam("\\A_WIDTH", GetSize(A));
}
SigSpec B = c->getPort("\\B");
int original_b_width = GetSize(B);
if (c->getParam("\\B_SIGNED").as_bool()) {
while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
B.remove(GetSize(B)-1, 1);
} else {
while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
B.remove(GetSize(B)-1, 1);
}
if (original_b_width != GetSize(B)) {
log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
c->setPort("\\B", B);
c->setParam("\\B_WIDTH", GetSize(B));
}
}
if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) { if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
IdString memid = c->getParam("\\MEMID").decode_string(); IdString memid = c->getParam("\\MEMID").decode_string();
RTLIL::Memory *mem = module->memories.at(memid); RTLIL::Memory *mem = module->memories.at(memid);

View file

@ -1 +1,2 @@
/ice40_dsp_pm.h /ice40_dsp_pm.h
/peepopt_pm.h

View file

@ -1,8 +1,23 @@
OBJS += passes/pmgen/ice40_dsp.o OBJS += passes/pmgen/ice40_dsp.o
OBJS += passes/pmgen/peepopt.o
# --------------------------------------
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
.SECONDARY: passes/pmgen/ice40_dsp_pm.h .SECONDARY: passes/pmgen/ice40_dsp_pm.h
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
$(P) mkdir -p passes/pmgen && python3 $^ $@ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
# --------------------------------------
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
EXTRA_OBJS += passes/pmgen/peepopt_pm.h
.SECONDARY: passes/pmgen/peepopt_pm.h
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)

View file

@ -29,19 +29,25 @@ up in any future matches:
pm.blacklist(some_cell); pm.blacklist(some_cell);
The `.run(callback_function)` method searches for all matches and calls the The `.run_<pattern_name>(callback_function)` method searches for all matches
callback function for each found match: for the pattern`<pattern_name>` and calls the callback function for each found
match:
pm.run([&](){ pm.run_foobar([&](){
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
log(" with 'bar' cell: %s\n", log_id(pm.st.bar)); log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
}); });
The `.pmg` file declares matcher state variables that are accessible via the The `.pmg` file declares matcher state variables that are accessible via the
`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) `.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is
of type `foobar_pm::state_<pattern_name>_t`.)
Similarly the `.pmg` file declares user data variables that become members of Similarly the `.pmg` file declares user data variables that become members of
`.ud`, a struct of type `foobar_pm::udata_t`. `.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
There are four versions of the `run_<pattern_name>()` method: Without callback,
callback without arguments, callback with reference to `pm`, and callback with
reference to `pm.st_<pattern_name>`.
The .pmg File Format The .pmg File Format
@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens.
Lines in `.pmg` files starting with `//` are comments. Lines in `.pmg` files starting with `//` are comments.
Declaring a pattern
-------------------
A `.pmg` file contains one or more patterns. Each pattern starts with a line
with the `pattern` keyword followed by the name of the pattern.
Declaring state variables Declaring state variables
------------------------- -------------------------
@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith
and saved and restored as needed. and saved and restored as needed.
They are automatically initialized to the default constructed value of their type They are automatically initialized to the default constructed value of their type
when `.run(callback_function)` is called. when `.run_<pattern_name>(callback_function)` is called.
Declaring udata variables Declaring udata variables
------------------------- -------------------------
@ -220,5 +232,5 @@ But in some cases it is more natural to utilize the implicit branch statement:
portAB = \B; portAB = \B;
endcode endcode
There is an implicit `code..endcode` block at the end of each `.pgm` file There is an implicit `code..endcode` block at the end of each `.pmg` file
that just accepts everything that gets all the way there. that just accepts everything that gets all the way there.

View file

@ -19,47 +19,50 @@
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "passes/pmgen/ice40_dsp_pm.h"
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
#include "passes/pmgen/ice40_dsp_pm.h"
void create_ice40_dsp(ice40_dsp_pm &pm) void create_ice40_dsp(ice40_dsp_pm &pm)
{ {
auto &st = pm.st_ice40_dsp;
#if 0 #if 0
log("\n"); log("\n");
log("ffA: %s\n", log_id(pm.st.ffA, "--")); log("ffA: %s\n", log_id(st.ffA, "--"));
log("ffB: %s\n", log_id(pm.st.ffB, "--")); log("ffB: %s\n", log_id(st.ffB, "--"));
log("mul: %s\n", log_id(pm.st.mul, "--")); log("mul: %s\n", log_id(st.mul, "--"));
log("ffY: %s\n", log_id(pm.st.ffY, "--")); log("ffY: %s\n", log_id(st.ffY, "--"));
log("addAB: %s\n", log_id(pm.st.addAB, "--")); log("addAB: %s\n", log_id(st.addAB, "--"));
log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); log("muxAB: %s\n", log_id(st.muxAB, "--"));
log("ffS: %s\n", log_id(pm.st.ffS, "--")); log("ffS: %s\n", log_id(st.ffS, "--"));
#endif #endif
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
if (GetSize(pm.st.sigA) > 16) { if (GetSize(st.sigA) > 16) {
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
return; return;
} }
if (GetSize(pm.st.sigB) > 16) { if (GetSize(st.sigB) > 16) {
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
return; return;
} }
if (GetSize(pm.st.sigS) > 32) { if (GetSize(st.sigS) > 32) {
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
return; return;
} }
if (GetSize(pm.st.sigY) > 32) { if (GetSize(st.sigY) > 32) {
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
return; return;
} }
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
if (mul_signed) { if (mul_signed) {
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n"); log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log(" replacing $mul with SB_MAC16 cell.\n"); log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
pm.module->swap_names(cell, pm.st.mul); pm.module->swap_names(cell, st.mul);
// SB_MAC16 Input Interface // SB_MAC16 Input Interface
SigSpec A = pm.st.sigA; SigSpec A = st.sigA;
A.extend_u0(16, mul_signed); A.extend_u0(16, mul_signed);
SigSpec B = pm.st.sigB; SigSpec B = st.sigB;
B.extend_u0(16, mul_signed); B.extend_u0(16, mul_signed);
SigSpec CD; SigSpec CD;
if (pm.st.muxA) if (st.muxA)
CD = pm.st.muxA->getPort("\\B"); CD = st.muxA->getPort("\\B");
if (pm.st.muxB) if (st.muxB)
CD = pm.st.muxB->getPort("\\A"); CD = st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed); CD.extend_u0(32, mul_signed);
cell->setPort("\\A", A); cell->setPort("\\A", A);
@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\C", CD.extract(0, 16)); cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16)); cell->setPort("\\D", CD.extract(16, 16));
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0); cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0); cell->setPort("\\BHOLD", State::S0);
@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\IRSTTOP", State::S0); cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0); cell->setPort("\\IRSTBOT", State::S0);
if (pm.st.clock_vld) if (st.clock_vld)
{ {
cell->setPort("\\CLK", pm.st.clock); cell->setPort("\\CLK", st.clock);
cell->setPort("\\CE", State::S1); cell->setPort("\\CE", State::S1);
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
if (pm.st.ffA) if (st.ffA)
log(" ffA:%s", log_id(pm.st.ffA)); log(" ffA:%s", log_id(st.ffA));
if (pm.st.ffB) if (st.ffB)
log(" ffB:%s", log_id(pm.st.ffB)); log(" ffB:%s", log_id(st.ffB));
if (pm.st.ffY) if (st.ffY)
log(" ffY:%s", log_id(pm.st.ffY)); log(" ffY:%s", log_id(st.ffY));
if (pm.st.ffS) if (st.ffS)
log(" ffS:%s", log_id(pm.st.ffS)); log(" ffS:%s", log_id(st.ffS));
log("\n"); log("\n");
} }
@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
// SB_MAC16 Output Interface // SB_MAC16 Output Interface
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; SigSpec O = st.ffS ? st.sigS : st.sigY;
if (GetSize(O) < 32) if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O); cell->setPort("\\O", O);
if (pm.st.addAB) { if (st.addAB) {
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
} else { } else {
cell->setPort("\\ADDSUBTOP", State::S0); cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0); cell->setPort("\\ADDSUBBOT", State::S0);
@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\OHOLDBOT", State::S0); cell->setPort("\\OHOLDBOT", State::S0);
SigSpec acc_reset = State::S0; SigSpec acc_reset = State::S0;
if (pm.st.muxA) if (st.muxA)
acc_reset = pm.st.muxA->getPort("\\S"); acc_reset = st.muxA->getPort("\\S");
if (pm.st.muxB) if (st.muxB)
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset); cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset); cell->setPort("\\OLOADBOT", acc_reset);
@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\C_REG", State::S0); cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0); cell->setParam("\\D_REG", State::S0);
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
pm.autoremove(pm.st.mul); pm.autoremove(st.mul);
pm.autoremove(pm.st.ffY); pm.autoremove(st.ffY);
pm.autoremove(pm.st.ffS); pm.autoremove(st.ffS);
} }
struct Ice40DspPass : public Pass { struct Ice40DspPass : public Pass {
@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
} }
} Ice40DspPass; } Ice40DspPass;

View file

@ -1,3 +1,5 @@
pattern ice40_dsp
state <SigBit> clock state <SigBit> clock
state <bool> clock_pol clock_vld state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS state <SigSpec> sigA sigB sigY sigS

68
passes/pmgen/peepopt.cc Normal file
View file

@ -0,0 +1,68 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
bool did_something;
#include "passes/pmgen/peepopt_pm.h"
struct PeepoptPass : public Pass {
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" peepopt [options] [selection]\n");
log("\n");
log("This pass applies a collection of peephole optimizers to the current design.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-singleton") {
// singleton_mode = true;
// continue;
// }
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
did_something = true;
while (did_something) {
did_something = false;
peepopt_pm pm(module, module->selected_cells());
pm.run_shiftmul();
pm.run_muldiv();
}
}
}
} PeepoptPass;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,36 @@
pattern muldiv
state <SigSpec> t x y
match mul
select mul->type == $mul
select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
endmatch
code t x y
t = port(mul, \Y);
x = port(mul, \A);
y = port(mul, \B);
branch;
std::swap(x, y);
endcode
match div
select div->type.in($div)
index <SigSpec> port(div, \A) === t
index <SigSpec> port(div, \B) === x
endmatch
code
SigSpec div_y = port(div, \Y);
SigSpec val_y = y;
if (GetSize(div_y) != GetSize(val_y))
val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool());
did_something = true;
log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
module->connect(div_y, val_y);
autoremove(div);
reject;
endcode

View file

@ -0,0 +1,83 @@
pattern shiftmul
state <SigSpec> shamt
match shift
select shift->type.in($shift, $shiftx, $shr)
endmatch
code shamt
shamt = port(shift, \B);
if (shamt[GetSize(shamt)-1] == State::S0) {
do {
shamt.remove(GetSize(shamt)-1);
} while (shamt[GetSize(shamt)-1] == State::S0);
} else
if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
reject;
}
if (GetSize(shamt) > 20)
reject;
endcode
match mul
select mul->type.in($mul)
select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
index <SigSpec> port(mul, \Y) === shamt
endmatch
code
IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
Const const_factor_cnst = port(mul, const_factor_port).as_const();
int const_factor = const_factor_cnst.as_int();
if (GetSize(const_factor_cnst) == 0)
reject;
if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
param(mul, const_factor_signed).as_bool())
reject;
if (GetSize(const_factor_cnst) > 20)
reject;
if (GetSize(port(shift, \Y)) > const_factor)
reject;
did_something = true;
log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
int new_const_factor_log2 = ceil_log2(const_factor);
int new_const_factor = 1 << new_const_factor_log2;
SigSpec padding(State::Sx, new_const_factor-const_factor);
SigSpec old_a = port(shift, \A), new_a;
int trunc = 0;
if (GetSize(old_a) % const_factor != 0) {
trunc = const_factor - GetSize(old_a) % const_factor;
old_a.append(SigSpec(State::Sx, trunc));
}
for (int i = 0; i*const_factor < GetSize(old_a); i++) {
SigSpec slice = old_a.extract(i*const_factor, const_factor);
new_a.append(slice);
new_a.append(padding);
}
if (trunc > 0)
new_a.remove(GetSize(new_a)-trunc, trunc);
SigSpec new_b = {port(mul, const_factor_port == \A ? \B : \A), SigSpec(State::S0, new_const_factor_log2)};
if (param(shift, \B_SIGNED).as_bool())
new_b.append(State::S0);
shift->setPort(\A, new_a);
shift->setParam(\A_WIDTH, GetSize(new_a));
shift->setPort(\B, new_b);
shift->setParam(\B_WIDTH, GetSize(new_b));
blacklist(shift);
reject;
endcode

View file

@ -3,15 +3,42 @@
import re import re
import sys import sys
import pprint import pprint
import getopt
pp = pprint.PrettyPrinter(indent=4) pp = pprint.PrettyPrinter(indent=4)
pmgfile = sys.argv[1] prefix = None
assert pmgfile.endswith(".pmg") pmgfiles = list()
prefix = pmgfile[0:-4] outfile = None
prefix = prefix.split('/')[-1] debug = False
outfile = sys.argv[2] genhdr = False
opts, args = getopt.getopt(sys.argv[1:], "p:o:dg")
for o, a in opts:
if o == "-p":
prefix = a
elif o == "-o":
outfile = a
elif o == "-d":
debug = True
elif o == "-g":
genhdr = True
if outfile is None:
outfile = "/dev/stdout"
for a in args:
assert a.endswith(".pmg")
if prefix is None and len(args) == 1:
prefix = a[0:-4]
prefix = prefix.split('/')[-1]
pmgfiles.append(a)
assert prefix is not None
current_pattern = None
patterns = dict()
state_types = dict() state_types = dict()
udata_types = dict() udata_types = dict()
blocks = list() blocks = list()
@ -77,7 +104,8 @@ def rewrite_cpp(s):
return "".join(t) return "".join(t)
with open(pmgfile, "r") as f: def process_pmgfile(f):
global current_pattern
while True: while True:
line = f.readline() line = f.readline()
if line == "": break if line == "": break
@ -87,14 +115,31 @@ with open(pmgfile, "r") as f:
if len(cmd) == 0 or cmd[0].startswith("//"): continue if len(cmd) == 0 or cmd[0].startswith("//"): continue
cmd = cmd[0] cmd = cmd[0]
if cmd == "pattern":
if current_pattern is not None:
block = dict()
block["type"] = "final"
block["pattern"] = current_pattern
blocks.append(block)
line = line.split()
assert len(line) == 2
assert line[1] not in patterns
current_pattern = line[1]
patterns[current_pattern] = len(blocks)
state_types[current_pattern] = dict()
udata_types[current_pattern] = dict()
continue
assert current_pattern is not None
if cmd == "state": if cmd == "state":
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
assert m assert m
type_str = m.group(1) type_str = m.group(1)
states_str = m.group(2) states_str = m.group(2)
for s in re.split(r"\s+", states_str): for s in re.split(r"\s+", states_str):
assert s not in state_types assert s not in state_types[current_pattern]
state_types[s] = type_str state_types[current_pattern][s] = type_str
continue continue
if cmd == "udata": if cmd == "udata":
@ -103,19 +148,20 @@ with open(pmgfile, "r") as f:
type_str = m.group(1) type_str = m.group(1)
udatas_str = m.group(2) udatas_str = m.group(2)
for s in re.split(r"\s+", udatas_str): for s in re.split(r"\s+", udatas_str):
assert s not in udata_types assert s not in udata_types[current_pattern]
udata_types[s] = type_str udata_types[current_pattern][s] = type_str
continue continue
if cmd == "match": if cmd == "match":
block = dict() block = dict()
block["type"] = "match" block["type"] = "match"
block["pattern"] = current_pattern
line = line.split() line = line.split()
assert len(line) == 2 assert len(line) == 2
assert line[1] not in state_types assert line[1] not in state_types[current_pattern]
block["cell"] = line[1] block["cell"] = line[1]
state_types[line[1]] = "Cell*"; state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list() block["if"] = list()
block["select"] = list() block["select"] = list()
@ -158,15 +204,18 @@ with open(pmgfile, "r") as f:
assert False assert False
blocks.append(block) blocks.append(block)
continue
if cmd == "code": if cmd == "code":
block = dict() block = dict()
block["type"] = "code" block["type"] = "code"
block["pattern"] = current_pattern
block["code"] = list() block["code"] = list()
block["states"] = set() block["states"] = set()
for s in line.split()[1:]: for s in line.split()[1:]:
assert s in state_types assert s in state_types[current_pattern]
block["states"].add(s) block["states"].add(s)
while True: while True:
@ -179,17 +228,36 @@ with open(pmgfile, "r") as f:
block["code"].append(rewrite_cpp(l.rstrip())) block["code"].append(rewrite_cpp(l.rstrip()))
blocks.append(block) blocks.append(block)
continue
assert False
for fn in pmgfiles:
with open(fn, "r") as f:
process_pmgfile(f)
if current_pattern is not None:
block = dict()
block["type"] = "final"
block["pattern"] = current_pattern
blocks.append(block)
current_pattern = None
if debug:
pp.pprint(blocks)
with open(outfile, "w") as f: with open(outfile, "w") as f:
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) for fn in pmgfiles:
print("// Generated by pmgen.py from {}".format(fn), file=f)
print("", file=f) print("", file=f)
print("#include \"kernel/yosys.h\"", file=f) if genhdr:
print("#include \"kernel/sigtools.h\"", file=f) print("#include \"kernel/yosys.h\"", file=f)
print("", file=f) print("#include \"kernel/sigtools.h\"", file=f)
print("", file=f)
print("YOSYS_NAMESPACE_BEGIN", file=f) print("YOSYS_NAMESPACE_BEGIN", file=f)
print("", file=f) print("", file=f)
print("struct {}_pm {{".format(prefix), file=f) print("struct {}_pm {{".format(prefix), file=f)
print(" Module *module;", file=f) print(" Module *module;", file=f)
@ -212,17 +280,19 @@ with open(outfile, "w") as f:
print(" int rollback;", file=f) print(" int rollback;", file=f)
print("", file=f) print("", file=f)
print(" struct state_t {", file=f) for current_pattern in sorted(patterns.keys()):
for s, t in sorted(state_types.items()): print(" struct state_{}_t {{".format(current_pattern), file=f)
print(" {} {};".format(t, s), file=f) for s, t in sorted(state_types[current_pattern].items()):
print(" } st;", file=f) print(" {} {};".format(t, s), file=f)
print("", file=f) print(" }} st_{};".format(current_pattern), file=f)
print("", file=f)
print(" struct udata_t {", file=f) print(" struct udata_{}_t {{".format(current_pattern), file=f)
for s, t in sorted(udata_types.items()): for s, t in sorted(udata_types[current_pattern].items()):
print(" {} {};".format(t, s), file=f) print(" {} {};".format(t, s), file=f)
print(" } ud;", file=f) print(" }} ud_{};".format(current_pattern), file=f)
print("", file=f) print("", file=f)
current_pattern = None
for v, n in sorted(ids.items()): for v, n in sorted(ids.items()):
if n[0] == "\\": if n[0] == "\\":
@ -258,20 +328,24 @@ with open(outfile, "w") as f:
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void check_blacklist() {", file=f) for current_pattern in sorted(patterns.keys()):
print(" if (!blacklist_dirty)", file=f) print(" void check_blacklist_{}() {{".format(current_pattern), file=f)
print(" return;", file=f) print(" if (!blacklist_dirty)", file=f)
print(" blacklist_dirty = false;", file=f) print(" return;", file=f)
for index in range(len(blocks)): print(" blacklist_dirty = false;", file=f)
block = blocks[index] for index in range(len(blocks)):
if block["type"] == "match": block = blocks[index]
print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) if block["pattern"] != current_pattern:
print(" rollback = {};".format(index+1), file=f) continue
print(" return;", file=f) if block["type"] == "match":
print(" }", file=f) print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
print(" rollback = 0;", file=f) print(" rollback = {};".format(index+1), file=f)
print(" }", file=f) print(" return;", file=f)
print("", file=f) print(" }", file=f)
print(" rollback = 0;", file=f)
print(" }", file=f)
print("", file=f)
current_pattern = None
print(" SigSpec port(Cell *cell, IdString portname) {", file=f) print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
print(" return sigmap(cell->getPort(portname));", file=f) print(" return sigmap(cell->getPort(portname));", file=f)
@ -294,11 +368,13 @@ with open(outfile, "w") as f:
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
print(" module(module), sigmap(module) {", file=f) print(" module(module), sigmap(module) {", file=f)
for s, t in sorted(udata_types.items()): for current_pattern in sorted(patterns.keys()):
if t.endswith("*"): for s, t in sorted(udata_types[current_pattern].items()):
print(" ud.{} = nullptr;".format(s), file=f) if t.endswith("*"):
else: print(" ud_{}.{} = nullptr;".format(current_pattern,s), file=f)
print(" ud.{} = {}();".format(s, t), file=f) else:
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
current_pattern = None
print(" for (auto cell : module->cells()) {", file=f) print(" for (auto cell : module->cells()) {", file=f)
print(" for (auto &conn : cell->connections())", file=f) print(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f) print(" add_siguser(conn.second, cell);", file=f)
@ -328,34 +404,52 @@ with open(outfile, "w") as f:
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void run(std::function<void()> on_accept_f) {", file=f) for current_pattern in sorted(patterns.keys()):
print(" on_accept = on_accept_f;", file=f) print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
print(" rollback = 0;", file=f) print(" on_accept = on_accept_f;", file=f)
print(" blacklist_dirty = false;", file=f) print(" rollback = 0;", file=f)
for s, t in sorted(state_types.items()): print(" blacklist_dirty = false;", file=f)
if t.endswith("*"): for s, t in sorted(state_types[current_pattern].items()):
print(" st.{} = nullptr;".format(s), file=f) if t.endswith("*"):
else: print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f)
print(" st.{} = {}();".format(s, t), file=f) else:
print(" block_0();", file=f) print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f)
print(" }", file=f) print(" block_{}();".format(patterns[current_pattern]), file=f)
print("", file=f) print(" }", file=f)
print("", file=f)
print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
print(" run([&](){on_accept_f(*this);});", file=f) print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
print(" }", file=f)
print("", file=f)
print(" void run_{}() {{".format(current_pattern), file=f)
print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
print(" }", file=f)
print("", file=f)
current_pattern = None
for index in range(len(blocks)): for index in range(len(blocks)):
block = blocks[index] block = blocks[index]
print(" void block_{}() {{".format(index), file=f) print(" void block_{}() {{".format(index), file=f)
current_pattern = block["pattern"]
if block["type"] == "final":
print(" on_accept();", file=f)
print(" check_blacklist_{}();".format(current_pattern), file=f)
print(" }", file=f)
if index+1 != len(blocks):
print("", file=f)
continue
const_st = set() const_st = set()
nonconst_st = set() nonconst_st = set()
restore_st = set() restore_st = set()
for i in range(index): for i in range(patterns[current_pattern], index):
if blocks[i]["type"] == "code": if blocks[i]["type"] == "code":
for s in blocks[i]["states"]: for s in blocks[i]["states"]:
const_st.add(s) const_st.add(s)
@ -378,27 +472,27 @@ with open(outfile, "w") as f:
assert False assert False
for s in sorted(const_st): for s in sorted(const_st):
t = state_types[s] t = state_types[current_pattern][s]
if t.endswith("*"): if t.endswith("*"):
print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
else: else:
print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
for s in sorted(nonconst_st): for s in sorted(nonconst_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
if len(restore_st): if len(restore_st):
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} backup_{} = {};".format(t, s, s), file=f) print(" {} backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code": if block["type"] == "code":
print("", file=f) print("", file=f)
print(" do {", file=f) print(" do {", file=f)
print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
for line in block["code"]: for line in block["code"]:
@ -417,11 +511,11 @@ with open(outfile, "w") as f:
if len(restore_st) or len(nonconst_st): if len(restore_st) or len(nonconst_st):
print("", file=f) print("", file=f)
for s in sorted(restore_st): for s in sorted(restore_st):
t = state_types[s] t = state_types[current_pattern][s]
print(" {} = backup_{};".format(s, s), file=f) print(" {} = backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st): for s in sorted(nonconst_st):
if s not in restore_st: if s not in restore_st:
t = state_types[s] t = state_types[current_pattern][s]
if t.endswith("*"): if t.endswith("*"):
print(" {} = nullptr;".format(s), file=f) print(" {} = nullptr;".format(s), file=f)
else: else:
@ -470,17 +564,12 @@ with open(outfile, "w") as f:
else: else:
assert False assert False
current_pattern = None
print(" }", file=f) print(" }", file=f)
print("", file=f) print("", file=f)
print(" void block_{}() {{".format(len(blocks)), file=f)
print(" on_accept();", file=f)
print(" check_blacklist();", file=f)
print(" }", file=f)
print("};", file=f) print("};", file=f)
print("", file=f) if genhdr:
print("YOSYS_NAMESPACE_END", file=f) print("", file=f)
print("YOSYS_NAMESPACE_END", file=f)
# pp.pprint(blocks)

View file

@ -1169,6 +1169,7 @@ struct SatPass : public Pass {
if (args[argidx] == "-tempinduct-def") { if (args[argidx] == "-tempinduct-def") {
tempinduct = true; tempinduct = true;
tempinduct_def = true; tempinduct_def = true;
enable_undef = true;
continue; continue;
} }
if (args[argidx] == "-tempinduct-baseonly") { if (args[argidx] == "-tempinduct-baseonly") {

View file

@ -102,7 +102,8 @@ struct DffinitPass : public Pass {
if (wire->attributes.count("\\init")) { if (wire->attributes.count("\\init")) {
Const value = wire->attributes.at("\\init"); Const value = wire->attributes.at("\\init");
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
init_bits[sigmap(SigBit(wire, i))] = value[i]; if (value[i] != State::Sx)
init_bits[sigmap(SigBit(wire, i))] = value[i];
} }
if (wire->port_output) if (wire->port_output)
for (auto bit : sigmap(wire)) for (auto bit : sigmap(wire))

View file

@ -178,7 +178,17 @@ struct ShregmapTechXilinx7 : ShregmapTech
// Only map if $shiftx exclusively covers the shift register // Only map if $shiftx exclusively covers the shift register
if (shiftx->type == "$shiftx") { if (shiftx->type == "$shiftx") {
if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int()) if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int())
return false;
// Due to padding the most significant bits of A may be 1'bx,
// and if so, discount them
if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) {
const SigSpec A = shiftx->getPort("\\A");
const int A_width = shiftx->getParam("\\A_WIDTH").as_int();
for (int i = GetSize(taps); i < A_width; ++i)
if (A[i] != RTLIL::Sx) return false;
}
else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
return false; return false;
} }
else if (shiftx->type == "$mux") { else if (shiftx->type == "$mux") {

View file

@ -201,6 +201,8 @@ struct SynthPass : public ScriptPass
run("check"); run("check");
run("opt"); run("opt");
run("wreduce"); run("wreduce");
run("peepopt");
run("opt_clean");
if (help_mode) if (help_mode)
run("techmap -map +/cmp2lut.v", " (if -lut)"); run("techmap -map +/cmp2lut.v", " (if -lut)");
else else

View file

@ -253,7 +253,7 @@ struct SynthEcp5Pass : public ScriptPass
if (!nodffe) if (!nodffe)
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*"); run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
run("techmap -D NO_LUT -map +/ecp5/cells_map.v"); run("techmap -D NO_LUT -map +/ecp5/cells_map.v");
run("opt_expr -mux_undef"); run("opt_expr -undriven -mux_undef");
run("simplemap"); run("simplemap");
run("ecp5_ffinit"); run("ecp5_ffinit");
} }

View file

@ -225,11 +225,13 @@ struct SynthIce40Pass : public ScriptPass
run("proc"); run("proc");
} }
if (flatten && check_label("flatten", "(unless -noflatten)")) if (check_label("flatten", "(unless -noflatten)"))
{ {
run("flatten"); if (flatten) {
run("tribuf -logic"); run("flatten");
run("deminout"); run("tribuf -logic");
run("deminout");
}
} }
if (check_label("coarse")) if (check_label("coarse"))
@ -239,6 +241,8 @@ struct SynthIce40Pass : public ScriptPass
run("check"); run("check");
run("opt"); run("opt");
run("wreduce"); run("wreduce");
run("peepopt");
run("opt_clean");
run("share"); run("share");
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
run("opt_expr"); run("opt_expr");

View file

@ -17,6 +17,16 @@
* *
*/ */
// Convert negative-polarity reset to positive-polarity
(* techmap_celltype = "$_DFF_NN0_" *)
module _90_dff_nn0_to_np0 (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
(* techmap_celltype = "$_DFF_PN0_" *)
module _90_dff_pn0_to_pp0 (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
(* techmap_celltype = "$_DFF_NN1_" *)
module _90_dff_nn1_to_np1 (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
(* techmap_celltype = "$_DFF_PN1_" *)
module _90_dff_pn1_to_pp1 (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$__SHREG_ (input C, input D, input E, output Q); module \$__SHREG_ (input C, input D, input E, output Q);
parameter DEPTH = 0; parameter DEPTH = 0;
parameter [DEPTH-1:0] INIT = 0; parameter [DEPTH-1:0] INIT = 0;

View file

@ -22,26 +22,21 @@
`ifndef _NO_FFS `ifndef _NO_FFS
`ifndef _NO_POS_SR
module \$_DFF_N_ (input D, C, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule module \$_DFF_N_ (input D, C, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
module \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule module \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
module \$_DFF_NN0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
module \$_DFF_PN0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
module \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
`endif
module \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
`endif
`endif `endif

View file

@ -25,18 +25,9 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) struct SynthXilinxPass : public ScriptPass
{ {
if (label == run_from) SynthXilinxPass() : ScriptPass("synth_xilinx", "synthesis for Xilinx FPGAs") { }
active = true;
if (label == run_to)
active = false;
return active;
}
struct SynthXilinxPass : public Pass
{
SynthXilinxPass() : Pass("synth_xilinx", "synthesis for Xilinx FPGAs") { }
void help() YS_OVERRIDE void help() YS_OVERRIDE
{ {
@ -85,79 +76,30 @@ struct SynthXilinxPass : public Pass
log("\n"); log("\n");
log("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
log("\n"); help_script();
log(" begin:\n");
log(" read_verilog -lib +/xilinx/cells_sim.v\n");
log(" read_verilog -lib +/xilinx/cells_xtra.v\n");
log(" read_verilog -lib +/xilinx/brams_bb.v\n");
log(" hierarchy -check -top <top>\n");
log("\n");
log(" flatten: (only if -flatten)\n");
log(" proc\n");
log(" flatten\n");
log("\n");
log(" coarse:\n");
log(" synth -run coarse\n");
log("\n");
log(" bram: (only executed when '-nobram' is not given)\n");
log(" memory_bram -rules +/xilinx/brams.txt\n");
log(" techmap -map +/xilinx/brams_map.v\n");
log("\n");
log(" dram: (only executed when '-nodram' is not given)\n");
log(" memory_bram -rules +/xilinx/drams.txt\n");
log(" techmap -map +/xilinx/drams_map.v\n");
log("\n");
log(" fine:\n");
log(" opt -fast\n");
log(" memory_map\n");
log(" dffsr2dff\n");
log(" dff2dffe\n");
log(" techmap -map +/xilinx/arith_map.v\n");
log(" opt -fast\n");
log("\n");
log(" map_cells:\n");
log(" simplemap t:$dff t:$dffe (without '-nosrl' only)\n");
log(" pmux2shiftx (without '-nosrl' only)\n");
log(" opt_expr -mux_undef (without '-nosrl' only)\n");
log(" shregmap -tech xilinx -minlen 3 (without '-nosrl' only)\n");
log(" techmap -map +/xilinx/cells_map.v\n");
log(" clean\n");
log("\n");
log(" map_luts:\n");
log(" opt -full\n");
log(" techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v\n");
log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n");
log(" clean\n");
log(" shregmap -minlen 3 -init -params -enpol any_or_none (without '-nosrl' only)\n");
log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n");
log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n");
log(" clean\n");
log("\n");
log(" check:\n");
log(" hierarchy -check\n");
log(" stat\n");
log(" check -noinit\n");
log("\n");
log(" edif: (only if -edif)\n");
log(" write_edif <file-name>\n");
log("\n");
log(" blif: (only if -blif)\n");
log(" write_blif <file-name>\n");
log("\n"); log("\n");
} }
std::string top_opt, edif_file, blif_file;
bool flatten, retime, vpr, nobram, nodram, nosrl;
void clear_flags() YS_OVERRIDE
{
top_opt = "-auto-top";
edif_file.clear();
blif_file.clear();
flatten = false;
retime = false;
vpr = false;
nobram = false;
nodram = false;
nosrl = false;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
std::string top_opt = "-auto-top";
std::string edif_file;
std::string blif_file;
std::string run_from, run_to; std::string run_from, run_to;
bool flatten = false; clear_flags();
bool retime = false;
bool vpr = false;
bool nobram = false;
bool nodram = false;
bool nosrl = false;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
@ -213,129 +155,122 @@ struct SynthXilinxPass : public Pass
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");
bool active = run_from.empty();
log_header(design, "Executing SYNTH_XILINX pass.\n"); log_header(design, "Executing SYNTH_XILINX pass.\n");
log_push(); log_push();
if (check_label(active, run_from, run_to, "begin")) run_script(design, run_from, run_to);
{
if (vpr) {
Pass::call(design, "read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
} else {
Pass::call(design, "read_verilog -lib +/xilinx/cells_sim.v");
}
Pass::call(design, "read_verilog -lib +/xilinx/cells_xtra.v");
if (!nobram) {
Pass::call(design, "read_verilog -lib +/xilinx/brams_bb.v");
}
Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str()));
}
if (flatten && check_label(active, run_from, run_to, "flatten"))
{
Pass::call(design, "proc");
Pass::call(design, "flatten");
}
if (check_label(active, run_from, run_to, "coarse"))
{
Pass::call(design, "synth -run coarse");
}
if (check_label(active, run_from, run_to, "bram"))
{
if (!nobram) {
Pass::call(design, "memory_bram -rules +/xilinx/brams.txt");
Pass::call(design, "techmap -map +/xilinx/brams_map.v");
}
}
if (check_label(active, run_from, run_to, "dram"))
{
if (!nodram) {
Pass::call(design, "memory_bram -rules +/xilinx/drams.txt");
Pass::call(design, "techmap -map +/xilinx/drams_map.v");
}
}
if (check_label(active, run_from, run_to, "fine"))
{
Pass::call(design, "opt -fast");
Pass::call(design, "memory_map");
Pass::call(design, "dffsr2dff");
Pass::call(design, "dff2dffe");
if (vpr) {
Pass::call(design, "techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
} else {
Pass::call(design, "techmap -map +/xilinx/arith_map.v");
}
Pass::call(design, "hierarchy -check");
Pass::call(design, "opt -fast");
}
if (check_label(active, run_from, run_to, "map_cells"))
{
if (!nosrl) {
// shregmap operates on bit-level flops, not word-level,
// so break those down here
Pass::call(design, "simplemap t:$dff t:$dffe");
// shregmap -tech xilinx can cope with $shiftx and $mux
// cells for identifiying variable-length shift registers,
// so attempt to convert $pmux-es to the former
Pass::call(design, "pmux2shiftx");
// pmux2shiftx can leave behind a $pmux with a single entry
// -- need this to clean that up before shregmap
Pass::call(design, "opt_expr -mux_undef");
// shregmap with '-tech xilinx' infers variable length shift regs
Pass::call(design, "shregmap -tech xilinx -minlen 3");
}
Pass::call(design, "techmap -map +/xilinx/cells_map.v");
Pass::call(design, "clean");
}
if (check_label(active, run_from, run_to, "map_luts"))
{
Pass::call(design, "opt -full");
Pass::call(design, "techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v");
Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
Pass::call(design, "clean");
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl)
Pass::call(design, "shregmap -minlen 3 -init -params -enpol any_or_none");
Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
Pass::call(design, "clean");
}
if (check_label(active, run_from, run_to, "check"))
{
Pass::call(design, "hierarchy -check");
Pass::call(design, "stat");
Pass::call(design, "check -noinit");
}
if (check_label(active, run_from, run_to, "edif"))
{
if (!edif_file.empty())
Pass::call(design, stringf("write_edif -pvector bra %s", edif_file.c_str()));
}
if (check_label(active, run_from, run_to, "blif"))
{
if (!blif_file.empty())
Pass::call(design, stringf("write_blif %s", edif_file.c_str()));
}
log_pop(); log_pop();
} }
void script() YS_OVERRIDE
{
if (check_label("begin")) {
if (vpr)
run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
else
run("read_verilog -lib +/xilinx/cells_sim.v");
run("read_verilog -lib +/xilinx/cells_xtra.v");
if (!nobram || help_mode)
run("read_verilog -lib +/xilinx/brams_bb.v", "(skip if '-nobram')");
run(stringf("hierarchy -check %s", top_opt.c_str()));
}
if (check_label("flatten", "(with '-flatten' only)")) {
if (flatten || help_mode) {
run("proc");
run("flatten");
}
}
if (check_label("coarse")) {
run("synth -run coarse");
}
if (check_label("bram", "(skip if '-nobram')")) {
if (!nobram || help_mode) {
run("memory_bram -rules +/xilinx/brams.txt");
run("techmap -map +/xilinx/brams_map.v");
}
}
if (check_label("dram", "(skip if '-nodram')")) {
if (!nodram || help_mode) {
run("memory_bram -rules +/xilinx/drams.txt");
run("techmap -map +/xilinx/drams_map.v");
}
}
if (check_label("fine")) {
// shregmap -tech xilinx can cope with $shiftx and $mux
// cells for identifiying variable-length shift registers,
// so attempt to convert $pmux-es to the former
if (!nosrl || help_mode)
run("pmux2shiftx", "(skip if '-nosrl')");
run("opt -fast -full");
run("memory_map");
run("dffsr2dff");
run("dff2dffe");
run("opt -full");
if (!vpr || help_mode)
run("techmap -map +/xilinx/arith_map.v");
else
run("techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
if (!nosrl || help_mode) {
// shregmap operates on bit-level flops, not word-level,
// so break those down here
run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')");
// shregmap with '-tech xilinx' infers variable length shift regs
run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
}
run("techmap");
run("opt -fast");
}
if (check_label("map_cells")) {
run("techmap -map +/techmap.v -map +/xilinx/cells_map.v");
run("clean");
}
if (check_label("map_luts")) {
if (help_mode)
run("abc -luts 2:2,3,6:5,10,20 [-dff]");
else
run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
run("clean");
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl || help_mode)
run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')");
run("techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
run("clean");
}
if (check_label("check")) {
run("hierarchy -check");
run("stat");
run("check -noinit");
}
if (check_label("edif")) {
if (!edif_file.empty() || help_mode)
run(stringf("write_edif -pvector bra %s", edif_file.c_str()));
}
if (check_label("blif")) {
if (!blif_file.empty() || help_mode)
run(stringf("write_blif %s", edif_file.c_str()));
}
}
} SynthXilinxPass; } SynthXilinxPass;
PRIVATE_NAMESPACE_END PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,22 @@
module top
(
input [7:0] data_a,
input [6:1] addr_a,
input we_a, clk,
output reg [7:0] q_a
);
// Declare the RAM variable
reg [7:0] ram[63:0];
// Port A
always @ (posedge clk)
begin
if (we_a)
begin
ram[addr_a] <= data_a;
q_a <= data_a;
end
q_a <= ram[addr_a];
end
endmodule

View file

@ -92,3 +92,25 @@ module mem2reg_test5(input ctrl, output out);
assign out = bar[foo[0]]; assign out = bar[foo[0]];
endmodule endmodule
// ------------------------------------------------------
module mem2reg_test6 (din, dout);
input wire [3:0] din;
output reg [3:0] dout;
reg [1:0] din_array [1:0];
reg [1:0] dout_array [1:0];
always @* begin
din_array[0] = din[0 +: 2];
din_array[1] = din[2 +: 2];
dout_array[0] = din_array[0];
dout_array[1] = din_array[1];
{dout_array[0][1], dout_array[0][0]} = dout_array[0][0] + dout_array[1][0];
dout[0 +: 2] = dout_array[0];
dout[2 +: 2] = dout_array[1];
end
endmodule

9
tests/simple/peepopt.v Normal file
View file

@ -0,0 +1,9 @@
module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output [W-1:0] o);
assign o = i[s*W+:W];
endmodule
module peepopt_muldiv_0(input [1:0] i, output [1:0] o);
wire [3:0] t;
assign t = i * 3;
assign o = t / 3;
endmodule

View file

@ -16,6 +16,7 @@ operators.v $pow
partsel.v drops modules partsel.v drops modules
process.v drops modules process.v drops modules
realexpr.v drops modules realexpr.v drops modules
retime.v Initial value (11110101) for (retime_test.ff) not supported
scopes.v original verilog issues ( -x where x isn't declared signed) scopes.v original verilog issues ( -x where x isn't declared signed)
sincos.v $adff sincos.v $adff
specify.v no code (empty module generates error specify.v no code (empty module generates error

View file

@ -11,13 +11,13 @@ echo "" > $STDERRFILE
echo -n "Test: ${TESTNAME} -> " echo -n "Test: ${TESTNAME} -> "
$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE set -e
$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE
$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE 2>> $STDERRFILE
$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE 2>> $STDERRFILE
rm -f a.out reference_result.txt dut_result.txt rm -f a.out reference_result.txt dut_result.txt
set -e
iverilog -g2012 ${TESTNAME}_syn.v iverilog -g2012 ${TESTNAME}_syn.v
iverilog -g2012 ${TESTNAME}_ref_syn.v iverilog -g2012 ${TESTNAME}_ref_syn.v

View file

@ -147,7 +147,8 @@ do
fi fi
if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi
compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \ compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \
"$toolsdir"/../../techlibs/common/simlib.v "$toolsdir"/../../techlibs/common/simlib.v \
"$toolsdir"/../../techlibs/common/simcells.v
if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi
test_count=0 test_count=0