From 3861cc31f0dc625ee48e7cc1a62e1f2fd7bd324d Mon Sep 17 00:00:00 2001 From: AdamHillier Date: Wed, 19 Apr 2023 11:00:51 +0000 Subject: [PATCH 01/16] Add outputs before inputs to the sigmap in the AIGER backend. --- backends/aiger/aiger.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 4ef28be9f..97acf937c 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -119,16 +119,16 @@ struct AigerWriter if (wire->name.isPublic()) sigmap.add(wire); - // promote input wires - for (auto wire : module->wires()) - if (wire->port_input) - sigmap.add(wire); - // promote output wires for (auto wire : module->wires()) if (wire->port_output) sigmap.add(wire); + // promote input wires + for (auto wire : module->wires()) + if (wire->port_input) + sigmap.add(wire); + for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { From 6b3e6d96a36e8e52c00e53ebadf3f3706b1e3025 Mon Sep 17 00:00:00 2001 From: Kamil Rakoczy Date: Tue, 28 Mar 2023 10:53:00 +0200 Subject: [PATCH 02/16] Fix missing brackets around else Signed-off-by: Kamil Rakoczy --- frontends/ast/genrtlil.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 8da4b0b0a..5c0b88027 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -844,13 +844,14 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) { if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; - } else - if (id_ast->children[0]->type != AST_CONSTANT) - while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } - if (id_ast->children[0]->type == AST_CONSTANT) - this_width = id_ast->children[0]->bits.size(); - else - log_file_error(filename, location.first_line, "Failed to detect width for parameter %s!\n", str.c_str()); + } else { + if (id_ast->children[0]->type != AST_CONSTANT) + while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } + if (id_ast->children[0]->type == AST_CONSTANT) + this_width = id_ast->children[0]->bits.size(); + else + log_file_error(filename, location.first_line, "Failed to detect width for parameter %s!\n", str.c_str()); + } if (children.size() != 0) range = children[0]; } else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) { From 52ad7a47f3798f17d9c33209649136418fe1194f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 18 May 2023 10:37:55 +1200 Subject: [PATCH 03/16] Assign wires an smtoffset Wires weren't being assigned an smtoffset value so when generating a yosys witness trace it would also use an offset of 0. Not sure if this has any other effects, but it fixes the bug I was having. @jix could you take a look at this? --- backends/smt2/smt2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 48da3f4be..bdef8d569 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -628,7 +628,7 @@ struct Smt2Worker bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); for (auto chunk : cell->getPort(QY).chunks()) if (chunk.is_wire()) - decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire)); + decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, chunk.offset)); makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY))); if (cell->type == ID($anyseq)) From e6f39148009df28280eacb25c0392c5bfbb39ce0 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 18 May 2023 11:58:09 +0200 Subject: [PATCH 04/16] smt2: Use smt bv offset for `$any*`'s smtoffset While not setting the smtoffset here was clearly a bug, I think using `chunk.offset` only worked incidentally. The `smtoffset` is an offset into the `smtname, smtid` pair (here `"", idcounter`) which corresponds to the smt bitvector `stringf("%s#%d", get_id(module), idcounter)` which contains all the chunks this loop is iterating over. Thus using an incrementing `smtoffset` (like the `$ff`/`$dff` case above already does) should be the correct fix. --- backends/smt2/smt2.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index bdef8d569..0ca3fbcac 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -626,9 +626,12 @@ struct Smt2Worker } bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); - for (auto chunk : cell->getPort(QY).chunks()) + int smtoffset = 0; + for (auto chunk : cell->getPort(QY).chunks()) { if (chunk.is_wire()) - decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, chunk.offset)); + decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); + smtoffset += chunk.width; + } makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY))); if (cell->type == ID($anyseq)) From ad2b04d63a8639a294bfe6148ee262980fc962e1 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 18 May 2023 16:50:11 +0200 Subject: [PATCH 05/16] sim: Fix cosimulation with nested modules having unconnected inputs When assigning values to input ports of nested modules in cosimulation, sim needs to find the actual driver of the signal to perform the assignment. The existing code didn't handle unconnected inputs in that scenario. --- passes/sat/sim.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index fe1635249..273e9db86 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -223,7 +223,8 @@ struct SimInstance if (wire->port_input && instance != nullptr && parent != nullptr) { for (int i = 0; i < GetSize(sig); i++) { - in_parent_drivers.emplace(sig[i], parent->sigmap(instance->getPort(wire->name)[i])); + if (instance->hasPort(wire->name)) + in_parent_drivers.emplace(sig[i], parent->sigmap(instance->getPort(wire->name)[i])); } } } From e7156c644ded014febfa202593d718aa098b54dd Mon Sep 17 00:00:00 2001 From: CORRADI Quentin <12198691+dwRchyngqxs@users.noreply.github.com> Date: Thu, 18 May 2023 14:46:25 +0100 Subject: [PATCH 06/16] Standard compliance for tests/verilog/block_labels.ys genvar declaration cannot take an initial value when declared as a module_or_generate_item_declaration. Correct this test so that it doesn't fail unexpectedly if Yosys aligns with the standard. --- tests/verilog/block_labels.ys | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/verilog/block_labels.ys b/tests/verilog/block_labels.ys index e76bcf771..76aad29ae 100644 --- a/tests/verilog/block_labels.ys +++ b/tests/verilog/block_labels.ys @@ -1,7 +1,7 @@ read_verilog < Date: Mon, 22 May 2023 00:16:53 +0000 Subject: [PATCH 07/16] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c191b8e4d..959d55737 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.29+21 +YOSYS_VER := 0.29+23 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From ecd289c10011f06aecb77b8c3961467f274c9d00 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 23 May 2023 08:25:08 +0200 Subject: [PATCH 08/16] Fix importing parametrized VHDL entity --- 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 bc61c9c82..5d93954a7 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2468,6 +2468,7 @@ std::string verific_import(Design *design, const std::mapAddAtt(new Att(" \\top", NULL)); nl_todo.emplace(nl->CellBaseName(), nl); + cell_name = nl->Owner()->Name(); } + if (top.empty()) cell_name = top; delete netlists; @@ -2495,7 +2498,7 @@ std::string verific_import(Design *design, const std::mapfirst) == 0) { VerificImporter importer(false, false, false, false, false, false, false); nl_done[it->first] = it->second; - importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == top); + importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == cell_name); } nl_todo.erase(it); } From 57c9eb70feb54cff112d095d2153b0d032bdbf18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 00:15:32 +0000 Subject: [PATCH 09/16] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 959d55737..a0b77a837 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.29+23 +YOSYS_VER := 0.29+34 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 52c8c28d2cd05145d5b224c56742c63e79af1e04 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 25 Nov 2021 19:43:58 +0000 Subject: [PATCH 10/16] Add recover_names pass to recover names post-mapping --- kernel/hashlib.h | 6 + passes/sat/Makefile.inc | 1 + passes/sat/recover_names.cc | 730 ++++++++++++++++++++++++++++++++++++ 3 files changed, 737 insertions(+) create mode 100644 passes/sat/recover_names.cc diff --git a/kernel/hashlib.h b/kernel/hashlib.h index af2827153..b3f99bf73 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -84,6 +84,12 @@ template<> struct hash_ops : hash_int_ops return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); } }; +template<> struct hash_ops : hash_int_ops +{ + static inline unsigned int hash(uint32_t a) { + return a; + } +}; template<> struct hash_ops { static inline bool cmp(const std::string &a, const std::string &b) { diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index cc89cc0c8..d9bf69dcb 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -16,6 +16,7 @@ OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o OBJS += passes/sat/cutpoint.o OBJS += passes/sat/fminit.o +OBJS += passes/sat/recover_names.o ifeq ($(DISABLE_SPAWN),0) OBJS += passes/sat/qbfsat.o endif diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc new file mode 100644 index 000000000..2d7e7f01c --- /dev/null +++ b/passes/sat/recover_names.cc @@ -0,0 +1,730 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 gatecat + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "kernel/utils.h" +#include "kernel/satgen.h" + +#include +#include + +USING_YOSYS_NAMESPACE + +template<> struct hashlib::hash_ops : hashlib::hash_int_ops +{ + static inline unsigned int hash(uint64_t a) { + return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + } +}; + +PRIVATE_NAMESPACE_BEGIN + +// xorshift128 params +#define INIT_X 123456789 +#define INIT_Y 362436069 +#define INIT_Z 521288629 +#define INIT_W 88675123 + + +// Similar to a SigBit; but module-independent +struct IdBit { + IdBit() : name(), bit(0) {}; + IdBit(IdString name, int bit = 0) : name(name), bit(bit) {}; + + bool operator==(const IdBit &other) const { return name == other.name && bit == other.bit; }; + bool operator!=(const IdBit &other) const { return name != other.name || bit != other.bit; }; + unsigned hash() const + { + return mkhash_add(name.hash(), bit); + } + + IdString name; + int bit; +}; + +// As above; but can be inverted +struct InvBit { + InvBit() : bit(), inverted(false) {}; + explicit InvBit(IdBit bit, bool inverted = false) : bit(bit), inverted(inverted) {}; + + bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; }; + bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; }; + unsigned hash() const + { + return mkhash(bit.hash(), inverted); + } + + IdBit bit; + bool inverted; +}; + +typedef uint64_t equiv_cls_t; +static const int sim_length = sizeof(equiv_cls_t) * 8; + +struct RecoverModuleWorker { + Design *design = nullptr; + Module *mod, *flat = nullptr; + RecoverModuleWorker(Module *mod) : design(mod->design), mod(mod) {}; + + ConstEval *ce = nullptr; + SigMap *sigmap = nullptr; + + dict flat2orig; + dict bit2primary; + dict bit2driver; + + void prepare() + { + // Create a derivative of the module with whiteboxes flattened so we can + // run eval and sat on it + flat = design->addModule(NEW_ID); + mod->cloneInto(flat); + Pass::call_on_module(design, flat, "flatten -wb"); + ce = new ConstEval(flat); + sigmap = new SigMap(flat); + // Create a mapping from primary name-bit in the box-flattened module to original sigbit + SigMap orig_sigmap(mod); + for (auto wire : mod->wires()) { + Wire *flat_wire = flat->wire(wire->name); + if (!flat_wire) + continue; + for (int i = 0; i < wire->width; i++) { + SigBit orig_sigbit = orig_sigmap(SigBit(wire, i)); + SigBit flat_sigbit = (*sigmap)(SigBit(flat_wire, i)); + if (!orig_sigbit.wire || !flat_sigbit.wire) + continue; + flat2orig[IdBit(flat_sigbit.wire->name, flat_sigbit.offset)] = orig_sigbit; + } + } + find_driven_bits(); + } + + void find_driven_bits() + { + // Add primary inputs + for (auto wire : flat->wires()) { + if (!wire->port_input) + continue; + for (int i = 0; i < wire->width; i++) { + SigBit bit(wire, i); + bit = (*sigmap)(bit); + if (bit.wire) + bit2driver[IdBit(bit.wire->name, bit.offset)] = nullptr; + } + } + // Add cell outputs + for (auto cell : flat->cells()) { + for (auto conn : cell->connections()) { + if (!cell->output(conn.first)) + continue; + for (auto bit : conn.second) { + auto resolved = (*sigmap)(bit); + if (resolved.wire) + bit2driver[IdBit(resolved.wire->name, resolved.offset)] = cell; + } + } + } + // Setup bit2primary + for (auto wire : flat->wires()) { + for (int i = 0; i < wire->width; i++) { + SigBit bit(wire, i); + bit = (*sigmap)(bit); + if (bit.wire) + bit2primary[IdBit(wire->name, i)] = IdBit(bit.wire->name, bit.offset); + } + } + } + + // Mapping from bit to (candidate) equivalence classes + dict bit2cls; + void sim_cycle(int t, const dict &anchors) + { + ce->clear(); + for (auto anchor : anchors) { + SigBit bit = (*sigmap)(SigBit(flat->wire(anchor.first.name), anchor.first.bit)); + // Ignore in the rare case that it's already determined + SigSpec res(bit); + if (ce->eval(res)) + continue; + ce->set(bit, anchor.second); + } + // Only evaluate IdBits that exist in the non-flat design; as they are all we care about + for (auto idbit : flat2orig) { + if (anchors.count(idbit.first)) + continue; + SigBit bit = (*sigmap)(SigBit(flat->wire(idbit.first.name), idbit.first.bit)); + SigSpec res(bit); + if (!ce->eval(res)) + continue; + if (res != State::S0 && res != State::S1) + continue; + // Update equivalence classes + if (res == State::S1) + bit2cls[idbit.first] = bit2cls[idbit.first] | (equiv_cls_t(1) << t); + } + } + + // Update the equivalence class groupings + void group_classes(dict, pool>> &cls2bits, bool is_gate) + { + equiv_cls_t all_ones = 0; + for (int i = 0; i < sim_length; i++) all_ones |= (equiv_cls_t(1) << i); + for (auto pair : bit2cls) { + if (pair.second == 0 || pair.second == all_ones) + continue; // skip stuck-ats + if (is_gate) { + // True doesn't exist in gold; but inverted does + if (!cls2bits.count(pair.second) && cls2bits.count(pair.second ^ all_ones)) + cls2bits[pair.second ^ all_ones].second.emplace(pair.first, true); + else + cls2bits[pair.second].second.emplace(pair.first, false); + } else { + cls2bits[pair.second].first.insert(pair.first); + } + } + } + + // Compute depths of IdBits + dict bit2depth; + void compute_depths(const dict &anchor_bits) + { + dict> bit_drivers, bit_users; + TopoSort toposort; + + for (auto cell : flat->cells()) + for (auto conn : cell->connections()) + { + for (auto bit : (*sigmap)(conn.second)) { + if (!bit.wire) + continue; + IdBit idbit(bit.wire->name, bit.offset); + if (anchor_bits.count(idbit)) + continue; + if (cell->input(conn.first)) + bit_users[bit].insert(cell->name); + + if (cell->output(conn.first)) + bit_drivers[bit].insert(cell->name); + } + + toposort.node(cell->name); + } + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + + toposort.sort(); + for (auto cell_name : toposort.sorted) { + Cell *cell = flat->cell(cell_name); + int cell_depth = 0; + for (auto conn : cell->connections()) { + if (!cell->input(conn.first)) + continue; + for (auto bit : (*sigmap)(conn.second)) { + if (!bit.wire) + continue; + IdBit idbit(bit.wire->name, bit.offset); + if (!bit2depth.count(idbit)) + continue; + cell_depth = std::max(cell_depth, bit2depth.at(idbit)); + } + } + for (auto conn : cell->connections()) { + if (!cell->output(conn.first)) + continue; + for (auto bit : (*sigmap)(conn.second)) { + if (!bit.wire) + continue; + IdBit idbit(bit.wire->name, bit.offset); + bit2depth[idbit] = std::max(bit2depth[idbit], cell_depth + 1); + } + } + } + } + + // SAT thresholds + const int max_sat_cells = 50; + + SigBit id2bit(IdBit bit) { return SigBit(flat->wire(bit.name), bit.bit); } + + // Set up the SAT problem for an IdBit + // the value side of 'anchors' will be populated with the SAT variable for anchor bits + int setup_sat(SatGen *sat, const std::string &prefix, IdBit bit, const dict &anchor_bits, dict &anchor2var) + { + sat->setContext(sigmap, prefix); + pool imported_cells; + int result = sat->importSigBit(id2bit(bit)); + // Recursively import driving cells + std::queue to_import; + to_import.push(bit); + while (!to_import.empty()) { + // Too many cells imported + if (GetSize(imported_cells) > max_sat_cells) + return -1; + IdBit cursor = to_import.front(); + to_import.pop(); + if (anchor_bits.count(cursor)) { + if (!anchor2var.count(cursor)) { + anchor2var[cursor] = sat->importSigBit(id2bit(cursor)); + } + continue; + } + // Import driver if it exists + if (!bit2driver.count(cursor)) + continue; + Cell *driver = bit2driver.at(cursor); + if (!driver || imported_cells.count(driver->name)) + continue; + if (!sat->importCell(driver)) + return -1; // cell can't be imported + imported_cells.insert(driver->name); + // Add cell inputs to queue + for (auto conn : driver->connections()) { + if (!driver->input(conn.first)) + continue; + for (SigBit in_bit : (*sigmap)(conn.second)) { + if (!in_bit.wire) + continue; + IdBit in_idbit(in_bit.wire->name, in_bit.offset); + to_import.push(in_idbit); + } + } + } + return result; + } + + void find_buffers(const pool &buffer_types, dict> &root2buffered) + { + SigMap orig_sigmap(mod); + dict buffer2root; + for (auto cell : mod->cells()) { + if (!buffer_types.count(cell->type)) + continue; + SigBit in, out; + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) { + in = orig_sigmap(conn.second[0]); + } + if (cell->output(conn.first)) { + out = orig_sigmap(conn.second[0]); + } + } + if (!in.wire || !out.wire) + continue; + SigBit root = in; + if (buffer2root.count(root)) + root = buffer2root[root]; + if (root2buffered.count(out)) { + for (auto out_sig : root2buffered.at(out)) + root2buffered[root].insert(out_sig); + root2buffered.erase(out); + } + root2buffered[root].insert(out); + buffer2root[out] = root; + } + } + + void do_rename(Module *gold, const dict &gate2gold, const pool &buffer_types) + { + dict>> bit2port; + pool unused_bits; + SigMap orig_sigmap(mod); + for (auto wire : mod->wires()) { + if (wire->port_input || wire->port_output) + continue; + for (int i = 0; i < wire->width; i++) + unused_bits.insert(orig_sigmap(SigBit(wire, i))); + } + for (auto cell : mod->cells()) { + for (auto conn : cell->connections()) { + for (int i = 0; i < GetSize(conn.second); i++) { + SigBit bit = orig_sigmap(conn.second[i]); + if (!bit.wire) + continue; + bit2port[bit].emplace_back(cell, conn.first, i); + unused_bits.erase(bit); + } + } + } + dict> root2buffered; + find_buffers(buffer_types, root2buffered); + + // An extension of gate2gold that deals with buffers too + // gate sigbit --> (new name, invert, gold wire) + dict> rename_map; + for (auto pair : gate2gold) { + SigBit gate_bit = flat2orig.at(pair.first); + Wire *gold_wire = gold->wire(pair.second.bit.name); + rename_map[gate_bit] = std::make_pair(pair.second, gold_wire); + if (root2buffered.count(gate_bit)) { + int buf_idx = 0; + for (auto buf_bit : root2buffered.at(gate_bit)) { + std::string buf_name_str = stringf("%s_buf_%d", pair.second.bit.name.c_str(), ++buf_idx); + if (buf_name_str[0] == '\\') + buf_name_str[0] = '$'; + rename_map[buf_bit] = std::make_pair( + InvBit(IdBit(IdString(buf_name_str), pair.second.bit.bit), pair.second.inverted), gold_wire); + } + } + } + + for (auto rule : rename_map) { + // Pick a uniq new name + IdBit new_name = rule.second.first.bit; + int dup_idx = 0; + bool must_invert_name = rule.second.first.inverted; + while (must_invert_name || + (mod->wire(new_name.name) && !unused_bits.count(SigBit(mod->wire(new_name.name), new_name.bit)))) { + std::string new_name_str = stringf("%s_%s_%d", rule.second.first.bit.name.c_str(), + rule.second.first.inverted ? "inv" : "dup", ++dup_idx); + if (new_name_str[0] == '\\') + new_name_str[0] = '$'; + new_name.name = IdString(new_name_str); + must_invert_name = false; + } + // Create the wire if needed + Wire *new_wire = mod->wire(new_name.name); + if (!new_wire) { + Wire *gold_wire = rule.second.second; + new_wire = mod->addWire(new_name.name, gold_wire->width); + new_wire->start_offset = gold_wire->start_offset; + new_wire->upto = gold_wire->upto; + for (const auto &attr : gold_wire->attributes) + new_wire->attributes[attr.first] = attr.second; + for (int i = 0; i < new_wire->width; i++) + unused_bits.insert(SigBit(new_wire, i)); + } + // Ensure it's wide enough + if (new_wire->width <= new_name.bit) + new_wire->width = new_name.bit + 1; + SigBit old_bit = rule.first; + SigBit new_bit(new_wire, new_name.bit); + unused_bits.erase(new_bit); + // Replace all users + if (bit2port.count(old_bit)) + for (auto port_ref : bit2port.at(old_bit)) { + Cell *cell = std::get<0>(port_ref); + IdString port_name = std::get<1>(port_ref); + int port_bit = std::get<2>(port_ref); + SigSpec port_sig = cell->getPort(port_name); + port_sig.replace(port_bit, new_bit); + cell->unsetPort(port_name); + cell->setPort(port_name, port_sig); + } + } + } + + ~RecoverModuleWorker() + { + delete ce; + delete sigmap; + if (flat) + design->remove(flat); + } +}; + +struct RecoverNamesWorker { + Design *design, *gold_design = nullptr; + CellTypes ct_all; + RecoverNamesWorker(Design *design) : design(design) {} + + pool comb_whiteboxes, buffer_types; + + // class -> (gold, (gate, inverted)) + dict, dict>> cls2bits; + + void analyse_boxes() + { + for (auto mod : design->modules()) { + if (!mod->get_bool_attribute(ID::whitebox)) + continue; + bool is_comb = true; + for (auto cell : mod->cells()) { + if (ct_all.cell_evaluable(cell->type)) { + is_comb = false; + break; + } + } + if (!is_comb) + continue; + comb_whiteboxes.insert(mod->name); + // Buffers have one input and one output; exactly + SigBit in{}, out{}; + ConstEval eval(mod); + for (auto wire : mod->wires()) { + if (wire->port_input) { + if (wire->width != 1 || in.wire) + goto not_buffer; + in = SigBit(wire, 0); + } + if (wire->port_output) { + if (wire->width != 1 || out.wire) + goto not_buffer; + out = SigBit(wire, 0); + } + } + if (!in.wire || !out.wire) + goto not_buffer; + // Buffer input mirrors output + for (auto bit : {State::S0, State::S1}) { + eval.clear(); + eval.set(in, bit); + SigSpec result(out); + if (!eval.eval(result)) + goto not_buffer; + if (result != bit) + goto not_buffer; + } + buffer_types.insert(mod->name); + if (false) { + not_buffer: + continue; + } + } + log_debug("Found %d combinational cells and %d buffer whiteboxes.\n", GetSize(comb_whiteboxes), GetSize(buffer_types)); + } + + uint32_t x, y, z, w, rng_val; + int rng_bit; + void rng_init() + { + x = INIT_X; + y = INIT_Y; + z = INIT_Z; + w = INIT_W; + rng_bit = 32; + } + uint32_t xorshift128() + { + uint32_t t = x ^ (x << 11); + x = y; y = z; z = w; + w ^= (w >> 19) ^ t ^ (t >> 8); + return w; + } + RTLIL::State next_randbit() + { + if (rng_bit >= 32) { + rng_bit = 0; + rng_val = xorshift128(); + } + return ((rng_val >> (rng_bit++)) & 0x1) ? RTLIL::State::S1 : RTLIL::State::S0; + } + + int popcount(equiv_cls_t cls) { + int result = 0; + for (unsigned i = 0; i < 8*sizeof(equiv_cls_t); i++) + if ((cls >> i) & 0x1) + ++result; + return result; + } + + bool prove_equiv(RecoverModuleWorker &gold_worker, RecoverModuleWorker &gate_worker, + const dict &gold_anchors, const dict &gate_anchors, + IdBit gold_bit, IdBit gate_bit, bool invert) { + ezSatPtr ez; + SatGen satgen(ez.get(), nullptr); + dict anchor2var_gold, anchor2var_gate; + int gold_var = gold_worker.setup_sat(&satgen, "gold", gold_bit, gold_anchors, anchor2var_gold); + if (gold_var == -1) + return false; + int gate_var = gate_worker.setup_sat(&satgen, "gate", gate_bit, gate_anchors, anchor2var_gate); + if (gate_var == -1) + return false; + // Assume anchors are equal + for (auto anchor : anchor2var_gate) { + IdBit gold_anchor = gate_anchors.at(anchor.first); + if (!anchor2var_gold.count(gold_anchor)) + continue; + ez->assume(ez->IFF(anchor.second, anchor2var_gold.at(gold_anchor))); + } + // Prove equivalence + return !ez->solve(ez->NOT(ez->IFF(gold_var, invert ? ez->NOT(gate_var) : gate_var))); + } + + void analyse_mod(Module *gate_mod) + { + Module *gold_mod = gold_design->module(gate_mod->name); + if (!gold_mod) + return; + + RecoverModuleWorker gold_worker(gold_mod); + RecoverModuleWorker gate_worker(gate_mod); + + gold_worker.prepare(); + gate_worker.prepare(); + + // Find anchors (same-name wire-bits driven in both gold and gate) + dict gold_anchors, gate_anchors; + + for (auto gold_bit : gold_worker.bit2driver) { + if (gate_worker.bit2primary.count(gold_bit.first)) { + IdBit gate_bit = gate_worker.bit2primary.at(gold_bit.first); + if (!gate_worker.bit2driver.count(gate_bit)) + continue; + gold_anchors[gold_bit.first] = gate_bit; + gate_anchors[gate_bit] = gold_bit.first; + } + } + // Run a random-value combinational simulation to find candidate equivalence classes + dict gold_anchor_vals, gate_anchor_vals; + rng_init(); + for (int t = 0; t < sim_length; t++) { + for (auto anchor : gold_anchors) { + gold_anchor_vals[anchor.first] = next_randbit(); + gate_anchor_vals[anchor.second] = gold_anchor_vals[anchor.first]; + } + gold_worker.sim_cycle(t, gold_anchor_vals); + gate_worker.sim_cycle(t, gate_anchor_vals); + } + log_debug("%d candidate equiv classes in gold; %d in gate\n", GetSize(gold_worker.bit2cls), GetSize(gate_worker.bit2cls)); + // Group bits by equivalence classes together + dict, pool>> cls2bits; + gold_worker.group_classes(cls2bits, false); + gate_worker.group_classes(cls2bits, true); + gate_worker.compute_depths(gate_anchors); + // Sort equivalence classes by shallowest first (so we have as many anchors as possible when reaching deeper bits) + std::vector> cls_depth; + for (auto &cls : cls2bits) { + if (cls.second.second.empty()) + continue; + int depth = 0; + for (auto gate_bit : cls.second.second) { + if (!gate_worker.bit2depth.count(gate_bit.bit)) + continue; + depth = std::max(depth, gate_worker.bit2depth.at(gate_bit.bit)); + } + cls_depth.emplace_back(cls.first, depth); + } + std::stable_sort(cls_depth.begin(), cls_depth.end(), + [](const std::pair &a, const std::pair &b) { + return a.second < b.second; + }); + // The magic result we've worked hard for.... + dict gate2gold; + // Solve starting from shallowest + for (auto cls : cls_depth) { + int pop = popcount(cls.first); + // Equivalence classes with only one set bit are invariably a waste of SAT time + if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1)) + continue; + + log_debug("equivalence class: %016lx\n", cls.first); + const pool &gold_bits = cls2bits.at(cls.first).first; + const pool &gate_bits = cls2bits.at(cls.first).second; + if (gold_bits.empty() || gate_bits.empty()) + continue; + pool solved_gate; + if (GetSize(gold_bits) > 10) + continue; // large equivalence classes are not very interesting; skip + for (IdBit gold_bit : gold_bits) { + for (auto gate_bit : gate_bits) { + if (solved_gate.count(gate_bit.bit)) + continue; + log_debug(" attempting to prove %s[%d] == %s%s[%d]\n", log_id(gold_bit.name), gold_bit.bit, + gate_bit.inverted ? "" : "!", log_id(gate_bit.bit.name), gate_bit.bit.bit); + if (!prove_equiv(gold_worker, gate_worker, gold_anchors, gate_anchors, gold_bit, gate_bit.bit, gate_bit.inverted)) + continue; + log_debug(" success!\n"); + // Success! + gate2gold[gate_bit.bit] = InvBit(gold_bit, gate_bit.inverted); + if (!gate_bit.inverted) { + // Only add as anchor if not inverted + gold_anchors[gold_bit] = gate_bit.bit; + gate_anchors[gate_bit.bit] = gold_bit; + } + solved_gate.insert(gate_bit.bit); + } + // All solved... + if (GetSize(solved_gate) == GetSize(gate_bits)) + break; + } + } + log("Recovered %d net name pairs in module `%s' out.\n", GetSize(gate2gold), log_id(gate_mod)); + gate_worker.do_rename(gold_mod, gate2gold, buffer_types); + } + + void operator()(string command) + { + // Make a backup copy of the pre-mapping design for later + gold_design = new RTLIL::Design; + + for (auto mod : design->modules()) + gold_design->add(mod->clone()); + + run_pass(command, design); + + analyse_boxes(); + + // keeping our own std::vector here avoids modify-while-iterating issues + std::vector to_analyse; + for (auto mod : design->modules()) + if (!mod->get_blackbox_attribute()) + to_analyse.push_back(mod); + for (auto mod : to_analyse) + analyse_mod(mod); + } + ~RecoverNamesWorker() { + delete gold_design; + } +}; + +struct RecoverNamesPass : public Pass { + RecoverNamesPass() : Pass("recover_names", "Execute a lossy mapping command and recover original netnames") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" recover_names [command]\n"); + log("\n"); + log("This pass executes a lossy mapping command and uses a combination of simulation\n"); + log(" to find candidate equivalences and SAT to recover exact original net names.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing RECOVER_NAMES pass (run mapping and recover original names).\n"); + string command; + + size_t argidx = 1; + for (; argidx < args.size(); argidx++) { + if (command.empty()) { + if (args[argidx].compare(0, 1, "-") == 0) + cmd_error(args, argidx, "Unknown option."); + } else { + command += " "; + } + command += args[argidx]; + } + + if (command.empty()) + log_cmd_error("No mapping pass specified!\n"); + + RecoverNamesWorker worker(design); + worker(command); + + } +} RecoverNamesPass; + +PRIVATE_NAMESPACE_END From 00b0e850dbb649a40e7508ae04a10a0816db87fd Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 25 May 2023 16:17:09 +0100 Subject: [PATCH 11/16] intel_alm: re-enable carry chains for ABC9 --- techlibs/intel_alm/common/alm_sim.v | 8 +++----- tests/arch/intel_alm/counter.ys | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/techlibs/intel_alm/common/alm_sim.v b/techlibs/intel_alm/common/alm_sim.v index 242f1003f..d3bd67390 100644 --- a/techlibs/intel_alm/common/alm_sim.v +++ b/techlibs/intel_alm/common/alm_sim.v @@ -1,5 +1,5 @@ // The core logic primitive of the Cyclone V/10GX is the Adaptive Logic Module -// (ALM). Each ALM is made up of an 8-input, 2-output look-up table, covered +// (ALM). Each ALM is made up of an 8-input, 2-output look-up table, covered // in this file, connected to combinational outputs, a carry chain, and four // D flip-flops (which are covered as MISTRAL_FF in dff_sim.v). // @@ -283,10 +283,8 @@ assign Q = ~A; endmodule -// Despite the abc9_carry attributes, this doesn't seem to stop ABC9 adding illegal fanout to the carry chain that nextpnr cannot handle. -// So we treat it as a total blackbox from ABC9's perspective for now. -// (* abc9_box, lib_whitebox *) -module MISTRAL_ALUT_ARITH(input A, B, C, D0, D1, /* (* abc9_carry *) */ input CI, output SO, /* (* abc9_carry *) */ output CO); +(* abc9_box, lib_whitebox *) +module MISTRAL_ALUT_ARITH(input A, B, C, D0, D1, (* abc9_carry *) input CI, output SO, (* abc9_carry *) output CO); parameter LUT0 = 16'h0000; parameter LUT1 = 16'h0000; diff --git a/tests/arch/intel_alm/counter.ys b/tests/arch/intel_alm/counter.ys index 0a5b9356a..2b428fb3e 100644 --- a/tests/arch/intel_alm/counter.ys +++ b/tests/arch/intel_alm/counter.ys @@ -6,7 +6,7 @@ equiv_opt -assert -async2sync -map +/intel_alm/common/alm_sim.v -map +/intel_alm 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 1 t:MISTRAL_NOT +select -assert-count 2 t:MISTRAL_NOT select -assert-count 8 t:MISTRAL_ALUT_ARITH select -assert-count 8 t:MISTRAL_FF select -assert-none t:MISTRAL_NOT t:MISTRAL_ALUT_ARITH t:MISTRAL_FF %% t:* %D @@ -21,7 +21,7 @@ equiv_opt -assert -async2sync -map +/intel_alm/common/alm_sim.v -map +/intel_alm 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 1 t:MISTRAL_NOT +select -assert-count 2 t:MISTRAL_NOT select -assert-count 8 t:MISTRAL_ALUT_ARITH select -assert-count 8 t:MISTRAL_FF select -assert-none t:MISTRAL_NOT t:MISTRAL_ALUT_ARITH t:MISTRAL_FF %% t:* %D From 862631d657fe04a2b32a7703263a2bf016cf04de Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 21 May 2023 18:19:51 -0700 Subject: [PATCH 12/16] Add ABC9 DSP cascade test --- tests/arch/xilinx/dsp_abc9.ys | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/arch/xilinx/dsp_abc9.ys b/tests/arch/xilinx/dsp_abc9.ys index 909e54149..ae4839d7f 100644 --- a/tests/arch/xilinx/dsp_abc9.ys +++ b/tests/arch/xilinx/dsp_abc9.ys @@ -1,3 +1,5 @@ +logger -nowarn "Yosys has only limited support for tri-state logic at the moment\. .*" + read_verilog < Date: Sun, 21 May 2023 18:20:18 -0700 Subject: [PATCH 13/16] abc9_ops -prep_hier to unmap entire module --- passes/techmap/abc9_ops.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index ed4d4bdfb..4eaed1f75 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -233,22 +233,23 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) if (derived_type != cell->type) { auto unmap_module = unmap_design->addModule(derived_type); + auto replace_cell = unmap_module->addCell(ID::_TECHMAP_REPLACE_, cell->type); for (auto port : derived_module->ports) { auto w = unmap_module->addWire(port, derived_module->wire(port)); // Do not propagate (* init *) values into the box, // in fact, remove it from outside too if (w->port_output) w->attributes.erase(ID::init); + // Attach (* techmap_autopurge *) to all ports to ensure that + // undriven inputs/unused outputs are propagated through to + // the techmapped cell + w->attributes[ID::techmap_autopurge] = 1; + + replace_cell->setPort(port, w); } unmap_module->ports = derived_module->ports; unmap_module->check(); - auto replace_cell = unmap_module->addCell(ID::_TECHMAP_REPLACE_, cell->type); - for (const auto &conn : cell->connections()) { - auto w = unmap_module->wire(conn.first); - log_assert(w); - replace_cell->setPort(conn.first, w); - } replace_cell->parameters = cell->parameters; } } From cac1bc6fbe4db01060bfd04a51c9e12f82ef6167 Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 25 May 2023 17:48:48 +0100 Subject: [PATCH 14/16] intel_alm: enable M10K initialisation --- techlibs/intel_alm/common/bram_m10k.txt | 2 +- techlibs/intel_alm/common/bram_m10k_map.v | 6 ++++-- techlibs/intel_alm/common/mem_sim.v | 8 +++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/techlibs/intel_alm/common/bram_m10k.txt b/techlibs/intel_alm/common/bram_m10k.txt index 560711b65..60ebc5bf1 100644 --- a/techlibs/intel_alm/common/bram_m10k.txt +++ b/techlibs/intel_alm/common/bram_m10k.txt @@ -1,5 +1,5 @@ bram $__MISTRAL_M10K - init 0 # TODO: Re-enable when I figure out how BRAM init works + init 1 abits 13 @D8192x1 dbits 1 @D8192x1 abits 12 @D4096x2 diff --git a/techlibs/intel_alm/common/bram_m10k_map.v b/techlibs/intel_alm/common/bram_m10k_map.v index 8f9d4a3b3..3121b9d04 100644 --- a/techlibs/intel_alm/common/bram_m10k_map.v +++ b/techlibs/intel_alm/common/bram_m10k_map.v @@ -2,6 +2,8 @@ module \$__MISTRAL_M10K (CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); +parameter INIT = 0; + parameter CFG_ABITS = 10; parameter CFG_DBITS = 10; @@ -11,6 +13,6 @@ input [CFG_DBITS-1:0] A1DATA; input A1EN, B1EN; output reg [CFG_DBITS-1:0] B1DATA; -MISTRAL_M10K #(.CFG_ABITS(CFG_ABITS), .CFG_DBITS(CFG_DBITS)) _TECHMAP_REPLACE_ (.CLK1(CLK1), .A1ADDR(A1ADDR), .A1DATA(A1DATA), .A1EN(!A1EN), .B1ADDR(B1ADDR), .B1DATA(B1DATA), .B1EN(B1EN)); +MISTRAL_M10K #(.INIT(INIT), .CFG_ABITS(CFG_ABITS), .CFG_DBITS(CFG_DBITS)) _TECHMAP_REPLACE_ (.CLK1(CLK1), .A1ADDR(A1ADDR), .A1DATA(A1DATA), .A1EN(!A1EN), .B1ADDR(B1ADDR), .B1DATA(B1DATA), .B1EN(B1EN)); -endmodule \ No newline at end of file +endmodule diff --git a/techlibs/intel_alm/common/mem_sim.v b/techlibs/intel_alm/common/mem_sim.v index c9ba8c7f1..563f1d241 100644 --- a/techlibs/intel_alm/common/mem_sim.v +++ b/techlibs/intel_alm/common/mem_sim.v @@ -3,7 +3,7 @@ // In addition to Logic Array Blocks (LABs) that contain ten Adaptive Logic // Modules (ALMs, see alm_sim.v), the Cyclone V/10GX also contain // Memory/Logic Array Blocks (MLABs) that can act as either ten ALMs, or utilise -// the memory the ALM uses to store the look-up table data for general usage, +// the memory the ALM uses to store the look-up table data for general usage, // producing a 32 address by 20-bit block of memory. MLABs are spread out // around the chip, so they can be placed near where they are needed, rather than // being comparatively limited in placement for a deep but narrow memory such as @@ -43,7 +43,7 @@ // Quartus will pack external flops into the MLAB, but this is an assumption // that needs testing. -// The vendor sim model outputs 'x for a very short period (a few +// The vendor sim model outputs 'x for a very short period (a few // combinational delta cycles) after each write. This has been omitted from // the following model because it's very difficult to trigger this in practice // as clock cycles will be much longer than any potential blip of 'x, so the @@ -110,6 +110,8 @@ endmodule module MISTRAL_M10K(CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); +parameter INIT = 0; + parameter CFG_ABITS = 10; parameter CFG_DBITS = 10; @@ -119,7 +121,7 @@ input [CFG_DBITS-1:0] A1DATA; input A1EN, B1EN; output reg [CFG_DBITS-1:0] B1DATA; -reg [2**CFG_ABITS * CFG_DBITS - 1 : 0] mem = 0; +reg [2**CFG_ABITS * CFG_DBITS - 1 : 0] mem = INIT; `ifdef cyclonev specify From 8596c5ce4915d1727d93df45fc58047f08886b41 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 00:15:52 +0000 Subject: [PATCH 15/16] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0b77a837..bcb6a066c 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.29+34 +YOSYS_VER := 0.29+40 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From fb7af093a88daf9fa20fb7c45877686f5516808b Mon Sep 17 00:00:00 2001 From: Lofty Date: Mon, 29 May 2023 05:49:53 +0100 Subject: [PATCH 16/16] intel_alm: re-enable 8x40-bit M10K support --- techlibs/intel_alm/common/bram_m10k.txt | 2 ++ techlibs/intel_alm/common/bram_m10k_map.v | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/techlibs/intel_alm/common/bram_m10k.txt b/techlibs/intel_alm/common/bram_m10k.txt index 60ebc5bf1..14688a04c 100644 --- a/techlibs/intel_alm/common/bram_m10k.txt +++ b/techlibs/intel_alm/common/bram_m10k.txt @@ -10,6 +10,8 @@ bram $__MISTRAL_M10K dbits 10 @D1024x10 abits 9 @D512x20 dbits 20 @D512x20 + abits 8 @D256x40 + dbits 40 @D256x40 groups 2 ports 1 1 wrmode 1 0 diff --git a/techlibs/intel_alm/common/bram_m10k_map.v b/techlibs/intel_alm/common/bram_m10k_map.v index 3121b9d04..d48a1999c 100644 --- a/techlibs/intel_alm/common/bram_m10k_map.v +++ b/techlibs/intel_alm/common/bram_m10k_map.v @@ -13,6 +13,14 @@ input [CFG_DBITS-1:0] A1DATA; input A1EN, B1EN; output reg [CFG_DBITS-1:0] B1DATA; -MISTRAL_M10K #(.INIT(INIT), .CFG_ABITS(CFG_ABITS), .CFG_DBITS(CFG_DBITS)) _TECHMAP_REPLACE_ (.CLK1(CLK1), .A1ADDR(A1ADDR), .A1DATA(A1DATA), .A1EN(!A1EN), .B1ADDR(B1ADDR), .B1DATA(B1DATA), .B1EN(B1EN)); +// Normal M10K configs use WREN[1], which is negative-true. +// However, 8x40-bit mode uses WREN[0], which is positive-true. +wire a1en; +if (CFG_DBITS == 40) + assign a1en = A1EN; +else + assign a1en = !A1EN; + +MISTRAL_M10K #(.INIT(INIT), .CFG_ABITS(CFG_ABITS), .CFG_DBITS(CFG_DBITS)) _TECHMAP_REPLACE_ (.CLK1(CLK1), .A1ADDR(A1ADDR), .A1DATA(A1DATA), .A1EN(a1en), .B1ADDR(B1ADDR), .B1DATA(B1DATA), .B1EN(B1EN)); endmodule