diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index ed6e9f8ee..9feff71c6 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -163,31 +163,61 @@ struct FirrtlWorker } }; /* Memories defined within this module. */ - struct memory { - string name; // memory name - int abits; // number of address bits - int size; // size (in units) of the memory - int width; // size (in bits) of each element - int read_latency; - int write_latency; - vector read_ports; - vector write_ports; - std::string init_file; - std::string init_file_srcFileSpec; - 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("") {} - memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} - 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; + struct memory { + Cell *pCell; // for error reporting + string name; // memory name + int abits; // number of address bits + int size; // size (in units) of the memory + int width; // size (in bits) of each element + int read_latency; + int write_latency; + vector read_ports; + vector write_ports; + std::string init_file; + std::string init_file_srcFileSpec; + string srcLine; + 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("") { + // Provide defaults for abits or size if one (but not the other) is specified. + if (this->abits == 0 && this->size != 0) { + this->abits = ceil_log2(this->size); + } else if (this->abits != 0 && this->size == 0) { + this->size = 1 << this->abits; + } + // Sanity-check this construction. + 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 memories; void register_memory(memory &m) @@ -604,7 +634,7 @@ struct FirrtlWorker int abits = cell->parameters.at("\\ABITS").as_int(); int width = cell->parameters.at("\\WIDTH").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 wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); @@ -681,6 +711,8 @@ struct FirrtlWorker { std::string cell_type = fid(cell->type); 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; if (cell->type == "$meminit" ) { 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_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); int portNum = 0; bool transparency = false; @@ -890,7 +927,7 @@ struct FirrtlWorker // If we have any memory definitions, output them. for (auto kv : memories) { - memory m = kv.second; + memory &m = kv.second; f << stringf(" mem %s:\n", m.name.c_str()); f << stringf(" data-type => UInt<%d>\n", m.width); f << stringf(" depth => %d\n", m.size); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b3a2a84be..d4515babf 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -645,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id_ast->children[0]->range_valid) 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; + if (children.size() > 1) + range = children[1]; } else log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str()); if (range) { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index a342bf5d9..e947125bf 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1605,6 +1605,7 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_tmp->str] = wire_tmp; wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, 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); wire_tmp_id->str = wire_tmp->str; diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index f83824088..7bb216dd0 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -45,7 +45,7 @@ YOSYS_NAMESPACE_END USING_YOSYS_NAMESPACE %} -%name-prefix "rtlil_frontend_ilang_yy" +%define api.prefix {rtlil_frontend_ilang_yy} %union { char *string; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 6ef38252a..08e556e8e 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -206,7 +206,9 @@ YOSYS_NAMESPACE_END "const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } "checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } +"var" { SV_KEYWORD(TOK_VAR); } "bit" { SV_KEYWORD(TOK_REG); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 40968d17a..46b3a9025 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -96,7 +96,7 @@ static void free_attr(std::map *al) %} -%name-prefix "frontend_verilog_yy" +%define api.prefix {frontend_verilog_yy} %union { std::string *string; @@ -106,11 +106,11 @@ static void free_attr(std::map *al) } %token TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE TOK_SVA_LABEL -%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER +%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP -%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT @@ -456,6 +456,9 @@ wire_type_token: TOK_LOGIC { astbuf3->is_logic = true; } | + TOK_VAR { + astbuf3->is_logic = true; + } | TOK_INTEGER { astbuf3->is_reg = true; astbuf3->range_left = 31; @@ -1341,6 +1344,9 @@ opt_property: TOK_PROPERTY { $$ = true; } | + TOK_FINAL { + $$ = false; + } | /* empty */ { $$ = false; }; diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index 1c64a7b77..adbe89e31 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -291,7 +291,7 @@ struct QwpWorker // gaussian elimination 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); // find best row diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index fcb662b59..618d32550 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -272,6 +272,10 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos } } + SigPool raw_used_signals_noaliases; + for (auto &it : module->connections_) + raw_used_signals_noaliases.add(it.second); + module->connections_.clear(); SigPool used_signals; @@ -281,6 +285,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); + raw_used_signals_noaliases.add(it2.second); if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } @@ -301,53 +306,63 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos } } - std::vector maybe_del_wires; + pool del_wires_queue; for (auto wire : module->wires()) { - 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; - assign_map.apply(s2); - 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)); - 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); - } - } + SigSpec s1 = SigSpec(wire), s2 = assign_map(s1); + log_assert(GetSize(s1) == GetSize(s2)); + + Const initval; + if (wire->attributes.count("\\init")) + initval = wire->attributes.at("\\init"); + if (GetSize(initval) != GetSize(wire)) + initval.bits.resize(GetSize(wire), State::Sx); + if (initval.is_fully_undef()) + wire->attributes.erase("\\init"); + + bool delete_this_wire = false; + if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) { + /* do not delete anything with "keep" or module ports or initialized wires */ + } else + if (!purge_mode && check_public_name(wire->name)) { + /* do not get rid of public names unless in purge mode */ } else { - if (!used_signals.check_any(RTLIL::SigSpec(wire))) - maybe_del_wires.push_back(wire); + if (!raw_used_signals_noaliases.check_any(s1)) + delete_this_wire = true; + if (!used_signals_nodrivers.check_any(s2)) + delete_this_wire = true; } - RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); - if (!used_signals_nodrivers.check_any(sig)) { + if (delete_this_wire) { + del_wires_queue.insert(wire); + } else { + 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; - for (int i = 0; i < GetSize(sig); i++) { - if (sig[i].wire == NULL) + for (int i = 0; i < GetSize(s2); i++) { + if (s2[i].wire == NULL) continue; - if (!used_signals_nodrivers.check(sig[i])) { + if (!used_signals_nodrivers.check(s2[i])) { if (!unused_bits.empty()) unused_bits += " "; unused_bits += stringf("%d", i); @@ -362,24 +377,19 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos } } + int del_temp_wires_count = 0; + for (auto wire : del_wires_queue) { + if (ys_debug() || (check_public_name(wire->name) && verbose)) + log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); + else + del_temp_wires_count++; + } - pool del_wires; + module->remove(del_wires_queue); + count_rm_wires += GetSize(del_wires_queue); - int del_wires_count = 0; - for (auto wire : maybe_del_wires) - if (!used_signals.check_any(RTLIL::SigSpec(wire))) { - if (check_public_name(wire->name) && verbose) { - log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); - } - del_wires.insert(wire); - del_wires_count++; - } - - module->remove(del_wires); - count_rm_wires += del_wires.size(); - - if (verbose && del_wires_count > 0) - log_debug(" removed %d unused temporary wires.\n", del_wires_count); + if (verbose && del_temp_wires_count) + log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count); } bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) @@ -526,6 +536,9 @@ struct OptCleanPass : public Pass { ct_all.setup(design); + count_rm_cells = 0; + count_rm_wires = 0; + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; @@ -591,7 +604,7 @@ struct CleanPass : public Pass { for (auto module : design->selected_whole_modules()) { if (module->has_processes()) continue; - rmunused_module(module, purge_mode, false, false); + rmunused_module(module, purge_mode, ys_debug(), false); } log_suppressed(); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index b445afdc8..512ef0cbf 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -61,7 +61,7 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) } if (wire->port_input) driven_signals.add(sigmap(wire)); - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) used_signals.add(sigmap(wire)); all_signals.add(sigmap(wire)); } @@ -88,7 +88,7 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) } } - log_debug("Setting undriven signal in %s to constant: %s = %s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(val)); + 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; } @@ -104,10 +104,15 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) if (SigBit(initval[i]) == sig[i]) initval[i] = State::Sx; } - if (initval.is_fully_undef()) + if (initval.is_fully_undef()) { + log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire)); wire->attributes.erase("\\init"); - else + 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; + } } } } diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 41de8aad1..58c6e4b4b 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -531,6 +531,42 @@ struct WreducePass : public Pass { 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")) { IdString memid = c->getParam("\\MEMID").decode_string(); RTLIL::Memory *mem = module->memories.at(memid); diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index c9263057e..0ad36ea2c 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1 +1,2 @@ /ice40_dsp_pm.h +/peepopt_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index e0609d9ba..7911132db 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,8 +1,23 @@ OBJS += passes/pmgen/ice40_dsp.o +OBJS += passes/pmgen/peepopt.o + +# -------------------------------------- passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h EXTRA_OBJS += 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 - $(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 $<,$^) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 320e95a77..2f0b1fd5a 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -29,19 +29,25 @@ up in any future matches: pm.blacklist(some_cell); -The `.run(callback_function)` method searches for all matches and calls the -callback function for each found match: +The `.run_(callback_function)` method searches for all matches +for the pattern`` 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(" with 'bar' cell: %s\n", log_id(pm.st.bar)); }); The `.pmg` file declares matcher state variables that are accessible via the -`.st.` members. (The `.st` member is of type `foobar_pm::state_t`.) +`.st_.` members. (The `.st_` member is +of type `foobar_pm::state__t`.) Similarly the `.pmg` file declares user data variables that become members of -`.ud`, a struct of type `foobar_pm::udata_t`. +`.ud_`, a struct of type `foobar_pm::udata__t`. + +There are four versions of the `run_()` method: Without callback, +callback without arguments, callback with reference to `pm`, and callback with +reference to `pm.st_`. The .pmg File Format @@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens. 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 ------------------------- @@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith and saved and restored as needed. They are automatically initialized to the default constructed value of their type -when `.run(callback_function)` is called. +when `.run_(callback_function)` is called. Declaring udata variables ------------------------- diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 3a054a463..39d033a04 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -19,47 +19,50 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "passes/pmgen/ice40_dsp_pm.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#include "passes/pmgen/ice40_dsp_pm.h" + void create_ice40_dsp(ice40_dsp_pm &pm) { + auto &st = pm.st_ice40_dsp; + #if 0 log("\n"); - log("ffA: %s\n", log_id(pm.st.ffA, "--")); - log("ffB: %s\n", log_id(pm.st.ffB, "--")); - log("mul: %s\n", log_id(pm.st.mul, "--")); - log("ffY: %s\n", log_id(pm.st.ffY, "--")); - log("addAB: %s\n", log_id(pm.st.addAB, "--")); - log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); - log("ffS: %s\n", log_id(pm.st.ffS, "--")); + log("ffA: %s\n", log_id(st.ffA, "--")); + log("ffB: %s\n", log_id(st.ffB, "--")); + log("mul: %s\n", log_id(st.mul, "--")); + log("ffY: %s\n", log_id(st.ffY, "--")); + log("addAB: %s\n", log_id(st.addAB, "--")); + log("muxAB: %s\n", log_id(st.muxAB, "--")); + log("ffS: %s\n", log_id(st.ffS, "--")); #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) { - log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); + if (GetSize(st.sigA) > 16) { + log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); return; } - if (GetSize(pm.st.sigB) > 16) { - log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); + if (GetSize(st.sigB) > 16) { + log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB)); return; } - if (GetSize(pm.st.sigS) > 32) { - log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); + if (GetSize(st.sigS) > 32) { + log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); return; } - if (GetSize(pm.st.sigY) > 32) { - log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); + if (GetSize(st.sigY) > 32) { + log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); 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) { 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"); 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 - SigSpec A = pm.st.sigA; + SigSpec A = st.sigA; A.extend_u0(16, mul_signed); - SigSpec B = pm.st.sigB; + SigSpec B = st.sigB; B.extend_u0(16, mul_signed); SigSpec CD; - if (pm.st.muxA) - CD = pm.st.muxA->getPort("\\B"); - if (pm.st.muxB) - CD = pm.st.muxB->getPort("\\A"); + if (st.muxA) + CD = st.muxA->getPort("\\B"); + if (st.muxB) + CD = st.muxB->getPort("\\A"); CD.extend_u0(32, mul_signed); 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("\\D", CD.extract(16, 16)); - cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); - cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); + cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); + cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); cell->setPort("\\AHOLD", 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("\\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->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) - log(" ffA:%s", log_id(pm.st.ffA)); + if (st.ffA) + log(" ffA:%s", log_id(st.ffA)); - if (pm.st.ffB) - log(" ffB:%s", log_id(pm.st.ffB)); + if (st.ffB) + log(" ffB:%s", log_id(st.ffB)); - if (pm.st.ffY) - log(" ffY:%s", log_id(pm.st.ffY)); + if (st.ffY) + log(" ffY:%s", log_id(st.ffY)); - if (pm.st.ffS) - log(" ffS:%s", log_id(pm.st.ffS)); + if (st.ffS) + log(" ffS:%s", log_id(st.ffS)); log("\n"); } @@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) // 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) O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); cell->setPort("\\O", O); - if (pm.st.addAB) { - log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); - cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); - cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); + if (st.addAB) { + log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); + cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); + cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); } else { cell->setPort("\\ADDSUBTOP", State::S0); cell->setPort("\\ADDSUBBOT", State::S0); @@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\OHOLDBOT", State::S0); SigSpec acc_reset = State::S0; - if (pm.st.muxA) - acc_reset = pm.st.muxA->getPort("\\S"); - if (pm.st.muxB) - acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); + if (st.muxA) + acc_reset = st.muxA->getPort("\\S"); + if (st.muxB) + acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S")); cell->setPort("\\OLOADTOP", 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("\\D_REG", State::S0); - cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); - cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); - cell->setParam("\\PIPELINE_16x16_MULT_REG1", 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", 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("\\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_UPPERINPUT", State::S0); 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_UPPERINPUT", State::S0); 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("\\B_SIGNED", mul_signed ? State::S1 : State::S0); - pm.autoremove(pm.st.mul); - pm.autoremove(pm.st.ffY); - pm.autoremove(pm.st.ffS); + pm.autoremove(st.mul); + pm.autoremove(st.ffY); + pm.autoremove(st.ffS); } struct Ice40DspPass : public Pass { @@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass { extra_args(args, argidx, design); 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; diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 96c62e313..1f3590d4e 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -1,3 +1,5 @@ +pattern ice40_dsp + state clock state clock_pol clock_vld state sigA sigB sigY sigS diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc new file mode 100644 index 000000000..e7f95cf85 --- /dev/null +++ b/passes/pmgen/peepopt.cc @@ -0,0 +1,68 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +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 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 diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg new file mode 100644 index 000000000..06c275834 --- /dev/null +++ b/passes/pmgen/peepopt_muldiv.pmg @@ -0,0 +1,36 @@ +pattern muldiv + +state 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 port(div, \A) === t + index 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 diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg new file mode 100644 index 000000000..fe861b728 --- /dev/null +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -0,0 +1,87 @@ +pattern shiftmul + +state shamt + +match shift + select shift->type.in($shift, $shiftx, $shr) +endmatch + +code shamt + shamt = port(shift, \B); + if (shamt.empty()) + reject; + if (shamt[GetSize(shamt)-1] == State::S0) { + do { + shamt.remove(GetSize(shamt)-1); + if (shamt.empty()) + reject; + } 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 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 diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index d9747b065..81052afce 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -3,15 +3,42 @@ import re import sys import pprint +import getopt pp = pprint.PrettyPrinter(indent=4) -pmgfile = sys.argv[1] -assert pmgfile.endswith(".pmg") -prefix = pmgfile[0:-4] -prefix = prefix.split('/')[-1] -outfile = sys.argv[2] +prefix = None +pmgfiles = list() +outfile = None +debug = False +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() udata_types = dict() blocks = list() @@ -77,7 +104,8 @@ def rewrite_cpp(s): return "".join(t) -with open(pmgfile, "r") as f: +def process_pmgfile(f): + global current_pattern while True: line = f.readline() if line == "": break @@ -87,14 +115,31 @@ with open(pmgfile, "r") as f: if len(cmd) == 0 or cmd[0].startswith("//"): continue 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": 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 type_str = m.group(1) states_str = m.group(2) for s in re.split(r"\s+", states_str): - assert s not in state_types - state_types[s] = type_str + assert s not in state_types[current_pattern] + state_types[current_pattern][s] = type_str continue if cmd == "udata": @@ -103,19 +148,20 @@ with open(pmgfile, "r") as f: type_str = m.group(1) udatas_str = m.group(2) for s in re.split(r"\s+", udatas_str): - assert s not in udata_types - udata_types[s] = type_str + assert s not in udata_types[current_pattern] + udata_types[current_pattern][s] = type_str continue if cmd == "match": block = dict() block["type"] = "match" + block["pattern"] = current_pattern line = line.split() assert len(line) == 2 - assert line[1] not in state_types + assert line[1] not in state_types[current_pattern] block["cell"] = line[1] - state_types[line[1]] = "Cell*"; + state_types[current_pattern][line[1]] = "Cell*"; block["if"] = list() block["select"] = list() @@ -158,15 +204,18 @@ with open(pmgfile, "r") as f: assert False blocks.append(block) + continue if cmd == "code": block = dict() block["type"] = "code" + block["pattern"] = current_pattern + block["code"] = list() block["states"] = set() for s in line.split()[1:]: - assert s in state_types + assert s in state_types[current_pattern] block["states"].add(s) while True: @@ -179,17 +228,36 @@ with open(pmgfile, "r") as f: block["code"].append(rewrite_cpp(l.rstrip())) 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: - 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("#include \"kernel/yosys.h\"", file=f) - print("#include \"kernel/sigtools.h\"", file=f) - print("", file=f) - - print("YOSYS_NAMESPACE_BEGIN", file=f) - print("", file=f) + if genhdr: + print("#include \"kernel/yosys.h\"", file=f) + print("#include \"kernel/sigtools.h\"", file=f) + print("", file=f) + print("YOSYS_NAMESPACE_BEGIN", file=f) + print("", file=f) print("struct {}_pm {{".format(prefix), file=f) print(" Module *module;", file=f) @@ -212,17 +280,19 @@ with open(outfile, "w") as f: print(" int rollback;", file=f) print("", file=f) - print(" struct state_t {", file=f) - for s, t in sorted(state_types.items()): - print(" {} {};".format(t, s), file=f) - print(" } st;", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" struct state_{}_t {{".format(current_pattern), file=f) + for s, t in sorted(state_types[current_pattern].items()): + print(" {} {};".format(t, s), file=f) + print(" }} st_{};".format(current_pattern), file=f) + print("", file=f) - print(" struct udata_t {", file=f) - for s, t in sorted(udata_types.items()): - print(" {} {};".format(t, s), file=f) - print(" } ud;", file=f) - print("", file=f) + print(" struct udata_{}_t {{".format(current_pattern), file=f) + for s, t in sorted(udata_types[current_pattern].items()): + print(" {} {};".format(t, s), file=f) + print(" }} ud_{};".format(current_pattern), file=f) + print("", file=f) + current_pattern = None for v, n in sorted(ids.items()): if n[0] == "\\": @@ -258,20 +328,24 @@ with open(outfile, "w") as f: print(" }", file=f) print("", file=f) - print(" void check_blacklist() {", file=f) - print(" if (!blacklist_dirty)", file=f) - print(" return;", file=f) - print(" blacklist_dirty = false;", file=f) - for index in range(len(blocks)): - block = blocks[index] - if block["type"] == "match": - print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) - print(" rollback = {};".format(index+1), file=f) - print(" return;", file=f) - print(" }", file=f) - print(" rollback = 0;", file=f) - print(" }", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" void check_blacklist_{}() {{".format(current_pattern), file=f) + print(" if (!blacklist_dirty)", file=f) + print(" return;", file=f) + print(" blacklist_dirty = false;", file=f) + for index in range(len(blocks)): + block = blocks[index] + if block["pattern"] != current_pattern: + continue + if block["type"] == "match": + print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) + print(" rollback = {};".format(index+1), file=f) + print(" return;", 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(" return sigmap(cell->getPort(portname));", file=f) @@ -294,11 +368,13 @@ with open(outfile, "w") as f: print(" {}_pm(Module *module, const vector &cells) :".format(prefix), file=f) print(" module(module), sigmap(module) {", file=f) - for s, t in sorted(udata_types.items()): - if t.endswith("*"): - print(" ud.{} = nullptr;".format(s), file=f) - else: - print(" ud.{} = {}();".format(s, t), file=f) + for current_pattern in sorted(patterns.keys()): + for s, t in sorted(udata_types[current_pattern].items()): + if t.endswith("*"): + print(" ud_{}.{} = nullptr;".format(current_pattern,s), 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 &conn : cell->connections())", 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(" void run(std::function on_accept_f) {", file=f) - print(" on_accept = on_accept_f;", file=f) - print(" rollback = 0;", file=f) - print(" blacklist_dirty = false;", file=f) - for s, t in sorted(state_types.items()): - if t.endswith("*"): - print(" st.{} = nullptr;".format(s), file=f) - else: - print(" st.{} = {}();".format(s, t), file=f) - print(" block_0();", file=f) - print(" }", file=f) - print("", file=f) - - print(" void run(std::function on_accept_f) {{".format(prefix), file=f) - print(" run([&](){on_accept_f(*this);});", file=f) - print(" }", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" void run_{}(std::function on_accept_f) {{".format(current_pattern), file=f) + print(" on_accept = on_accept_f;", file=f) + print(" rollback = 0;", file=f) + print(" blacklist_dirty = false;", file=f) + for s, t in sorted(state_types[current_pattern].items()): + if t.endswith("*"): + print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f) + else: + print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) + print(" block_{}();".format(patterns[current_pattern]), file=f) + print(" }", file=f) + print("", file=f) + print(" void run_{}(std::function on_accept_f) {{".format(current_pattern, prefix), file=f) + print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) + print(" }", file=f) + print("", file=f) + print(" void run_{}(std::function 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)): block = blocks[index] 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() nonconst_st = set() restore_st = set() - for i in range(index): + for i in range(patterns[current_pattern], index): if blocks[i]["type"] == "code": for s in blocks[i]["states"]: const_st.add(s) @@ -378,27 +472,27 @@ with open(outfile, "w") as f: assert False for s in sorted(const_st): - t = state_types[s] + t = state_types[current_pattern][s] 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: - 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): - t = state_types[s] - print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + t = state_types[current_pattern][s] + print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) if len(restore_st): print("", file=f) for s in sorted(restore_st): - t = state_types[s] + t = state_types[current_pattern][s] print(" {} backup_{} = {};".format(t, s, s), file=f) if block["type"] == "code": print("", file=f) print(" do {", file=f) - print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) - print("#define accept do { on_accept(); check_blacklist(); if (rollback) 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)".format(current_pattern), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) for line in block["code"]: @@ -417,11 +511,11 @@ with open(outfile, "w") as f: if len(restore_st) or len(nonconst_st): print("", file=f) for s in sorted(restore_st): - t = state_types[s] + t = state_types[current_pattern][s] print(" {} = backup_{};".format(s, s), file=f) for s in sorted(nonconst_st): if s not in restore_st: - t = state_types[s] + t = state_types[current_pattern][s] if t.endswith("*"): print(" {} = nullptr;".format(s), file=f) else: @@ -470,17 +564,12 @@ with open(outfile, "w") as f: else: assert False - + current_pattern = None 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("YOSYS_NAMESPACE_END", file=f) - -# pp.pprint(blocks) + if genhdr: + print("", file=f) + print("YOSYS_NAMESPACE_END", file=f) diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 809345486..71ce1683d 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -508,7 +508,7 @@ struct ExposePass : public Pass { } for (auto &conn : module->connections_) - conn.first = out_to_in_map(sigmap(conn.first)); + conn.first = out_to_in_map(conn.first); } if (flag_cut) diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 695a03e15..cbba738f0 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -1169,6 +1169,7 @@ struct SatPass : public Pass { if (args[argidx] == "-tempinduct-def") { tempinduct = true; tempinduct_def = true; + enable_undef = true; continue; } if (args[argidx] == "-tempinduct-baseonly") { diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 547115459..5b19d84fb 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -330,20 +330,33 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr) { std::string abc_sname = abc_name.substr(1); - if (abc_sname.substr(0, 5) == "ys__n") { - bool inv = abc_sname.back() == 'v'; - if (inv) abc_sname.pop_back(); + bool isnew = false; + if (abc_sname.substr(0, 4) == "new_") + { + abc_sname.erase(0, 4); + isnew = true; + } + if (abc_sname.substr(0, 5) == "ys__n") + { abc_sname.erase(0, 5); - if (abc_sname.find_last_not_of("012345689") == std::string::npos) { + if (std::isdigit(abc_sname.at(0))) + { int sid = std::stoi(abc_sname); - for (auto sig : signal_list) { - if (sig.id == sid && sig.bit.wire != nullptr) { + size_t postfix_start = abc_sname.find_first_not_of("0123456789"); + std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : ""; + + if (sid < GetSize(signal_list)) + { + auto sig = signal_list.at(sid); + if (sig.bit.wire != nullptr) + { std::stringstream sstr; sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1); if (sig.bit.wire->width != 1) sstr << "[" << sig.bit.offset << "]"; - if (inv) - sstr << "_inv"; + if (isnew) + sstr << "_new"; + sstr << postfix; if (orig_wire != nullptr) *orig_wire = sig.bit.wire; return sstr.str(); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 48390488e..0ad33dc0e 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -102,7 +102,8 @@ struct DffinitPass : public Pass { if (wire->attributes.count("\\init")) { Const value = wire->attributes.at("\\init"); 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) for (auto bit : sigmap(wire)) diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index a541b33be..75eedfbcc 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -178,7 +178,17 @@ struct ShregmapTechXilinx7 : ShregmapTech // Only map if $shiftx exclusively covers the shift register 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; } else if (shiftx->type == "$mux") { diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index ccfa76e02..e41c0fe97 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -201,6 +201,8 @@ struct SynthPass : public ScriptPass run("check"); run("opt"); run("wreduce"); + run("peepopt"); + run("opt_clean"); if (help_mode) run("techmap -map +/cmp2lut.v", " (if -lut)"); else diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 5de33110a..bb96d66d1 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -241,6 +241,8 @@ struct SynthIce40Pass : public ScriptPass run("check"); run("opt"); run("wreduce"); + run("peepopt"); + run("opt_clean"); run("share"); run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("opt_expr"); diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index 704ab21b1..40789ddbe 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -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); parameter DEPTH = 0; parameter [DEPTH-1:0] INIT = 0; diff --git a/techlibs/xilinx/ff_map.v b/techlibs/xilinx/ff_map.v index c61fd7070..13beaa6ae 100644 --- a/techlibs/xilinx/ff_map.v +++ b/techlibs/xilinx/ff_map.v @@ -22,26 +22,21 @@ `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_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_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_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_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_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 -`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 diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index c4c27d816..8aa7b508e 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -205,45 +205,41 @@ struct SynthXilinxPass : public ScriptPass } if (check_label("fine")) { - run("opt -fast"); + // 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"); - run("hierarchy -check"); - run("opt -fast"); - } - - if (check_label("map_cells")) - { 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 -tech xilinx can cope with $shiftx and $mux - // cells for identifiying variable-length shift registers, - // so attempt to convert $pmux-es to the former - run("pmux2shiftx", "(skip if '-nosrl')"); - // pmux2shiftx can leave behind a $pmux with a single entry - // -- need this to clean that up before shregmap - run("opt_expr -mux_undef", "(skip if '-nosrl')"); // shregmap with '-tech xilinx' infers variable length shift regs run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); } - run("techmap -map +/xilinx/cells_map.v"); + 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")) - { - run("opt -full"); - run("techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v"); + if (check_label("map_luts")) { if (help_mode) run("abc -luts 2:2,3,6:5,10,20 [-dff]"); else @@ -259,21 +255,18 @@ struct SynthXilinxPass : public ScriptPass run("clean"); } - if (check_label("check")) - { + if (check_label("check")) { run("hierarchy -check"); run("stat"); run("check -noinit"); } - if (check_label("edif")) - { + 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 (check_label("blif")) { if (!blif_file.empty() || help_mode) run(stringf("write_blif %s", edif_file.c_str())); } diff --git a/tests/memories/firrtl_938.v b/tests/memories/firrtl_938.v new file mode 100644 index 000000000..af5efcd25 --- /dev/null +++ b/tests/memories/firrtl_938.v @@ -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 diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v index 9839fd4a8..100426785 100644 --- a/tests/simple/mem2reg.v +++ b/tests/simple/mem2reg.v @@ -92,3 +92,25 @@ module mem2reg_test5(input ctrl, output out); assign out = bar[foo[0]]; 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 diff --git a/tests/simple/peepopt.v b/tests/simple/peepopt.v new file mode 100644 index 000000000..b27b9fe57 --- /dev/null +++ b/tests/simple/peepopt.v @@ -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 diff --git a/tests/simple/xfirrtl b/tests/simple/xfirrtl index 50d693513..ba61a4476 100644 --- a/tests/simple/xfirrtl +++ b/tests/simple/xfirrtl @@ -16,6 +16,7 @@ operators.v $pow partsel.v drops modules process.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) sincos.v $adff specify.v no code (empty module generates error diff --git a/tests/svinterfaces/runone.sh b/tests/svinterfaces/runone.sh index 0adecc797..54cf5f2ec 100755 --- a/tests/svinterfaces/runone.sh +++ b/tests/svinterfaces/runone.sh @@ -11,13 +11,13 @@ echo "" > $STDERRFILE echo -n "Test: ${TESTNAME} -> " -$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE -$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE +set -e + +$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 -set -e - iverilog -g2012 ${TESTNAME}_syn.v iverilog -g2012 ${TESTNAME}_ref_syn.v diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index bb9c3bfb5..920474a84 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -147,7 +147,8 @@ do 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 \ - "$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 test_count=0