From f94f544b502c9de88807dec8205417628fe38202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 5 Apr 2023 12:36:56 +0200 Subject: [PATCH 01/43] Fix the python generator for a bunch of const cases Makes the below show up in the binding. .def("c_str", &IdString::c_str) .def("chunks", &SigSpec::chunks) .def("bits", &SigSpec::bits) .def("at", &SigSpec::at) .def("getPort", &Cell::getPort) .def("connections", &Cell::connections) .def("getParam", &Cell::getParam) .def("connections", &Module::connections) def("log_signal", YOSYS_PYTHON::log_signal); def("log_signal", YOSYS_PYTHON::log_signal); def("log_const", YOSYS_PYTHON::log_const); def("log_const", YOSYS_PYTHON::log_const); def("log_id", YOSYS_PYTHON::log_id); --- misc/py_wrap_generator.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 4d9a60113..ecf02e601 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -178,6 +178,8 @@ class WType: t.cont = None t.attr_type = attr_types.default if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "): + str_def = str_def.replace("const ", "") + candidate = WContainer.from_string(str_def, containing_file, line_number) if candidate == None: return None @@ -203,8 +205,12 @@ class WType: prefix = "" + if str.startswith(str_def, "const "): + if "char_p" in str_def: + prefix = "const " + str_def = str_def[6:] if str.startswith(str_def, "unsigned "): - prefix = "unsigned " + prefix = "unsigned " + prefix str_def = str_def[9:] while str.startswith(str_def, "long "): prefix= "long " + prefix @@ -1285,7 +1291,7 @@ class WFunction: prefix = "" i = 0 for part in parts: - if part in ["unsigned", "long", "short"]: + if part in ["unsigned", "long", "short", "const"]: prefix += part + " " i += 1 else: From bd063381727a182b5d9f155483d5707fb6ce7e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 5 Apr 2023 13:33:18 +0200 Subject: [PATCH 02/43] py_wrap_generator: Fix handling of method name collisions If two methods have the same signature but for qualifiers the Python binding doesn't care about ('const'), do not generate a mangled name for the method. Fixes .def("wire__YOSYS_NAMESPACE_RTLIL_IdString", &Module::wire__YOSYS_NAMESPACE_RTLIL_IdString) .def("cell__YOSYS_NAMESPACE_RTLIL_IdString", &Module::cell__YOSYS_NAMESPACE_RTLIL_IdString) in the output after the previous change. --- misc/py_wrap_generator.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index ecf02e601..7fe78e03a 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -1367,10 +1367,17 @@ class WFunction: func.args.append(parsed) return func + @property + def mangled_name(self): + mangled_typename = lambda code: code.replace("::", "_").replace("<","_").replace(">","_") \ + .replace(" ","").replace("*","").replace(",","") + + return self.name + "".join( + f"__{mangled_typename(arg.wtype.gen_text_cpp())}" for arg in self.args + ) + def gen_alias(self): - self.alias = self.name - for arg in self.args: - self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","") + self.alias = self.mangled_name def gen_decl(self): if self.duplicate: @@ -2196,12 +2203,15 @@ def clean_duplicates(): for fun in class_.found_funs: if fun.gen_decl_hash_py() in known_decls: debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) + other = known_decls[fun.gen_decl_hash_py()] - other.gen_alias() - fun.gen_alias() - if fun.gen_decl_hash_py() == other.gen_decl_hash_py(): + if fun.mangled_name == other.mangled_name: fun.duplicate = True debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3) + continue + + other.gen_alias() + fun.gen_alias() else: known_decls[fun.gen_decl_hash_py()] = fun known_decls = [] From 7caeb922a0fc9e5f8fc7b670d2f58d16d6156e50 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 25 May 2023 12:46:16 +0200 Subject: [PATCH 03/43] sim: Run level triggered async updates to fixpoint during initialization --- passes/sat/sim.cc | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 273e9db86..b970ee568 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -589,7 +589,7 @@ struct SimInstance } } - bool update_ph2(bool gclk) + bool update_ph2(bool gclk, bool stable_past_update = false) { bool did_something = false; @@ -600,7 +600,7 @@ struct SimInstance Const current_q = get_state(ff.data.sig_q); - if (ff_data.has_clk) { + if (ff_data.has_clk && !stable_past_update) { // flip-flops State current_clk = get_state(ff_data.sig_clk)[0]; if (ff_data.pol_clk ? (ff.past_clk == State::S0 && current_clk != State::S0) : @@ -621,7 +621,7 @@ struct SimInstance if (ff_data.has_aload) { State current_aload = get_state(ff_data.sig_aload)[0]; if (current_aload == (ff_data.pol_aload ? State::S1 : State::S0)) { - current_q = ff_data.has_clk ? ff.past_ad : get_state(ff.data.sig_ad); + current_q = ff_data.has_clk && !stable_past_update ? ff.past_ad : get_state(ff.data.sig_ad); } } // async reset @@ -672,6 +672,8 @@ struct SimInstance } else { + if (stable_past_update) + continue; if (port.clk_polarity ? (mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) : (mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0)) @@ -701,7 +703,7 @@ struct SimInstance } for (auto it : children) - if (it.second->update_ph2(gclk)) { + if (it.second->update_ph2(gclk, stable_past_update)) { dirty_children.insert(it.second); did_something = true; } @@ -1197,9 +1199,21 @@ struct SimWorker : SimShared void initialize_stable_past() { - if (debug) - log("\n-- ph1 (initialize) --\n"); - top->update_ph1(); + + while (1) + { + if (debug) + log("\n-- ph1 (initialize) --\n"); + + top->update_ph1(); + + if (debug) + log("\n-- ph2 (initialize) --\n"); + + if (!top->update_ph2(false, true)) + break; + } + if (debug) log("\n-- ph3 (initialize) --\n"); top->update_ph3(true); From e36c71b5b743e578f3a965cda9926a426cd5c295 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 25 May 2023 12:48:02 +0200 Subject: [PATCH 04/43] Use clk2fflogic attr on cells to track original FF names in witnesses This makes clk2fflogic add an attr to $ff cells that carry the state of the emulated async FF. The $ff output doesn't have any async updates that happened in the current cycle, but the $ff input does, so the $ff input corresponds to the async FF's output in the original design. Hence this patch also makes the following changes to passes besides clk2fflogic (but only for FFs with the clk2fflogic attr set): * opt_clean treats the input as a register name (instead of the output) * rename -witness ensures that the input has a public name * the formal backends (smt2, btor, aiger) will use the input's name for the initial state of the FF in witness files * when sim reads a yw witness that assigns an initial value to the input signal, the state update is redirected to the output This ensures that yosys witness files for clk2fflogic designs have useful and stable public signal names. It also makes it possible to simulate a clk2fflogic witness on the original design (with some limitations when the original design is already using $ff cells). It might seem like setting the output of a clk2fflogic FF to update the input's initial value might not work in general, but it works fine for these reasons: * Witnesses for FFs are only present in the initial cycle, so we do not care about any later cycles. * The logic that clk2fflogic generates loops the output of the genreated FF back to the input, with muxes in between to apply any edge or level sensitive updates. So when there are no active updates in the current gclk cycle, there is a combinational path from the output back to the input. * The logic clk2fflogic generates makes sure that an edge sensitive update cannot be active in the first cycle (i.e. the past initial value is assumed to be whatever it needs to be to avoid an edge). * When a level sensitive update is active in the first gclk cycle, it is actively driving the output for the whole gclk cycle, so ignoring any witness initialization is the correct behavior. --- backends/aiger/aiger.cc | 3 +++ backends/btor/btor.cc | 5 ++++- backends/smt2/smt2.cc | 3 ++- passes/cmds/rename.cc | 12 ++++++++++-- passes/opt/opt_clean.cc | 6 ++++-- passes/sat/clk2fflogic.cc | 20 ++++++++++++++++---- passes/sat/sim.cc | 10 ++++++++++ 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 97acf937c..e39e17715 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -733,6 +733,9 @@ struct AigerWriter auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q); SigSpec sig = sigmap(sig_qy); + if (cell->get_bool_attribute(ID(clk2fflogic))) + sig_qy = cell->getPort(ID::D); // For a clk2fflogic $_FF_ the named signal is the D input not the Q output + for (int i = 0; i < GetSize(sig_qy); i++) { if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr) continue; diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 4c43e91e7..9cfd967e5 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -728,7 +728,10 @@ struct BtorWorker else btorf("%d state %d %s\n", nid, sid, log_id(symbol)); - ywmap_state(sig_q); + if (cell->get_bool_attribute(ID(clk2fflogic))) + ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output + else + ywmap_state(sig_q); if (nid_init_val >= 0) { int nid_init = next_nid++; diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 0ca3fbcac..7b48be299 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -626,8 +626,9 @@ struct Smt2Worker } bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); + bool clk2fflogic = cell->type == ID($anyinit) && cell->get_bool_attribute(ID(clk2fflogic)); int smtoffset = 0; - for (auto chunk : cell->getPort(QY).chunks()) { + for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) { if (chunk.is_wire()) decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); smtoffset += chunk.width; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 6bd317ed0..da4ba2f17 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -139,7 +139,12 @@ static bool rename_witness(RTLIL::Design *design, dict &ca if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { has_witness_signals = true; - auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; + IdString QY; + bool clk2fflogic = false; + if (cell->type == ID($anyinit)) + QY = (clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic))) ? ID::D : ID::Q; + else + QY = ID::Y; auto sig_out = cell->getPort(QY); for (auto chunk : sig_out.chunks()) { @@ -151,7 +156,10 @@ static bool rename_witness(RTLIL::Design *design, dict &ca auto new_id = module->uniquify("\\_witness_." + name); auto new_wire = module->addWire(new_id, GetSize(sig_out)); new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 }); - module->connect({sig_out, new_wire}); + if (clk2fflogic) + module->connect({new_wire, sig_out}); + else + module->connect({sig_out, new_wire}); cell->setPort(QY, new_wire); break; } diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index dde7c5299..cb2490dc7 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -292,10 +292,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (!purge_mode) for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; - if (ct_reg.cell_known(cell->type)) + if (ct_reg.cell_known(cell->type)) { + bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic)); for (auto &it2 : cell->connections()) - if (ct_reg.cell_output(cell->type, it2.first)) + if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first)) register_signals.add(it2.second); + } for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index bba2cbbec..3dc96ecce 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -80,15 +80,27 @@ struct Clk2fflogicPass : public Pass { return module->Eqx(NEW_ID, {sampled_sig, sig}, polarity ? SigSpec {State::S0, State::S1} : SigSpec {State::S1, State::S0}); } // Sampled and current value of a data signal. - SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine) { + SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine, bool set_attribute = false) { std::string sig_str = log_signal(sig); sig_str.erase(std::remove(sig_str.begin(), sig_str.end(), ' '), sig_str.end()); + + Wire *sampled_sig = module->addWire(NEW_ID_SUFFIX(stringf("%s#sampled", sig_str.c_str())), GetSize(sig)); sampled_sig->attributes[ID::init] = init; + + Cell *cell; if (is_fine) - module->addFfGate(NEW_ID, sig, sampled_sig); + cell = module->addFfGate(NEW_ID, sig, sampled_sig); else - module->addFf(NEW_ID, sig, sampled_sig); + cell = module->addFf(NEW_ID, sig, sampled_sig); + + if (set_attribute) { + for (auto &chunk : sig.chunks()) + if (chunk.wire != nullptr) + chunk.wire->set_bool_attribute(ID::keep); + cell->set_bool_attribute(ID(clk2fflogic)); + } + return {sampled_sig, sig}; } SigSpec mux(Module *module, SigSpec a, SigSpec b, SigSpec s, bool is_fine) { @@ -213,7 +225,7 @@ struct Clk2fflogicPass : public Pass { if (ff.has_clk) ff.unmap_ce_srst(); - auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine).sampled; + auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled; if (ff.has_clk) { // The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index b970ee568..2f353672b 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -140,6 +140,7 @@ struct SimInstance dict> upd_outports; dict in_parent_drivers; + dict clk2fflogic_drivers; pool dirty_bits; pool dirty_cells; @@ -270,6 +271,11 @@ struct SimInstance ff.past_srst = State::Sx; ff.data = ff_data; ff_database[cell] = ff; + + if (cell->get_bool_attribute(ID(clk2fflogic))) { + for (int i = 0; i < ff_data.width; i++) + clk2fflogic_drivers.emplace(sigmap(ff_data.sig_d[i]), sigmap(ff_data.sig_q[i])); + } } if (cell->is_mem_cell()) @@ -389,6 +395,10 @@ struct SimInstance auto sigbit = sig[i]; auto sigval = value[i]; + auto clk2fflogic_driver = clk2fflogic_drivers.find(sigbit); + if (clk2fflogic_driver != clk2fflogic_drivers.end()) + sigbit = clk2fflogic_driver->second; + auto in_parent_driver = in_parent_drivers.find(sigbit); if (in_parent_driver == in_parent_drivers.end()) set_state(sigbit, sigval); From 3aee765793689f775e4e423be3fd6851e3513620 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 4 Apr 2023 21:31:26 +1200 Subject: [PATCH 05/43] Initial version of memory mapping doc --- docs/source/CHAPTER_Memorymap.rst | 652 ++++++++++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 653 insertions(+) create mode 100644 docs/source/CHAPTER_Memorymap.rst diff --git a/docs/source/CHAPTER_Memorymap.rst b/docs/source/CHAPTER_Memorymap.rst new file mode 100644 index 000000000..e18ee9bb2 --- /dev/null +++ b/docs/source/CHAPTER_Memorymap.rst @@ -0,0 +1,652 @@ +.. _chapter:memorymap: + +Memory mapping +============== + +Documentation for the Yosys memory_libmap memory mapper. + +See also: `passes/memory/memlib.md `_ + +Supported patterns +------------------ + +Asynchronous-read RAM +~~~~~~~~~~~~~~~~~~~~~ + +- This will result in LUT RAM on supported targets + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + always @(posedge clk) + if (write_enable) + mem[write_addr] <= write_data; + assign read_data = mem[read_addr]; + +Synchronous SDP with clock domain crossing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in block RAM or LUT RAM depending on size +- No behavior guarantees in case of simultaneous read and write to the same address + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge write_clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + always @(posedge read_clk) begin + if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous SDP read first +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- The read and write parts can be in the same or different processes. +- Will result in block RAM or LUT RAM depending on size +- As long as the same clock is used for both, yosys will ensure read-first behavior. This may + require extra circuitry on some targets for block RAM. If this is not necessary, use one of the + patterns below. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous SDP with undefined collision behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Like above, but the read value is undefined when read and write ports target the same address in + the same cycle + + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) begin + read_data <= mem[read_addr]; + + // 👇 this if block 👇 + if (write_enable && read_addr == write_addr) + read_data <= 'x; + end + end + +- Or below, using the no_rw_check attribute + +.. code:: verilog + + (* no_rw_check *) + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous SDP with write-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in block RAM or LUT RAM depending on size +- May use additional circuitry for block RAM if write-first is not natively supported. Will always + use additional circuitry for LUT RAM. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) begin + read_data <= mem[read_addr]; + if (write_enable && read_addr == write_addr) + read_data <= write_data; + end + end + +Synchronous SDP with write-first behavior (alternate pattern) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- This pattern is supported for compatibility, but is much less flexible than the above + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + read_addr_reg <= read_addr; + end + + assign read_data = mem[read_addr_reg]; + +Asynchronous-read single-port RAM +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port LUT RAM on supported targets + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + always @(posedge clk) + if (write_enable) + mem[addr] <= write_data; + assign read_data = mem[addr]; + +Synchronous single-port RAM with mutually exclusive read/write +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port block RAM or LUT RAM depending on size +- This is the correct pattern to infer ice40 SPRAM (with manual ram_style selection) +- On targets that don't support read/write block RAM ports (eg. ice40), will result in SDP block RAM instead +- For block RAM, will use "NO_CHANGE" mode if available + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + else if (read_enable) + read_data <= mem[addr]; + end + +Synchronous single-port RAM with read-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will only result in single-port block RAM when read-first behavior is natively supported; + otherwise, SDP RAM with additional circuitry will be used +- Many targets (Xilinx, ECP5, …) can only natively support read-first/write-first single-port RAM + (or TDP RAM) where the write_enable signal implies the read_enable signal (ie. can never write + without reading). The memory inference code will run a simple SAT solver on the control signals to + determine if this is the case, and insert emulation circuitry if it cannot be easily proven. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + if (read_enable) + read_data <= mem[addr]; + end + +Synchronous single-port RAM with write-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port block RAM or LUT RAM when supported +- Block RAMs will require extra circuitry if write-first behavior not natively supported + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + if (read_enable) + if (write_enable) + read_data <= write_data; + else + read_data <= mem[addr]; + end + +Synchronous read port with initial value +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Initial read port values can be combined with any other supported pattern +- If block RAM is used and initial read port values are not natively supported by the target, small + emulation circuit will be inserted + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + reg [DATA_WIDTH - 1 : 0] read_data; + initial read_data = 'h1234; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous read port with synchronous reset (reset priority over enable) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Synchronous resets can be combined with any other supported pattern (except that synchronous reset + and asynchronous reset cannot be used on a single read port) +- If block RAM is used and synchronous resets are not natively supported by the target, small + emulation circuit will be inserted + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_reset) + read_data <= {sval}; + else if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous read port with synchronous reset (enable priority over reset) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Synchronous resets can be combined with any other supported pattern (except that synchronous reset + and asynchronous reset cannot be used on a single read port) +- If block RAM is used and synchronous resets are not natively supported by the target, small + emulation circuit will be inserted + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + if (read_reset) + read_data <= 'h1234; + else + read_data <= mem[read_addr]; + end + +Synchronous read port with asynchronous reset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Asynchronous resets can be combined with any other supported pattern (except that synchronous + reset and asynchronous reset cannot be used on a single read port) +- If block RAM is used and asynchronous resets are not natively supported by the target, small + emulation circuit will be inserted + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + always @(posedge clk, posedge reset_read) begin + if (reset_read) + read_data <= 'h1234; + else if (read_enable) + read_data <= mem[read_addr]; + end + +Initial data +~~~~~~~~~~~~ + +- Most FPGA targets support initializing all kinds of memory to user-provided values +- If explicit initialization is not used, initial memory value is undefined +- Initial data can be provided by either initial statements writing memory cells one by one or + $readmemh/$readmemb system tasks + +Write port with byte enables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Byte enables can be used with any supported pattern +- To ensure that multiple writes will be merged into one port, they need to have disjoint bit + ranges, have the same address, and the same clock +- Any write enable granularity will be accepted (down to per-bit write enables), but using smaller + granularity than natively supported by the target is very likely to be inefficient (eg. using + 4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit + units or splitting the RAM into two block RAMs) + +.. code:: verilog + + reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable[0]) + mem[write_addr][7:0] <= write_data[7:0]; + if (write_enable[1]) + mem[write_addr][15:8] <= write_data[15:8]; + if (write_enable[2]) + mem[write_addr][23:16] <= write_data[23:16]; + if (write_enable[3]) + mem[write_addr][31:24] <= write_data[31:24]; + if (read_enable) + read_data <= mem[read_addr]; + end + +Asymmetric memory — general notes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To construct an asymmetric memory (memory with read/write ports of differing widths): + +- Declare the memory with the width of the narrowest intended port +- Split all wide ports into multiple narrow ports +- To ensure the wide ports will be correctly merged: + + - For the address, use a concatenation of actual address in the high bits and a constant in the + low bits + - Ensure the actual address is identical for all ports belonging to the wide port + - Ensure that clock is identical + - For read ports, ensure that enable/reset signals are identical (for write ports, the enable + signal may vary — this will result in using the byte enable functionality) + +- Asymmetric memory is supported on all targets, but may require emulation circuitry where not + natively supported +- Note: when the memory is larger than the underlying block RAM primitive, hardware asymmetric + memory support is likely not to be used even if present, as this is cheaper + +Asymmetric memory with wide synchronous read port +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [7:0] mem [0:255]; + wire [7:0] write_addr; + wire [5:0] read_addr; + wire [7:0] write_data; + reg [31:0] read_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) begin + read_data[7:0] <= mem[{read_addr, 2'b00}]; + read_data[15:8] <= mem[{read_addr, 2'b01}]; + read_data[23:16] <= mem[{read_addr, 2'b10}]; + read_data[31:24] <= mem[{read_addr, 2'b11}]; + end + end + +Wide asynchronous read port +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Note: the only target natively supporting this pattern is Xilinx UltraScale + +.. code:: verilog + + reg [7:0] mem [0:511]; + wire [8:0] write_addr; + wire [5:0] read_addr; + wire [7:0] write_data; + wire [63:0] read_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + assign read_data[7:0] = mem[{read_addr, 3'b000}]; + assign read_data[15:8] = mem[{read_addr, 3'b001}]; + assign read_data[23:16] = mem[{read_addr, 3'b010}]; + assign read_data[31:24] = mem[{read_addr, 3'b011}]; + assign read_data[39:32] = mem[{read_addr, 3'b100}]; + assign read_data[47:40] = mem[{read_addr, 3'b101}]; + assign read_data[55:48] = mem[{read_addr, 3'b110}]; + assign read_data[63:56] = mem[{read_addr, 3'b111}]; + +Wide write port +~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [7:0] mem [0:255]; + wire [5:0] write_addr; + wire [7:0] read_addr; + wire [31:0] write_data; + reg [7:0] read_data; + + always @(posedge clk) begin + if (write_enable[0]) + mem[{write_addr, 2'b00}] <= write_data[7:0]; + if (write_enable[1]) + mem[{write_addr, 2'b01}] <= write_data[15:8]; + if (write_enable[2]) + mem[{write_addr, 2'b10}] <= write_data[23:16]; + if (write_enable[3]) + mem[{write_addr, 2'b11}] <= write_data[31:24]; + if (read_enable) + read_data <= mem[read_addr]; + end + +True dual port memory — general notes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Many different variations of true dual port memory can be created by combining two single-port RAM + patterns on the same memory +- When TDP memory is used, memory inference code has much less maneuver room to create requested + semantics compared to individual single-port patterns (which can end up lowered to SDP memory + where necessary) — supported patterns depend strongly on the target +- In particular, when both ports have the same clock, it's likely that "undefined collision" mode + needs to be manually selected to enable TDP memory inference +- The examples below are non-exhaustive — many more combinations of port types are possible +- Note: if two write ports are in the same process, this defines a priority relation between them + (if both ports are active in the same clock, the later one wins). On almost all targets, this will + result in a bit of extra circuitry to ensure the priority semantics. If this is not what you want, + put them in separate processes. + + - Priority is not supported when using the verific front end and any priority semantics are ignored. + +True Dual Port — different clocks, exclusive read/write +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk_a) begin + if (write_enable_a) + mem[addr_a] <= write_data_a; + else if (read_enable_a) + read_data_a <= mem[addr_a]; + end + + always @(posedge clk_b) begin + if (write_enable_b) + mem[addr_b] <= write_data_b; + else if (read_enable_b) + read_data_b <= mem[addr_b]; + end + +True Dual Port — same clock, read-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- This requires hardware inter-port read-first behavior, and will only work on some targets (Xilinx, Nexus) + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable_a) + mem[addr_a] <= write_data_a; + if (read_enable_a) + read_data_a <= mem[addr_a]; + end + + always @(posedge clk) begin + if (write_enable_b) + mem[addr_b] <= write_data_b; + if (read_enable_b) + read_data_b <= mem[addr_b]; + end + +Multiple read ports +~~~~~~~~~~~~~~~~~~~ + +- The combination of a single write port with an arbitrary amount of read ports is supported on all + targets — if a multi-read port primitive is available (like Xilinx RAM64M), it'll be used as + appropriate. Otherwise, the memory will be automatically split into multiple primitives. + +.. code:: verilog + + reg [31:0] mem [0:31]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + assign read_data_a = mem[read_addr_a]; + assign read_data_b = mem[read_addr_b]; + assign read_data_c = mem[read_addr_c]; + +Memory kind selection +~~~~~~~~~~~~~~~~~~~~~ + +- The memory inference code will automatically pick target memory primitive based on memory geometry + and features used. Depending on the target, there can be up to four memory primitive classes + available for selection: + +- FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers + + - Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain + - Can handle arbitrary number and kind of read ports + +- LUT RAM (aka distributed RAM): uses LUT storage as RAM + + - Supported on most FPGAs (with notable exception of ice40) + - Usually has one synchronous write port, one or more asynchronous read ports + - Small + - Will never be used for ROMs (lowering to plain LUTs is always better) + +- Block RAM: dedicated memory tiles + + - Supported on basically all FPGAs + - Supports only synchronous reads + - Two ports with separate clocks + - Usually supports true dual port (with notable exception of ice40 that only supports SDP) + - Usually supports asymmetric memories and per-byte write enables + - Several kilobits in size + +- Huge RAM: + + - Only supported on several targets: + + - Some Xilinx UltraScale devices (UltraRAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + - Initial data must be all-0 + + - Some ice40 devices (SPRAM) + + - Single port with mutually exclusive synchronous read and write + - Does not support initial data + + - Nexus (large RAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + + - Will not be automatically selected by memory inference code, needs explicit opt-in via + ram_style attribute + +In general, you can expect the automatic selection process to work roughly like this: + +- If any read port is asynchronous, only LUT RAM (or FF RAM) can be used. +- If there is more than one write port, only block RAM can be used, and this needs to be a + hardware-supported true dual port pattern + + - … unless all write ports are in the same clock domain, in which case FF RAM can also be used, + but this is generally not what you want for anything but really small memories + +- Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size + +This process can be overridden by attaching a ram_style attribute to the memory: + +- `(* ram_style = "logic" *)` selects FF RAM +- `(* ram_style = "distributed" *)` selects LUT RAM +- `(* ram_style = "block" *)` selects block RAM +- `(* ram_style = "huge" *)` selects huge RAM + +It is an error if this override cannot be realized for the given target. + +Many alternate spellings of the attribute are also accepted, for compatibility with other software. + +Not yet supported patterns +-------------------------- + +Synchronous SDP with write-first behavior via blocking assignments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Would require modifications to the Yosys Verilog frontend. +- Use `Synchronous SDP with write-first behavior`_ instead + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] = write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + +Asymmetric memories via part selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Would require major changes to the Verilog frontend. +- Build wide ports out of narrow ports instead (see `Asymmetric memory with wide synchronous read port`_) + +.. code:: verilog + + reg [31:0] mem [2**ADDR_WIDTH - 1 : 0]; + + wire [1:0] byte_lane; + wire [7:0] write_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr][byte_lane * 8 +: 8] <= write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + + +Undesired patterns +------------------ + +Asynchronous writes +~~~~~~~~~~~~~~~~~~~ + +- Not supported in modern FPGAs +- Not supported in yosys code anyhow + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @* begin + if (write_enable) + mem[write_addr] = write_data; + end + + assign read_data = mem[read_addr]; + diff --git a/docs/source/index.rst b/docs/source/index.rst index fb8643072..111aea873 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -42,6 +42,7 @@ Yosys manual CHAPTER_Verilog.rst CHAPTER_Optimize.rst CHAPTER_Techmap.rst + CHAPTER_Memorymap.rst CHAPTER_Eval.rst .. raw:: latex From 26555a998d0aa8bbeba331ad6eb5a870c719f606 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 24 May 2023 17:52:14 +0200 Subject: [PATCH 06/43] show -colorattr: extend colors to arrows when wires have attribute --- passes/cmds/show.cc | 48 +++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index dd7de8273..46aa16c90 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -84,7 +84,7 @@ struct ShowWorker std::string nextColor() { if (currentColor == 0) - return "color=\"black\""; + return "color=\"black\", fontcolor=\"black\""; return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1); } @@ -97,18 +97,15 @@ struct ShowWorker std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor) { - sig.sort_and_unify(); - for (auto &c : sig.chunks()) { - if (c.wire != nullptr) - for (auto &s : color_selections) - if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0) - return stringf("color=\"%s\"", s.first.c_str()); - } + std::string color = findColor(sig); + if (!color.empty()) return color; return defaultColor; } std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor) { + std::string color = findColor(conn); + if (!color.empty()) return color; return nextColor(conn.first, nextColor(conn.second, defaultColor)); } @@ -131,12 +128,28 @@ struct ShowWorker return stringf("style=\"setlinewidth(3)\", label=\"<%d>\"", bits); } - const char *findColor(std::string member_name) + std::string findColor(RTLIL::SigSpec sig) + { + sig.sort_and_unify(); + for (auto &c : sig.chunks()) { + if (c.wire != nullptr) + return findColor(c.wire->name); + } + return ""; + } + + std::string findColor(const RTLIL::SigSig &conn) + { + std::string firstColor = findColor(conn.first); + if (findColor(conn.second) == firstColor) return firstColor; + return ""; + } + + std::string findColor(IdString member_name) { for (auto &s : color_selections) if (s.second.selected_member(module->name, member_name)) { - dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str())); - return dot_escape_store.back().c_str(); + return stringf("color=\"%s\", fontcolor=\"%s\"", s.first.c_str(), s.first.c_str()); } RTLIL::Const colorattr_value; @@ -155,8 +168,7 @@ struct ShowWorker colorattr_cache[colorattr_value] = (next_id % 8) + 1; } - dot_escape_store.push_back(stringf(", colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value))); - return dot_escape_store.back().c_str(); + return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value)); } const char *findLabel(std::string member_name) @@ -414,9 +426,9 @@ struct ShowWorker if (wire->port_input || wire->port_output) shape = "octagon"; if (wire->name.isPublic()) { - fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n", + fprintf(f, "n%d [ shape=%s, label=\"%s\", %s ];\n", id2num(wire->name), shape, findLabel(wire->name.str()), - nextColor(RTLIL::SigSpec(wire), "color=\"black\"").c_str()); + nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str()); if (wire->port_input) all_sources.insert(stringf("n%d", id2num(wire->name))); else if (wire->port_output) @@ -478,14 +490,16 @@ struct ShowWorker conn.second, ct.cell_output(cell->type, conn.first)); } + std::string color = findColor(cell->name); + if (!color.empty()) color = ", " + color; #ifdef CLUSTER_CELLS_AND_PORTBOXES if (!code.empty()) fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n", - id2num(cell->name), id2num(cell->name), label_string.c_str(), findColor(cell->name), code.c_str()); + id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); else #endif fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s", - id2num(cell->name), label_string.c_str(), findColor(cell->name.str()), code.c_str()); + id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); } for (auto &it : module->processes) From 1cd1e57e3c955a61970480fdc0295d5e3484746c Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Mon, 29 May 2023 16:53:50 +0200 Subject: [PATCH 07/43] Fix use of non-POSIX test expressions in Makefile POSIX test only allows "=" for string comparison. Accepting "==" as an alias is a bashism. Even the bash manpage discourages its use. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index bcb6a066c..af6603302 100644 --- a/Makefile +++ b/Makefile @@ -797,13 +797,13 @@ ifneq ($(ABCREV),default) $(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi - $(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \ + $(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \ fi # set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string - $(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \ + $(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ echo "Compiling local copy of ABC"; \ - elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \ + elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" = "$$rev"); then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ echo "Pulling ABC from $(ABCURL):"; set -x; \ test -d abc || git clone $(ABCURL) abc; \ From c244a7161b60e74a4db767016b45f4e5dec9920c Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Tue, 30 May 2023 09:05:43 +0200 Subject: [PATCH 08/43] gatemate: Fix SDP read behavior --- techlibs/gatemate/cells_sim.v | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/techlibs/gatemate/cells_sim.v b/techlibs/gatemate/cells_sim.v index 12e01d2df..dbf2d514a 100644 --- a/techlibs/gatemate/cells_sim.v +++ b/techlibs/gatemate/cells_sim.v @@ -733,13 +733,12 @@ module CC_BRAM_20K ( // SDP read port always @(posedge clkb) begin - // "NO_CHANGE" only for (k=0; k < B_RD_WIDTH; k=k+1) begin if (k < 20) begin - if (enb && !wea) A_DO_out[k] <= memory[addrb+k]; + if (enb) A_DO_out[k] <= memory[addrb+k]; end else begin // use both ports - if (enb && !wea) B_DO_out[k-20] <= memory[addrb+k]; + if (enb) B_DO_out[k-20] <= memory[addrb+k]; end end end @@ -1274,13 +1273,12 @@ module CC_BRAM_40K ( // SDP read port always @(posedge clkb) begin - // "NO_CHANGE" only for (k=0; k < B_RD_WIDTH; k=k+1) begin if (k < 40) begin - if (enb && !wea) A_DO_out[k] <= memory[addrb+k]; + if (enb) A_DO_out[k] <= memory[addrb+k]; end else begin // use both ports - if (enb && !wea) B_DO_out[k-40] <= memory[addrb+k]; + if (enb) B_DO_out[k-40] <= memory[addrb+k]; end end end From 2004a9ff4ac008cce94655233def519ca70e2948 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Tue, 30 May 2023 09:06:23 +0200 Subject: [PATCH 09/43] gatemate: Add CC_FIFO_40K simulation model --- techlibs/gatemate/cells_bb.v | 67 ------ techlibs/gatemate/cells_sim.v | 387 ++++++++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+), 67 deletions(-) diff --git a/techlibs/gatemate/cells_bb.v b/techlibs/gatemate/cells_bb.v index 87b91764f..f928a3d7a 100644 --- a/techlibs/gatemate/cells_bb.v +++ b/techlibs/gatemate/cells_bb.v @@ -131,70 +131,3 @@ module CC_USR_RSTN ( output USR_RSTN ); endmodule - -(* blackbox *) -module CC_FIFO_40K ( - output A_ECC_1B_ERR, - output B_ECC_1B_ERR, - output A_ECC_2B_ERR, - output B_ECC_2B_ERR, - // FIFO pop port - output [39:0] A_DO, - output [39:0] B_DO, - (* clkbuf_sink *) - input A_CLK, - input A_EN, - // FIFO push port - input [39:0] A_DI, - input [39:0] B_DI, - input [39:0] A_BM, - input [39:0] B_BM, - (* clkbuf_sink *) - input B_CLK, - input B_EN, - input B_WE, - // FIFO control - input F_RST_N, - input [12:0] F_ALMOST_FULL_OFFSET, - input [12:0] F_ALMOST_EMPTY_OFFSET, - // FIFO status signals - output F_FULL, - output F_EMPTY, - output F_ALMOST_FULL, - output F_ALMOST_EMPTY, - output F_RD_ERROR, - output F_WR_ERROR, - output [15:0] F_RD_PTR, - output [15:0] F_WR_PTR -); - // Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED - parameter LOC = "UNPLACED"; - - // Offset configuration - parameter [12:0] ALMOST_FULL_OFFSET = 12'b0; - parameter [12:0] ALMOST_EMPTY_OFFSET = 12'b0; - - // Port Widths - parameter A_WIDTH = 0; - parameter B_WIDTH = 0; - - // RAM and Write Modes - parameter RAM_MODE = "SDP"; // "TPD" or "SDP" - parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC" - - // Inverting Control Pins - parameter A_CLK_INV = 1'b0; - parameter B_CLK_INV = 1'b0; - parameter A_EN_INV = 1'b0; - parameter B_EN_INV = 1'b0; - parameter A_WE_INV = 1'b0; - parameter B_WE_INV = 1'b0; - - // Output Register - parameter A_DO_REG = 1'b0; - parameter B_DO_REG = 1'b0; - - // Error Checking and Correction - parameter A_ECC_EN = 1'b0; - parameter B_ECC_EN = 1'b0; -endmodule diff --git a/techlibs/gatemate/cells_sim.v b/techlibs/gatemate/cells_sim.v index dbf2d514a..93f463043 100644 --- a/techlibs/gatemate/cells_sim.v +++ b/techlibs/gatemate/cells_sim.v @@ -1410,6 +1410,393 @@ module CC_BRAM_40K ( endgenerate endmodule +module CC_FIFO_40K ( + output A_ECC_1B_ERR, + output B_ECC_1B_ERR, + output A_ECC_2B_ERR, + output B_ECC_2B_ERR, + // FIFO pop port + output [39:0] A_DO, + output [39:0] B_DO, + (* clkbuf_sink *) + input A_CLK, + input A_EN, + // FIFO push port + input [39:0] A_DI, + input [39:0] B_DI, + input [39:0] A_BM, + input [39:0] B_BM, + (* clkbuf_sink *) + input B_CLK, + input B_EN, + input B_WE, + // FIFO control + input F_RST_N, + input [14:0] F_ALMOST_FULL_OFFSET, + input [14:0] F_ALMOST_EMPTY_OFFSET, + // FIFO status signals + output F_FULL, + output F_EMPTY, + output F_ALMOST_FULL, + output F_ALMOST_EMPTY, + output F_RD_ERROR, + output F_WR_ERROR, + output [15:0] F_RD_PTR, + output [15:0] F_WR_PTR +); + // Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED + parameter LOC = "UNPLACED"; + + // Offset configuration + parameter DYN_STAT_SELECT = 1'b0; + parameter [14:0] ALMOST_FULL_OFFSET = 15'b0; + parameter [14:0] ALMOST_EMPTY_OFFSET = 15'b0; + + // Port Widths + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + + // RAM and Write Modes + parameter RAM_MODE = "TDP"; // "TDP" or "SDP" + parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC" + + // Inverting Control Pins + parameter A_CLK_INV = 1'b0; + parameter B_CLK_INV = 1'b0; + parameter A_EN_INV = 1'b0; + parameter B_EN_INV = 1'b0; + parameter A_WE_INV = 1'b0; + parameter B_WE_INV = 1'b0; + + // Output Register + parameter A_DO_REG = 1'b0; + parameter B_DO_REG = 1'b0; + + // Error Checking and Correction + parameter A_ECC_EN = 1'b0; + parameter B_ECC_EN = 1'b0; + + integer i, k; + + // 512 x 80 bit + reg [40959:0] memory = 40960'b0; + + reg [15:0] counter_max; + reg [15:0] sram_depth; + localparam tp = (A_WIDTH == 1) ? 15 : + (A_WIDTH == 2) ? 14 : + (A_WIDTH == 5) ? 13 : + (A_WIDTH == 10) ? 12 : + (A_WIDTH == 20) ? 11 : + (A_WIDTH == 40) ? 10 : 9; + + initial begin + // Check parameters + if ((RAM_MODE != "SDP") && (RAM_MODE != "TDP")) begin + $display("ERROR: Illegal RAM MODE %d.", RAM_MODE); + $finish(); + end + if ((FIFO_MODE != "ASYNC") && (FIFO_MODE != "SYNC")) begin + $display("ERROR: Illegal FIFO MODE %d.", FIFO_MODE); + $finish(); + end + if ((RAM_MODE == "SDP") && (DYN_STAT_SELECT == 1)) begin + $display("ERROR: Dynamic offset configuration is not supported in %s mode.", RAM_MODE); + $finish(); + end + if ((RAM_MODE == "SDP") && ((A_WIDTH != 80) || (B_WIDTH != 80))) begin + $display("ERROR: SDP is ony supported in 80 bit mode."); + $finish(); + end + if ((A_WIDTH == 80) && (RAM_MODE == "TDP")) begin + $display("ERROR: Port A width of 80 bits is only supported in SDP mode."); + $finish(); + end + if ((B_WIDTH == 80) && (RAM_MODE == "TDP")) begin + $display("ERROR: Port B width of 80 bits is only supported in SDP mode."); + $finish(); + end + if ((A_WIDTH != 80) && (A_WIDTH != 40) && (A_WIDTH != 20) && (A_WIDTH != 10) && + (A_WIDTH != 5) && (A_WIDTH != 2) && (A_WIDTH != 1) && (A_WIDTH != 0)) begin + $display("ERROR: Illegal %s Port A width configuration %d.", RAM_MODE, A_WIDTH); + $finish(); + end + if ((B_WIDTH != 80) && (B_WIDTH != 40) && (B_WIDTH != 20) && (B_WIDTH != 10) && + (B_WIDTH != 5) && (B_WIDTH != 2) && (B_WIDTH != 1) && (B_WIDTH != 0)) begin + $display("ERROR: Illegal %s Port B width configuration %d.", RAM_MODE, B_WIDTH); + $finish(); + end + if (A_WIDTH != B_WIDTH) begin + $display("ERROR: The values of A_WIDTH and B_WIDTH must be equal."); + end + if ((A_ECC_EN == 1'b1) && (RAM_MODE != "SDP") && (A_WIDTH != 40)) begin + $display("ERROR: Illegal ECC Port A configuration. ECC mode requires TDP >=40 bit or SDP 80 bit, but is %s %d.", RAM_MODE, A_WIDTH); + $finish(); + end + // Set local parameters + if (A_WIDTH == 1) begin // A_WIDTH=B_WIDTH + counter_max = 2 * 32*1024 - 1; + sram_depth = 32*1024; + end + else if (A_WIDTH == 2) begin + counter_max = 2 * 16*1024 - 1; + sram_depth = 16*1024; + end + else if (A_WIDTH == 5) begin + counter_max = 2 * 8*1024 - 1; + sram_depth = 8*1024; + end + else if (A_WIDTH == 10) begin + counter_max = 2 * 4*1024 - 1; + sram_depth = 4*1024; + end + else if (A_WIDTH == 20) begin + counter_max = 2 * 2*1024 - 1; + sram_depth = 2*1024; + end + else if (A_WIDTH == 40) begin + counter_max = 2 * 1*1024 - 1; + sram_depth = 1*1024; + end + else begin // 80 bit SDP + counter_max = 2 * 512 - 1; + sram_depth = 512; + end + end + + // Internal signals + wire fifo_rdclk = A_CLK ^ A_CLK_INV; + wire fifo_wrclk = (FIFO_MODE == "ASYNC") ? (B_CLK ^ B_CLK_INV) : (A_CLK ^ A_CLK_INV); + wire [15:0] almost_full_offset = DYN_STAT_SELECT ? F_ALMOST_FULL_OFFSET : ALMOST_FULL_OFFSET; + wire [15:0] almost_empty_offset = DYN_STAT_SELECT ? F_ALMOST_EMPTY_OFFSET : ALMOST_EMPTY_OFFSET; + reg [39:0] A_DO_out = 0, A_DO_reg = 0; + reg [39:0] B_DO_out = 0, B_DO_reg = 0; + + // Status signals + reg fifo_full; + reg fifo_empty; + reg fifo_almost_full; + reg fifo_almost_empty; + assign F_FULL = fifo_full; + assign F_EMPTY = fifo_empty; + assign F_ALMOST_FULL = fifo_almost_full; + assign F_ALMOST_EMPTY = fifo_almost_empty; + assign F_WR_ERROR = (F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); + assign F_RD_ERROR = (F_EMPTY && (A_EN ^ A_EN_INV)); + assign ram_we = (~F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); + assign ram_en = (~F_EMPTY && (A_EN ^ A_EN_INV)); + + // Reset synchronizers + reg [1:0] aclk_reset_q, bclk_reset_q; + wire fifo_sync_rstn = aclk_reset_q; + wire fifo_async_wrrstn = bclk_reset_q; + wire fifo_async_rdrstn = aclk_reset_q; + + always @(posedge fifo_rdclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + aclk_reset_q <= 2'b0; + end + else begin + aclk_reset_q[1] <= aclk_reset_q[0]; + aclk_reset_q[0] <= 1'b1; + end + end + + always @(posedge fifo_wrclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + bclk_reset_q <= 2'b0; + end + else begin + bclk_reset_q[1] <= bclk_reset_q[0]; + bclk_reset_q[0] <= 1'b1; + end + end + + // Push/pop pointers + reg [15:0] rd_pointer, rd_pointer_int; + reg [15:0] wr_pointer, wr_pointer_int; + reg [15:0] rd_pointer_cmp, wr_pointer_cmp; + wire [15:0] rd_pointer_nxt; + wire [15:0] wr_pointer_nxt; + reg [15:0] fifo_rdaddr, rdaddr; + reg [15:0] fifo_wraddr, wraddr; + assign F_RD_PTR = fifo_rdaddr; + assign F_WR_PTR = fifo_wraddr; + + always @(posedge fifo_rdclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + rd_pointer <= 0; + rd_pointer_int <= 0; + end + else if (ram_en) begin + rd_pointer <= rd_pointer_nxt; + rd_pointer_int <= rd_pointer_nxt[15:1] ^ rd_pointer_nxt[14:0]; + end + end + + assign rd_pointer_nxt = (rd_pointer == counter_max) ? (0) : (rd_pointer + 1'b1); + + always @(posedge fifo_wrclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + wr_pointer <= 0; + wr_pointer_int <= 0; + end + else if (ram_we) begin + wr_pointer <= wr_pointer_nxt; + wr_pointer_int <= wr_pointer_nxt[15:1] ^ wr_pointer_nxt[14:0]; + end + end + + assign wr_pointer_nxt = (wr_pointer == counter_max) ? (0) : (wr_pointer + 1'b1); + + // Address synchronizers + reg [15:0] rd_pointer_sync, wr_pointer_sync; + reg [15:0] rd_pointer_sync_0, rd_pointer_sync_1; + reg [15:0] wr_pointer_sync_0, wr_pointer_sync_1; + + always @(posedge fifo_rdclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + wr_pointer_sync_0 <= 0; + wr_pointer_sync_1 <= 0; + end + else begin + wr_pointer_sync_0 <= wraddr; + wr_pointer_sync_1 <= wr_pointer_sync_0; + end + end + + always @(posedge fifo_wrclk or negedge F_RST_N) + begin + if (F_RST_N == 1'b0) begin + rd_pointer_sync_0 <= 0; + rd_pointer_sync_1 <= 0; + end + else begin + rd_pointer_sync_0 <= rdaddr; + rd_pointer_sync_1 <= rd_pointer_sync_0; + end + end + + always @(*) begin + fifo_wraddr = {wr_pointer[tp-1:0], {(15-tp){1'b0}}}; + fifo_rdaddr = {rd_pointer[tp-1:0], {(15-tp){1'b0}}}; + + rdaddr = {rd_pointer[tp], rd_pointer_int[tp-1:0]}; + wraddr = {{(15-tp){1'b0}}, wr_pointer[tp], wr_pointer_int[tp:0]}; + + if (FIFO_MODE == "ASYNC") + fifo_full = (wraddr[tp-2:0] == rd_pointer_sync_1[tp-2:0] ) && (wraddr[tp] != rd_pointer_sync_1[tp] ) && ( wraddr[tp-1] != rd_pointer_sync_1[tp-1] ); + else + fifo_full = (wr_pointer[tp-1:0] == rd_pointer[tp-1:0]) && (wr_pointer[tp] ^ rd_pointer[tp]); + + if (FIFO_MODE == "ASYNC") + fifo_empty = (wr_pointer_sync_1[tp:0] == rdaddr[tp:0]); + else + fifo_empty = (wr_pointer[tp:0] == rd_pointer[tp:0]); + + rd_pointer_cmp = (FIFO_MODE == "ASYNC") ? rd_pointer_sync : rd_pointer; + if (wr_pointer[tp] == rd_pointer_cmp[tp]) + fifo_almost_full = ((wr_pointer[tp-1:0] - rd_pointer_cmp[tp-1:0]) >= (sram_depth - almost_full_offset)); + else + fifo_almost_full = ((rd_pointer_cmp[tp-1:0] - wr_pointer[tp-1:0]) <= almost_full_offset); + + wr_pointer_cmp = (FIFO_MODE == "ASYNC") ? wr_pointer_sync : wr_pointer; + if (wr_pointer_cmp[tp] == rd_pointer[tp]) + fifo_almost_empty = ((wr_pointer_cmp[tp-1:0] - rd_pointer[tp-1:0]) <= almost_empty_offset); + else + fifo_almost_empty = ((rd_pointer[tp-1:0] - wr_pointer_cmp[tp-1:0]) >= (sram_depth - almost_empty_offset)); + end + + generate + always @(*) begin + wr_pointer_sync = 0; + rd_pointer_sync = 0; + for (i=tp; i >= 0; i=i-1) begin + if (i == tp) begin + wr_pointer_sync[i] = wr_pointer_sync_1[i]; + rd_pointer_sync[i] = rd_pointer_sync_1[i]; + end + else begin + wr_pointer_sync[i] = wr_pointer_sync_1[i] ^ wr_pointer_sync[i+1]; + rd_pointer_sync[i] = rd_pointer_sync_1[i] ^ rd_pointer_sync[i+1]; + end + end + end + if (RAM_MODE == "SDP") begin + // SDP push ports A+B + always @(posedge fifo_wrclk) + begin + for (k=0; k < A_WIDTH; k=k+1) begin + if (k < 40) begin + if (ram_we && A_BM[k]) memory[fifo_wraddr+k] <= A_DI[k]; + end + else begin // use both ports + if (ram_we && B_BM[k-40]) memory[fifo_wraddr+k] <= B_DI[k-40]; + end + end + end + // SDP pop ports A+B + always @(posedge fifo_rdclk) + begin + for (k=0; k < B_WIDTH; k=k+1) begin + if (k < 40) begin + if (ram_en) A_DO_out[k] <= memory[fifo_rdaddr+k]; + end + else begin // use both ports + if (ram_en) B_DO_out[k-40] <= memory[fifo_rdaddr+k]; + end + end + end + end + else if (RAM_MODE == "TDP") begin + // TDP pop port A + always @(posedge fifo_rdclk) + begin + for (i=0; i < A_WIDTH; i=i+1) begin + if (ram_en) begin + A_DO_out[i] <= memory[fifo_rdaddr+i]; + end + end + end + // TDP push port B + always @(posedge fifo_wrclk) + begin + for (i=0; i < B_WIDTH; i=i+1) begin + if (ram_we && B_BM[i]) + memory[fifo_wraddr+i] <= B_DI[i]; + end + end + end + endgenerate + + // Optional output register + generate + if (A_DO_REG) begin + always @(posedge fifo_rdclk) begin + A_DO_reg <= A_DO_out; + end + assign A_DO = A_DO_reg; + end + else begin + assign A_DO = A_DO_out; + end + if (B_DO_REG) begin + always @(posedge fifo_rdclk) begin + B_DO_reg <= B_DO_out; + end + assign B_DO = B_DO_reg; + end + else begin + assign B_DO = B_DO_out; + end + endgenerate +endmodule + // Models of the LUT2 tree primitives module CC_L2T4( output O, From 4b986c9c65a9ceb297f0843eb0a9f34d97baadb0 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 31 May 2023 17:38:46 +0200 Subject: [PATCH 10/43] fix wire color after BUF --- passes/cmds/show.cc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 46aa16c90..09fd3d4b6 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -106,7 +106,7 @@ struct ShowWorker { std::string color = findColor(conn); if (!color.empty()) return color; - return nextColor(conn.first, nextColor(conn.second, defaultColor)); + return defaultColor; } std::string nextColor(const RTLIL::SigSpec &sig) @@ -490,16 +490,14 @@ struct ShowWorker conn.second, ct.cell_output(cell->type, conn.first)); } - std::string color = findColor(cell->name); - if (!color.empty()) color = ", " + color; #ifdef CLUSTER_CELLS_AND_PORTBOXES if (!code.empty()) fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n", id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); else #endif - fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s", - id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); + fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s", + id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str()); } for (auto &it : module->processes) @@ -569,9 +567,9 @@ struct ShowWorker } else if (right_node[0] == 'x') { net_conn_map[left_node].out.insert({right_node, GetSize(conn.first)}); } else { - net_conn_map[right_node].in.insert({stringf("x%d:e", single_idx_count), GetSize(conn.first)}); - net_conn_map[left_node].out.insert({stringf("x%d:w", single_idx_count), GetSize(conn.first)}); - fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++); + net_conn_map[right_node].in.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); + net_conn_map[left_node].out.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); + fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\", %s];\n", single_idx_count++, findColor(conn).c_str()); } } } From 0707b911c7352398388a0331cf1660a78cc571c8 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Thu, 1 Jun 2023 10:02:30 +0200 Subject: [PATCH 11/43] show: add -viewer none option --- passes/cmds/show.cc | 49 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 09fd3d4b6..525c81d5b 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -329,7 +329,7 @@ struct ShowWorker } code += stringf("x%d [ shape=record, style=rounded, label=\"", dot_idx) \ - + join_label_pieces(label_pieces) + "\" ];\n"; + + join_label_pieces(label_pieces) + stringf("\", %s ];\n", nextColor(sig).c_str()); if (!port.empty()) { currentColor = xorshift32(currentColor); @@ -655,6 +655,7 @@ struct ShowPass : public Pass { log(" -viewer \n"); log(" Run the specified command with the graphics file as parameter.\n"); log(" On Windows, this pauses yosys until the viewer exits.\n"); + log(" Use \"-viewer none\" to not run any command.\n"); log("\n"); log(" -format \n"); log(" Generate a graphics file in the specified format. Use 'dot' to just\n"); @@ -915,28 +916,30 @@ struct ShowPass : public Pass { #if defined(YOSYS_DISABLE_SPAWN) log_assert(viewer_exe.empty() && !format.empty()); #else - if (!viewer_exe.empty()) { - #ifdef _WIN32 - // system()/cmd.exe does not understand single quotes nor - // background tasks on Windows. So we have to pause yosys - // until the viewer exits. - std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str()); - #else - std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str()); - #endif - log("Exec: %s\n", cmd.c_str()); - if (run_command(cmd) != 0) - log_cmd_error("Shell command failed!\n"); - } else - if (format.empty()) { - #ifdef __APPLE__ - std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str()); - #else - std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str()); - #endif - log("Exec: %s\n", cmd.c_str()); - if (run_command(cmd) != 0) - log_cmd_error("Shell command failed!\n"); + if (viewer_exe != "none") { + if (!viewer_exe.empty()) { + #ifdef _WIN32 + // system()/cmd.exe does not understand single quotes nor + // background tasks on Windows. So we have to pause yosys + // until the viewer exits. + std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str()); + #else + std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str()); + #endif + log("Exec: %s\n", cmd.c_str()); + if (run_command(cmd) != 0) + log_cmd_error("Shell command failed!\n"); + } else + if (format.empty()) { + #ifdef __APPLE__ + std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str()); + #else + std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str()); + #endif + log("Exec: %s\n", cmd.c_str()); + if (run_command(cmd) != 0) + log_cmd_error("Shell command failed!\n"); + } } #endif From 61387d78b7243c10fee247f8a6c4e1fc1a6a5659 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Mon, 5 Jun 2023 19:08:44 +0200 Subject: [PATCH 12/43] gatemate: Prevent implicit declaration of `ram_{we,en}` --- techlibs/gatemate/cells_sim.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/techlibs/gatemate/cells_sim.v b/techlibs/gatemate/cells_sim.v index 93f463043..e05ce811c 100644 --- a/techlibs/gatemate/cells_sim.v +++ b/techlibs/gatemate/cells_sim.v @@ -1583,8 +1583,8 @@ module CC_FIFO_40K ( assign F_ALMOST_EMPTY = fifo_almost_empty; assign F_WR_ERROR = (F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); assign F_RD_ERROR = (F_EMPTY && (A_EN ^ A_EN_INV)); - assign ram_we = (~F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); - assign ram_en = (~F_EMPTY && (A_EN ^ A_EN_INV)); + wire ram_we = (~F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); + wire ram_en = (~F_EMPTY && (A_EN ^ A_EN_INV)); // Reset synchronizers reg [1:0] aclk_reset_q, bclk_reset_q; From 73badeccefc5dedd2681ae5bcbc2893d92be2354 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 00:17:35 +0000 Subject: [PATCH 13/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5fc6ac29c..430dd5b7d 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.29+44 +YOSYS_VER := 0.29+58 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From f7a8284c7b095bca4bc2c65032144c4e3264ee4d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 6 Jun 2023 09:38:46 +0200 Subject: [PATCH 14/43] Release version 0.30 --- CHANGELOG | 12 +++++++++++- Makefile | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1430f8e2f..a46156408 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,18 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.29 .. Yosys 0.29-dev +Yosys 0.29 .. Yosys 0.30 -------------------------- + * New commands and options + - Added "recover_names" pass to recover names post-mapping. + + * Gowin support + - Added remaining primitives blackboxes. + + * Various + - "show -colorattr" will now color the cells, wires, and + connection arrows. + - "show -viewer none" will not execute viewer. Yosys 0.28 .. Yosys 0.29 -------------------------- diff --git a/Makefile b/Makefile index 430dd5b7d..3ff983198 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.29+58 +YOSYS_VER := 0.30 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -157,7 +157,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c5a60e.. | wc -l`/;" Makefile +# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c5a60e.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # From c5e4eec3ba932268a81bb352b3734e8868d874bf Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 6 Jun 2023 09:41:26 +0200 Subject: [PATCH 15/43] Next dev cycle --- CHANGELOG | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a46156408..0d433d4a3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.30 .. Yosys 0.31-dev +-------------------------- + Yosys 0.29 .. Yosys 0.30 -------------------------- * New commands and options diff --git a/Makefile b/Makefile index 3ff983198..6732998af 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30 +YOSYS_VER := 0.30+0 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -157,7 +157,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: -# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c5a60e.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline f7a8284.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # From b623888f6aa3b9d519cdb55bc7508021ae43dad2 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 6 Jun 2023 11:57:20 +0200 Subject: [PATCH 16/43] Update ABC to latest --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6732998af..f957c7730 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ bumpversion: # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = 2c1c83f +ABCREV = 0b36135 ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) From 0d4a67026732749931463c7f455f72c8e21d110c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 6 Jun 2023 14:37:14 +0200 Subject: [PATCH 17/43] Update ABC --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f957c7730..a5049bfa4 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ bumpversion: # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = 0b36135 +ABCREV = 1de4eaf ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) From 5813809ad9afbe1c38f65c6aae7c3441d7614d0b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:17:31 +0000 Subject: [PATCH 18/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6732998af..7afa6c472 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+0 +YOSYS_VER := 0.30+1 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From e6f7cf3b29adcfef592055158308c6ab2bc69288 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 9 Jun 2023 14:41:45 +0200 Subject: [PATCH 19/43] Update tests --- tests/arch/anlogic/mux.ys | 8 +++++--- tests/arch/ecp5/mux.ys | 4 ++-- tests/arch/gatemate/fsm.ys | 2 +- tests/arch/ice40/rom.ys | 2 +- tests/arch/intel_alm/mux.ys | 8 ++++---- tests/arch/nexus/mux.ys | 3 +-- tests/arch/xilinx/mux_lut4.ys | 9 +++++---- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/arch/anlogic/mux.ys b/tests/arch/anlogic/mux.ys index 3d5fe7c9a..89014b5e0 100644 --- a/tests/arch/anlogic/mux.ys +++ b/tests/arch/anlogic/mux.ys @@ -26,10 +26,12 @@ proc equiv_opt -assert -map +/anlogic/cells_sim.v synth_anlogic # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux8 # Constrain all select calls below inside the top module -select -assert-count 3 t:AL_MAP_LUT4 -select -assert-count 1 t:AL_MAP_LUT6 +select -assert-max 3 t:AL_MAP_LUT3 +select -assert-max 3 t:AL_MAP_LUT4 +select -assert-max 1 t:AL_MAP_LUT5 +select -assert-max 1 t:AL_MAP_LUT6 -select -assert-none t:AL_MAP_LUT4 t:AL_MAP_LUT6 %% t:* %D +select -assert-none t:AL_MAP_LUT3 t:AL_MAP_LUT4 t:AL_MAP_LUT5 t:AL_MAP_LUT6 %% t:* %D design -load read hierarchy -top mux16 diff --git a/tests/arch/ecp5/mux.ys b/tests/arch/ecp5/mux.ys index db63dda5f..daa9e86f2 100644 --- a/tests/arch/ecp5/mux.ys +++ b/tests/arch/ecp5/mux.ys @@ -28,8 +28,8 @@ equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux8 # Constrain all select calls below inside the top module select -assert-max 1 t:L6MUX21 -select -assert-max 7 t:LUT4 -select -assert-max 2 t:PFUMX +select -assert-max 8 t:LUT4 +select -assert-max 3 t:PFUMX select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D diff --git a/tests/arch/gatemate/fsm.ys b/tests/arch/gatemate/fsm.ys index 6b43ead7a..506862c90 100644 --- a/tests/arch/gatemate/fsm.ys +++ b/tests/arch/gatemate/fsm.ys @@ -15,6 +15,6 @@ cd fsm # Constrain all select calls below inside the top module select -assert-count 1 t:CC_BUFG select -assert-count 6 t:CC_DFF select -assert-max 5 t:CC_LUT2 -select -assert-max 4 t:CC_LUT3 +select -assert-max 6 t:CC_LUT3 select -assert-max 9 t:CC_LUT4 select -assert-none t:CC_BUFG t:CC_DFF t:CC_LUT2 t:CC_LUT3 t:CC_LUT4 %% t:* %D diff --git a/tests/arch/ice40/rom.ys b/tests/arch/ice40/rom.ys index 41d214e2a..d795e206b 100644 --- a/tests/arch/ice40/rom.ys +++ b/tests/arch/ice40/rom.ys @@ -4,5 +4,5 @@ flatten equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module -select -assert-count 5 t:SB_LUT4 +select -assert-max 6 t:SB_LUT4 select -assert-none t:SB_LUT4 %% t:* %D diff --git a/tests/arch/intel_alm/mux.ys b/tests/arch/intel_alm/mux.ys index 6fb6ae80a..20969ead3 100644 --- a/tests/arch/intel_alm/mux.ys +++ b/tests/arch/intel_alm/mux.ys @@ -69,7 +69,7 @@ proc equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclonev -noiopad -noclkbuf # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux16 # Constrain all select calls below inside the top module -select -assert-count 1 t:MISTRAL_ALUT3 +select -assert-max 1 t:MISTRAL_ALUT3 select -assert-max 2 t:MISTRAL_ALUT5 select -assert-max 5 t:MISTRAL_ALUT6 select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D @@ -81,8 +81,8 @@ proc equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclone10gx -noiopad -noclkbuf # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux16 # Constrain all select calls below inside the top module -select -assert-count 1 t:MISTRAL_ALUT3 -select -assert-count 2 t:MISTRAL_ALUT5 -select -assert-count 4 t:MISTRAL_ALUT6 +select -assert-max 1 t:MISTRAL_ALUT3 +select -assert-max 2 t:MISTRAL_ALUT5 +select -assert-max 5 t:MISTRAL_ALUT6 select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D diff --git a/tests/arch/nexus/mux.ys b/tests/arch/nexus/mux.ys index 0e12d674a..280d3e48f 100644 --- a/tests/arch/nexus/mux.ys +++ b/tests/arch/nexus/mux.ys @@ -36,8 +36,7 @@ proc equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux16 # Constrain all select calls below inside the top module -select -assert-min 11 t:LUT4 select -assert-max 12 t:LUT4 -select -assert-count 1 t:WIDEFN9 +select -assert-max 2 t:WIDEFN9 select -assert-none t:IB t:OB t:VLO t:VHI t:LUT4 t:WIDEFN9 %% t:* %D diff --git a/tests/arch/xilinx/mux_lut4.ys b/tests/arch/xilinx/mux_lut4.ys index 3e3256993..147601dce 100644 --- a/tests/arch/xilinx/mux_lut4.ys +++ b/tests/arch/xilinx/mux_lut4.ys @@ -30,12 +30,13 @@ proc equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux8 # Constrain all select calls below inside the top module -select -assert-count 4 t:LUT1 -select -assert-count 3 t:LUT4 -select -assert-count 2 t:MUXF5 +select -assert-max 5 t:LUT1 +select -assert-max 3 t:LUT3 +select -assert-max 3 t:LUT4 +select -assert-max 3 t:MUXF5 select -assert-count 1 t:MUXF6 -select -assert-none t:LUT1 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D +select -assert-none t:LUT1 t:LUT3 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D design -load read From dcc4d6e90be3ad349eb6497adf6dbe2781e8bd57 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 9 Jun 2023 15:21:22 +0200 Subject: [PATCH 20/43] yosys-witness: Don't treat aiw x-bits as don't change While treating initialization only bits as don't change during later cycles is correct, actual x-bits should be kept as x-bits. --- backends/smt2/witness.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/smt2/witness.py b/backends/smt2/witness.py index 8d0cc8112..7d5a2469e 100644 --- a/backends/smt2/witness.py +++ b/backends/smt2/witness.py @@ -194,7 +194,7 @@ def aiw2yw(input, mapfile, output): values = WitnessValues() for i, v in enumerate(inline): - if v == "x" or outyw.t > 0 and i in aiger_map.init_inputs: + if outyw.t > 0 and i in aiger_map.init_inputs: continue try: From 75cf79588e6b877fdbb45836abf6d177130ee27d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 12 Jun 2023 10:01:01 +0200 Subject: [PATCH 21/43] Add ability for user plugin to add new verific log callback --- frontends/verific/verific.cc | 21 ++++++++++++++++----- kernel/log.cc | 1 + kernel/log.h | 3 +++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 5d93954a7..52008ddc6 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -112,15 +112,26 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; message += vstringf(msg, args); - if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) - log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); - else - log("%s%s\n", message_prefix.c_str(), message.c_str()); - + if (log_verific_callback) { + string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str()); + log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile), full_message.c_str()); + } else { + if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) + log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); + else + log("%s%s\n", message_prefix.c_str(), message.c_str()); + } if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR)) verific_error_msg = message; } +void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg)) +{ + Message::SetConsoleOutput(0); + Message::RegisterCallBackMsg(msg_func); + log_verific_callback = cb; +} + string get_full_netlist_name(Netlist *nl) { if (nl->NumOfRefs() == 1) { diff --git a/kernel/log.cc b/kernel/log.cc index 75a1ffb45..985165a97 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -59,6 +59,7 @@ bool log_quiet_warnings = false; int log_verbose_level; string log_last_error; void (*log_error_atexit)() = NULL; +void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg) = NULL; int log_make_debug = 0; int log_force_debug = 0; diff --git a/kernel/log.h b/kernel/log.h index 3a6ec8758..3e4c2edfd 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -130,6 +130,9 @@ void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(for void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); +void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg)); +extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg); + // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); From 34a6bef768934413a3c7064b2406b490d8e63252 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 12 Jun 2023 10:01:35 +0200 Subject: [PATCH 22/43] link verific where appropriate and link full archives --- Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 7afa6c472..1ecf672f9 100644 --- a/Makefile +++ b/Makefile @@ -523,6 +523,7 @@ CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link) endif +LDLIBS_VERIFIC = ifeq ($(ENABLE_VERIFIC),1) VERIFIC_DIR ?= /usr/local/src/verific_lib VERIFIC_COMPONENTS ?= verilog database util containers hier_tree @@ -548,9 +549,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS endif CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC ifeq ($(OS), Darwin) -LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz +LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz else -LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz +LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz endif endif @@ -740,13 +741,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) - $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) + $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) - $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) + $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) else - $(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) + $(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) endif %.o: %.cc From 8b2a0010216f9a15c09bd2f4dc63691949b126df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:17:19 +0000 Subject: [PATCH 23/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 808bf6bc8..c068c1f6d 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+1 +YOSYS_VER := 0.30+16 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From c9d31c3c876c1b95accede88cbe8687eab815e7f Mon Sep 17 00:00:00 2001 From: Charlotte Connor Date: Tue, 13 Jun 2023 14:59:18 +1000 Subject: [PATCH 24/43] smt2: abits needs to be at least 1 for BitVec BitVecs need a minimum length of 1; we zero-fill any extra bits in the extend_u0() calls which works perfectly. --- backends/smt2/smt2.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 7b48be299..5e63e6237 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -773,7 +773,7 @@ struct Smt2Worker int arrayid = idcounter++; memarrays[mem] = arrayid; - int abits = ceil_log2(mem->size); + int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; @@ -1220,7 +1220,7 @@ struct Smt2Worker { int arrayid = memarrays.at(mem); - int abits = ceil_log2(mem->size);; + int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; From d1b86d2fcf2fe92eb6b58dd05ea1284084af188b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:05:51 +1200 Subject: [PATCH 25/43] docs: reflow memory map Move additional notes up to the top and give it its own section. Also reformat some paragraphs, and turn some bullet points into paragraphs. Split supported patterns section into some kind of grouping. Currently: - SDP - single-port RAM - reset patterns - asymmetric - TDP --- docs/source/CHAPTER_Memorymap.rst | 300 +++++++++++++++--------------- 1 file changed, 151 insertions(+), 149 deletions(-) diff --git a/docs/source/CHAPTER_Memorymap.rst b/docs/source/CHAPTER_Memorymap.rst index e18ee9bb2..cdc381eed 100644 --- a/docs/source/CHAPTER_Memorymap.rst +++ b/docs/source/CHAPTER_Memorymap.rst @@ -3,14 +3,131 @@ Memory mapping ============== -Documentation for the Yosys memory_libmap memory mapper. +Documentation for the Yosys ``memory_libmap`` memory mapper. Note that not all supported patterns +are included in this document, of particular note is that combinations of multiple patterns should +generally work. For example, `Write port with byte enables`_ could be used in conjunction with any +of the simple dual port (SDP) models. In general if a hardware memory definition does not support a +given configuration, additional logic will be instantiated to guarantee behaviour is consistent with +simulation. See also: `passes/memory/memlib.md `_ -Supported patterns ------------------- +Additional notes +---------------- -Asynchronous-read RAM +Memory kind selection +~~~~~~~~~~~~~~~~~~~~~ + +The memory inference code will automatically pick target memory primitive based on memory geometry +and features used. Depending on the target, there can be up to four memory primitive classes +available for selection: + +- FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers + + - Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain + - Can handle arbitrary number and kind of read ports + +- LUT RAM (aka distributed RAM): uses LUT storage as RAM + + - Supported on most FPGAs (with notable exception of ice40) + - Usually has one synchronous write port, one or more asynchronous read ports + - Small + - Will never be used for ROMs (lowering to plain LUTs is always better) + +- Block RAM: dedicated memory tiles + + - Supported on basically all FPGAs + - Supports only synchronous reads + - Two ports with separate clocks + - Usually supports true dual port (with notable exception of ice40 that only supports SDP) + - Usually supports asymmetric memories and per-byte write enables + - Several kilobits in size + +- Huge RAM: + + - Only supported on several targets: + + - Some Xilinx UltraScale devices (UltraRAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + - Initial data must be all-0 + + - Some ice40 devices (SPRAM) + + - Single port with mutually exclusive synchronous read and write + - Does not support initial data + + - Nexus (large RAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + + - Will not be automatically selected by memory inference code, needs explicit opt-in via + ram_style attribute + +In general, you can expect the automatic selection process to work roughly like this: + +- If any read port is asynchronous, only LUT RAM (or FF RAM) can be used. +- If there is more than one write port, only block RAM can be used, and this needs to be a + hardware-supported true dual port pattern + + - … unless all write ports are in the same clock domain, in which case FF RAM can also be used, + but this is generally not what you want for anything but really small memories + +- Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size + +This process can be overridden by attaching a ram_style attribute to the memory: + +- `(* ram_style = "logic" *)` selects FF RAM +- `(* ram_style = "distributed" *)` selects LUT RAM +- `(* ram_style = "block" *)` selects block RAM +- `(* ram_style = "huge" *)` selects huge RAM + +It is an error if this override cannot be realized for the given target. + +Many alternate spellings of the attribute are also accepted, for compatibility with other software. + +Initial data +~~~~~~~~~~~~ + +Most FPGA targets support initializing all kinds of memory to user-provided values. If explicit +initialization is not used the initial memory value is undefined. Initial data can be provided by +either initial statements writing memory cells one by one of ``$readmemh`` or ``$readmemb`` system +tasks. For an example pattern, see `Synchronous read port with initial value`_. + +Write port with byte enables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Byte enables can be used with any supported pattern +- To ensure that multiple writes will be merged into one port, they need to have disjoint bit + ranges, have the same address, and the same clock +- Any write enable granularity will be accepted (down to per-bit write enables), but using smaller + granularity than natively supported by the target is very likely to be inefficient (eg. using + 4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit + units or splitting the RAM into two block RAMs) + +.. code:: verilog + + reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable[0]) + mem[write_addr][7:0] <= write_data[7:0]; + if (write_enable[1]) + mem[write_addr][15:8] <= write_data[15:8]; + if (write_enable[2]) + mem[write_addr][23:16] <= write_data[23:16]; + if (write_enable[3]) + mem[write_addr][31:24] <= write_data[31:24]; + if (read_enable) + read_data <= mem[read_addr]; + end + +Simple dual port (SDP) memory patterns +-------------------------------------- + +Asynchronous-read SDP ~~~~~~~~~~~~~~~~~~~~~ - This will result in LUT RAM on supported targets @@ -69,7 +186,6 @@ Synchronous SDP with undefined collision behavior - Like above, but the read value is undefined when read and write ports target the same address in the same cycle - .. code:: verilog reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; @@ -141,6 +257,9 @@ Synchronous SDP with write-first behavior (alternate pattern) assign read_data = mem[read_addr_reg]; +Single-port RAM memory patterns +------------------------------- + Asynchronous-read single-port RAM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -234,13 +353,16 @@ Synchronous read port with initial value read_data <= mem[read_addr]; end -Synchronous read port with synchronous reset (reset priority over enable) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Read register reset patterns +---------------------------- -- Synchronous resets can be combined with any other supported pattern (except that synchronous reset - and asynchronous reset cannot be used on a single read port) -- If block RAM is used and synchronous resets are not natively supported by the target, small - emulation circuit will be inserted +Resets can be combined with any other supported pattern (except that synchronous reset and +asynchronous reset cannot both be used on a single read port). If block RAM is used and the +selected reset (synchronous or asynchronous) is used but not natively supported by the target, small +emulation circuitry will be inserted. + +Synchronous reset, reset priority over enable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: verilog @@ -256,13 +378,8 @@ Synchronous read port with synchronous reset (reset priority over enable) read_data <= mem[read_addr]; end -Synchronous read port with synchronous reset (enable priority over reset) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- Synchronous resets can be combined with any other supported pattern (except that synchronous reset - and asynchronous reset cannot be used on a single read port) -- If block RAM is used and synchronous resets are not natively supported by the target, small - emulation circuit will be inserted +Synchronous reset, enable priority over reset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: verilog @@ -281,11 +398,6 @@ Synchronous read port with synchronous reset (enable priority over reset) Synchronous read port with asynchronous reset ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Asynchronous resets can be combined with any other supported pattern (except that synchronous - reset and asynchronous reset cannot be used on a single read port) -- If block RAM is used and asynchronous resets are not natively supported by the target, small - emulation circuit will be inserted - .. code:: verilog reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; @@ -302,44 +414,8 @@ Synchronous read port with asynchronous reset read_data <= mem[read_addr]; end -Initial data -~~~~~~~~~~~~ - -- Most FPGA targets support initializing all kinds of memory to user-provided values -- If explicit initialization is not used, initial memory value is undefined -- Initial data can be provided by either initial statements writing memory cells one by one or - $readmemh/$readmemb system tasks - -Write port with byte enables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- Byte enables can be used with any supported pattern -- To ensure that multiple writes will be merged into one port, they need to have disjoint bit - ranges, have the same address, and the same clock -- Any write enable granularity will be accepted (down to per-bit write enables), but using smaller - granularity than natively supported by the target is very likely to be inefficient (eg. using - 4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit - units or splitting the RAM into two block RAMs) - -.. code:: verilog - - reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0]; - - always @(posedge clk) begin - if (write_enable[0]) - mem[write_addr][7:0] <= write_data[7:0]; - if (write_enable[1]) - mem[write_addr][15:8] <= write_data[15:8]; - if (write_enable[2]) - mem[write_addr][23:16] <= write_data[23:16]; - if (write_enable[3]) - mem[write_addr][31:24] <= write_data[31:24]; - if (read_enable) - read_data <= mem[read_addr]; - end - -Asymmetric memory — general notes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Asymmetric memory patterns +-------------------------- To construct an asymmetric memory (memory with read/write ports of differing widths): @@ -354,13 +430,12 @@ To construct an asymmetric memory (memory with read/write ports of differing wid - For read ports, ensure that enable/reset signals are identical (for write ports, the enable signal may vary — this will result in using the byte enable functionality) -- Asymmetric memory is supported on all targets, but may require emulation circuitry where not - natively supported -- Note: when the memory is larger than the underlying block RAM primitive, hardware asymmetric - memory support is likely not to be used even if present, as this is cheaper +Asymmetric memory is supported on all targets, but may require emulation circuitry where not +natively supported. Note that when the memory is larger than the underlying block RAM primitive, +hardware asymmetric memory support is likely not to be used even if present as it is more expensive. -Asymmetric memory with wide synchronous read port -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Wide synchronous read port +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: verilog @@ -432,8 +507,8 @@ Wide write port read_data <= mem[read_addr]; end -True dual port memory — general notes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +True dual port (TDP) patterns +----------------------------- - Many different variations of true dual port memory can be created by combining two single-port RAM patterns on the same memory @@ -450,8 +525,8 @@ True dual port memory — general notes - Priority is not supported when using the verific front end and any priority semantics are ignored. -True Dual Port — different clocks, exclusive read/write -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TDP with different clocks, exclusive read/write +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: verilog @@ -471,8 +546,8 @@ True Dual Port — different clocks, exclusive read/write read_data_b <= mem[addr_b]; end -True Dual Port — same clock, read-first behavior -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TDP with same clock, read-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This requires hardware inter-port read-first behavior, and will only work on some targets (Xilinx, Nexus) @@ -494,8 +569,8 @@ True Dual Port — same clock, read-first behavior read_data_b <= mem[addr_b]; end -Multiple read ports -~~~~~~~~~~~~~~~~~~~ +TDP with multiple read ports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The combination of a single write port with an arbitrary amount of read ports is supported on all targets — if a multi-read port primitive is available (like Xilinx RAM64M), it'll be used as @@ -514,79 +589,6 @@ Multiple read ports assign read_data_b = mem[read_addr_b]; assign read_data_c = mem[read_addr_c]; -Memory kind selection -~~~~~~~~~~~~~~~~~~~~~ - -- The memory inference code will automatically pick target memory primitive based on memory geometry - and features used. Depending on the target, there can be up to four memory primitive classes - available for selection: - -- FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers - - - Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain - - Can handle arbitrary number and kind of read ports - -- LUT RAM (aka distributed RAM): uses LUT storage as RAM - - - Supported on most FPGAs (with notable exception of ice40) - - Usually has one synchronous write port, one or more asynchronous read ports - - Small - - Will never be used for ROMs (lowering to plain LUTs is always better) - -- Block RAM: dedicated memory tiles - - - Supported on basically all FPGAs - - Supports only synchronous reads - - Two ports with separate clocks - - Usually supports true dual port (with notable exception of ice40 that only supports SDP) - - Usually supports asymmetric memories and per-byte write enables - - Several kilobits in size - -- Huge RAM: - - - Only supported on several targets: - - - Some Xilinx UltraScale devices (UltraRAM) - - - Two ports, both with mutually exclusive synchronous read and write - - Single clock - - Initial data must be all-0 - - - Some ice40 devices (SPRAM) - - - Single port with mutually exclusive synchronous read and write - - Does not support initial data - - - Nexus (large RAM) - - - Two ports, both with mutually exclusive synchronous read and write - - Single clock - - - Will not be automatically selected by memory inference code, needs explicit opt-in via - ram_style attribute - -In general, you can expect the automatic selection process to work roughly like this: - -- If any read port is asynchronous, only LUT RAM (or FF RAM) can be used. -- If there is more than one write port, only block RAM can be used, and this needs to be a - hardware-supported true dual port pattern - - - … unless all write ports are in the same clock domain, in which case FF RAM can also be used, - but this is generally not what you want for anything but really small memories - -- Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size - -This process can be overridden by attaching a ram_style attribute to the memory: - -- `(* ram_style = "logic" *)` selects FF RAM -- `(* ram_style = "distributed" *)` selects LUT RAM -- `(* ram_style = "block" *)` selects block RAM -- `(* ram_style = "huge" *)` selects huge RAM - -It is an error if this override cannot be realized for the given target. - -Many alternate spellings of the attribute are also accepted, for compatibility with other software. - Not yet supported patterns -------------------------- @@ -612,7 +614,7 @@ Asymmetric memories via part selection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Would require major changes to the Verilog frontend. -- Build wide ports out of narrow ports instead (see `Asymmetric memory with wide synchronous read port`_) +- Build wide ports out of narrow ports instead (see `Wide synchronous read port`_) .. code:: verilog From 25954715f0a8a2fc65d5961c3f09f9ee95774760 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 00:16:06 +0000 Subject: [PATCH 26/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c068c1f6d..befbc58d7 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+16 +YOSYS_VER := 0.30+21 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From fb9e12761bd9ea0e81316f69190c96b4ec872a82 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 12 Nov 2019 17:38:00 +0100 Subject: [PATCH 27/43] Add "write_edif -lsbidx" --- backends/edif/edif.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 7722d0c33..04037edcd 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -120,6 +120,9 @@ struct EdifBackend : public Backend { log(" sets the delimiting character for module port rename clauses to\n"); log(" parentheses, square brackets, or angle brackets.\n"); log("\n"); + log(" -lsbidx\n"); + log(" use index 0 for the LSB bit of a net or port instead of MSB.\n"); + log("\n"); log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); log("necessary to make small modifications to this command when a different tool\n"); @@ -132,6 +135,7 @@ struct EdifBackend : public Backend { std::string top_module_name; bool port_rename = false; bool attr_properties = false; + bool lsbidx = false; std::map> lib_cell_ports; bool nogndvcc = false, gndvccy = false, keepmode = false; CellTypes ct(design); @@ -173,6 +177,10 @@ struct EdifBackend : public Backend { } continue; } + if (args[argidx] == "-lsbidx") { + lsbidx = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -437,7 +445,7 @@ struct EdifBackend : public Backend { *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); + net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), lsbidx ? i : GetSize(wire)-i-1), wire->port_input)); } } } @@ -468,13 +476,13 @@ struct EdifBackend : public Backend { log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else { - int member_idx = GetSize(sig)-i-1; + int member_idx = lsbidx ? i : GetSize(sig)-i-1; auto m = design->module(cell->type); int width = sig.size(); if (m) { auto w = m->wire(p.first); if (w) { - member_idx = GetSize(w)-i-1; + member_idx = lsbidx ? i : GetSize(w)-i-1; width = GetSize(w); } } From cff3195caa8297f39dd77c1b7842f0a70e938eca Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 14 Nov 2019 15:55:21 +0100 Subject: [PATCH 28/43] Improve EDIF lib_cell_ports scan --- backends/edif/edif.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 04037edcd..00fd7f54e 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -192,6 +192,14 @@ struct EdifBackend : public Backend { for (auto module : design->modules()) { + lib_cell_ports[module->name]; + + for (auto port : module->ports) + { + Wire *wire = module->wire(port); + lib_cell_ports[module->name][port] = std::max(lib_cell_ports[module->name][port], GetSize(wire)); + } + if (module->get_blackbox_attribute()) continue; @@ -208,7 +216,7 @@ struct EdifBackend : public Backend { if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) - lib_cell_ports[cell->type][p.first] = GetSize(p.second); + lib_cell_ports[cell->type][p.first] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second)); } } } From 22c9237716028ccfd1876a31eef813f7720b0a42 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Tue, 20 Jun 2023 11:17:12 +0200 Subject: [PATCH 29/43] show: escape angle brackets --- passes/cmds/show.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 525c81d5b..f1854058c 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -208,7 +208,7 @@ struct ShowWorker str += "╲"; continue; } - if (ch == '"') + if (ch == '"' || ch == '<' || ch == '>') str += "\\"; str += ch; } From 9c7f0e76709aa7f4f77c404562a7468acbb116bc Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Tue, 20 Jun 2023 12:53:56 +0200 Subject: [PATCH 30/43] show: truncate very long module names --- passes/cmds/show.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index f1854058c..0dc5c452c 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -201,6 +201,12 @@ struct ShowWorker if (id[0] == '\\') id = id.substr(1); + // TODO: optionally include autoname + print correspondence in case of ambiguity + size_t max_label_len = abbreviateIds ? 256 : 16384; + if (id.size() > max_label_len) { + id = id.substr(0,max_label_len-3) + "..."; + } + std::string str; for (char ch : id) { if (ch == '\\') { From 104edb458784fdbf2bb782f095c2afada9b0299c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 00:17:27 +0000 Subject: [PATCH 31/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index befbc58d7..2bf079ee8 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+21 +YOSYS_VER := 0.30+25 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 0c0171bd6040e44b7013ffca830cfc891db536e6 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 21 Jun 2023 17:21:04 +1000 Subject: [PATCH 32/43] docs: RD_DATA is an output, not input --- docs/source/CHAPTER_CellLib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/CHAPTER_CellLib.rst b/docs/source/CHAPTER_CellLib.rst index c5db434a6..c89040868 100644 --- a/docs/source/CHAPTER_CellLib.rst +++ b/docs/source/CHAPTER_CellLib.rst @@ -571,7 +571,7 @@ The ``$mem_v2`` cell has the following ports: signals for the read ports. ``\RD_DATA`` - This input is ``\RD_PORTS*\WIDTH`` bits wide, containing all data + This output is ``\RD_PORTS*\WIDTH`` bits wide, containing all data signals for the read ports. ``\RD_ARST`` From 63e4114233d2610ae69e95baca835b7ff946e804 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 21 Jun 2023 19:05:02 +1000 Subject: [PATCH 33/43] proc_prune: avoid using invalidated iterator An `std::vector::reverse_iterator` stores the `std::vector::iterator` which points to the (forwards-ordered) *following* item. Thus while `vec.rbegin()` dereferences to the final item of `vec`, the iterator it wraps (`vec.rbegin().base()`) is equal to `vec.end()`. In the remove case here, we advance `it` (backwards), erasing the item we just advanced past by grabbing its (pre-increment) base forward-iterator and subtracting 1. The iterator maths here is obviously all OK, but the forward-iterator that `it` wraps post-increment actually points to the item we just removed. That iterator was invalidated by the `erase()` call. That this works anyway is (AFAICT) some combination of luck and/or promises that aren't part of the C++ spec, but MSVC's debug iterator support picks this up. `erase()` returns the new iterator that follows the item just erased, which happens to be the exact one we want our reverse-iterator to wrap for the next loop; we get a fresh iterator to the same base, now without the preceding item. --- passes/proc/proc_prune.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc index 9f1080ef6..3433557ee 100644 --- a/passes/proc/proc_prune.cc +++ b/passes/proc/proc_prune.cc @@ -91,7 +91,7 @@ struct PruneWorker if (GetSize(new_lhs) == 0) { if (GetSize(conn_lhs) == 0) removed_count++; - cs->actions.erase((it++).base() - 1); + it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1)); } else { it->first = new_lhs; it->second = new_rhs; From aff0065646f1f526c199bdbb8de74f22391a345d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 21 Jun 2023 13:21:34 +0200 Subject: [PATCH 34/43] Use defaultvalue for init values of input ports --- frontends/verific/verific.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 52008ddc6..989e2173b 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2005,7 +2005,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma initval[i] = State::Sx; } - if (initval.is_fully_undef()) + if (wire->port_input) { + wire->attributes[ID::defaultvalue] = Const(initval); + wire->attributes.erase(ID::init); + } else if (initval.is_fully_undef()) wire->attributes.erase(ID::init); } } From 8f7a9a0b667031d661cd91a66c5dcab39eb470d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 00:17:44 +0000 Subject: [PATCH 35/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2bf079ee8..e676c7cef 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+25 +YOSYS_VER := 0.30+33 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 3f29bdbbc56885fe1f6db9c9b49a5323d1d30ad6 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 23 Jun 2023 14:38:15 +1000 Subject: [PATCH 36/43] smt2: py3.12+: avoid SyntaxWarning. Python 3.12 emits a SyntaxWarning when encountering invalid escape sequences. They still parse as expected. Marking these raw produces the same result without the warnings. --- backends/smt2/smtio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index a73745896..8094747bc 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -768,7 +768,7 @@ class SmtIo: if self.timeinfo: i = 0 - s = "/-\|" + s = r"/-\|" count = 0 num_bs = 0 @@ -1171,7 +1171,7 @@ class MkVcd: def escape_name(self, name): name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name) - if re.match("[\[\]]", name) and name[0] != "\\": + if re.match(r"[\[\]]", name) and name[0] != "\\": name = "\\" + name return name From f9744fdfcd8106107e2bef750c36c04ae2406973 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 23 Jun 2023 10:27:38 +0200 Subject: [PATCH 37/43] smtbmc: Make cover mode respect --keep-going As cover mode by default stops looking for further traces when an assertion fails, it should respect --keep-going. --- backends/smt2/smtbmc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index 6b81740a2..02e15a1b5 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -174,6 +174,8 @@ def help(): further failed assertions. To output multiple traces covering all found failed assertions, the character '%' is replaced in all dump filenames with an increasing number. + In cover mode, don't stop when a cover trace contains a failed + assertion. --check-witness check that the used witness file contains sufficient @@ -1739,7 +1741,7 @@ elif covermode: smt_pop() smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask)) - if found_failed_assert: + if found_failed_assert and not keep_going: break if "1" not in cover_mask: From a07f8ac38ab9e4c2160673f601755d5044c15de2 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 23 Jun 2023 18:07:28 +0200 Subject: [PATCH 38/43] check: Also check for conflicts with constant drivers --- passes/cmds/check.cc | 31 +++++++++----- tests/various/constant_drive_conflict.ys | 51 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 tests/various/constant_drive_conflict.ys diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index ee0f0a58f..d1c83f04d 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -112,11 +112,10 @@ struct CheckPass : public Pass { for (size_t i = 0; i < all_cases.size(); i++) { for (auto action : all_cases[i]->actions) { for (auto bit : sigmap(action.first)) - if (bit.wire) { - wire_drivers[bit].push_back( - stringf("action %s <= %s (case rule) in process %s", - log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); - } + wire_drivers[bit].push_back( + stringf("action %s <= %s (case rule) in process %s", + log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); + for (auto bit : sigmap(action.second)) if (bit.wire) used_wires.insert(bit); } @@ -134,10 +133,9 @@ struct CheckPass : public Pass { if (bit.wire) used_wires.insert(bit); for (auto action : sync->actions) { for (auto bit : sigmap(action.first)) - if (bit.wire) - wire_drivers[bit].push_back( - stringf("action %s <= %s (sync rule) in process %s", - log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); + wire_drivers[bit].push_back( + stringf("action %s <= %s (sync rule) in process %s", + log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); for (auto bit : sigmap(action.second)) if (bit.wire) used_wires.insert(bit); } @@ -176,7 +174,8 @@ struct CheckPass : public Pass { if (logic_cell) topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), stringf("wire %s", log_signal(sig[i]))); - if (sig[i].wire) + + if (sig[i].wire || !cell->input(conn.first)) wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", log_id(conn.first), i, log_id(cell), log_id(cell->type))); } @@ -192,7 +191,8 @@ struct CheckPass : public Pass { if (wire->port_input) { SigSpec sig = sigmap(wire); for (int i = 0; i < GetSize(sig); i++) - wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); + if (sig[i].wire || !wire->port_output) + wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); } if (wire->port_output) for (auto bit : sigmap(wire)) @@ -212,6 +212,15 @@ struct CheckPass : public Pass { } } + for (auto state : {State::S0, State::S1, State::Sx}) + if (wire_drivers.count(state)) { + string message = stringf("Drivers conflicting with a constant %s driver:\n", log_signal(state)); + for (auto str : wire_drivers[state]) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + for (auto it : wire_drivers) if (wire_drivers_count[it.first] > 1) { string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); diff --git a/tests/various/constant_drive_conflict.ys b/tests/various/constant_drive_conflict.ys new file mode 100644 index 000000000..1246cbcae --- /dev/null +++ b/tests/various/constant_drive_conflict.ys @@ -0,0 +1,51 @@ +read_verilog < Date: Fri, 23 Jun 2023 19:40:29 +0200 Subject: [PATCH 39/43] verific: import src attribute on $memrd/$memwr cells --- frontends/verific/verific.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 989e2173b..0dd785ec3 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1649,6 +1649,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma cell->parameters[ID::TRANSPARENT] = false; cell->parameters[ID::ABITS] = GetSize(addr); cell->parameters[ID::WIDTH] = GetSize(data); + import_attributes(cell->attributes, inst); cell->setPort(ID::CLK, RTLIL::State::Sx); cell->setPort(ID::EN, RTLIL::State::Sx); cell->setPort(ID::ADDR, addr); @@ -1678,6 +1679,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma cell->parameters[ID::PRIORITY] = 0; cell->parameters[ID::ABITS] = GetSize(addr); cell->parameters[ID::WIDTH] = GetSize(data); + import_attributes(cell->attributes, inst); cell->setPort(ID::EN, RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); cell->setPort(ID::CLK, RTLIL::State::S0); cell->setPort(ID::ADDR, addr); From 2310a0ea9a61ed14d2769f01283a5a7590cbe558 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:21:16 +0000 Subject: [PATCH 40/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e676c7cef..fae752952 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+33 +YOSYS_VER := 0.30+38 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From a7bccdfe8d2a3ef7faa8b1cf1d311b36e698d6f5 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 28 Jun 2023 11:20:44 +0200 Subject: [PATCH 41/43] Update ABC version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fae752952..fb2d16425 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ bumpversion: # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = 1de4eaf +ABCREV = bb64142 ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) From eb397592f0d84c54e8923af9512582b7efcd6622 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 28 Jun 2023 11:47:30 +1000 Subject: [PATCH 42/43] cxxrtl: add `$divfloor`. --- backends/cxxrtl/cxxrtl.h | 19 +++++++++++++++++++ backends/cxxrtl/cxxrtl_backend.cc | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index 073921cc4..5d0596f0d 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -1595,6 +1595,25 @@ value modfloor_ss(const value &a, const value &b) { return r; } +template +CXXRTL_ALWAYS_INLINE +value divfloor_uu(const value &a, const value &b) { + return divmod_uu(a, b).first; +} + +// Divfloor. Similar to above: returns q=a//b, where q has the sign of a*b and a=b*q+N. +// In other words, returns (truncating) a/b, except if a and b have different signs +// and there's non-zero remainder, subtract one more towards floor. +template +CXXRTL_ALWAYS_INLINE +value divfloor_ss(const value &a, const value &b) { + value q, r; + std::tie(q, r) = divmod_ss(a, b); + if ((b.is_neg() != a.is_neg()) && !r.is_zero()) + return sub_uu(q, value<1> { 1u }); + return q; + +} // Memory helper struct memory_index { diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 62768bd33..1b13985ab 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -185,7 +185,7 @@ bool is_binary_cell(RTLIL::IdString type) ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le), - ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor)); + ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($divfloor)); } bool is_extending_cell(RTLIL::IdString type) From b5b0b7e839c562f23623108e9cfc6015756045b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:18:55 +0000 Subject: [PATCH 43/43] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fb2d16425..cf2d8f200 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.30+38 +YOSYS_VER := 0.30+48 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo