From 303a386ecc89bbff7cfbc0d4a64535087826a372 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Fri, 31 Jan 2025 11:28:57 +0100 Subject: [PATCH] create duplicate IOFFs if multiple output ports are connected to the same register --- techlibs/quicklogic/ql_ioff.cc | 66 +++++++++++++++++++----- techlibs/quicklogic/synth_quicklogic.cc | 5 +- tests/arch/quicklogic/qlf_k6n10f/ioff.ys | 37 +++++++++++++ 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc index 4cc9785d5..87b62e855 100644 --- a/techlibs/quicklogic/ql_ioff.cc +++ b/techlibs/quicklogic/ql_ioff.cc @@ -29,7 +29,17 @@ struct QlIoffPass : public Pass { if (!module) return; modwalker.setup(module); - pool cells_to_replace; + pool input_ffs; + dict> output_ffs; + dict> output_bit_aliases; + + for (Wire* wire : module->wires()) + if (wire->port_output) { + output_ffs[wire].resize(wire->width, nullptr); + for (SigBit bit : SigSpec(wire)) + output_bit_aliases[modwalker.sigmap(bit)].insert(bit); + } + for (auto cell : module->selected_cells()) { if (cell->type.in(ID(dffsre), ID(sdffsre))) { log_debug("Checking cell %s.\n", cell->name.c_str()); @@ -53,32 +63,62 @@ struct QlIoffPass : public Pass { log_debug("not promoting: D has other consumers\n"); continue; } - cells_to_replace.insert(cell); - continue; // no need to check Q if we already put it on the list + input_ffs.insert(cell); + continue; // prefer input FFs over output FFs } + SigSpec q = cell->getPort(ID::Q); log_assert(GetSize(q) == 1); - if (modwalker.has_outputs(q)) { + if (modwalker.has_outputs(q) && !modwalker.has_consumers(q)) { log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str()); - // check that q_sig has no other consumers - pool portbits; - modwalker.get_consumers(portbits, q); - if (GetSize(portbits) > 0) { - log_debug("not promoting: Q has other consumers\n"); - continue; + for (SigBit bit : output_bit_aliases[modwalker.sigmap(q)]) { + log_assert(bit.is_wire()); + output_ffs[bit.wire][bit.offset] = cell; } - cells_to_replace.insert(cell); + } } } - for (auto cell : cells_to_replace) { - log("Promoting register %s to IOFF.\n", log_signal(cell->getPort(ID::Q))); + for (auto cell : input_ffs) { + log("Promoting register %s to input IOFF.\n", log_signal(cell->getPort(ID::Q))); cell->type = ID(dff); cell->unsetPort(ID::E); cell->unsetPort(ID::R); cell->unsetPort(ID::S); } + for (auto & [old_port_output, ioff_cells] : output_ffs) { + if (std::any_of(ioff_cells.begin(), ioff_cells.end(), [](Cell * c) { return c != nullptr; })) + { + // create replacement output wire + RTLIL::Wire* new_port_output = module->addWire(NEW_ID, old_port_output->width); + new_port_output->start_offset = old_port_output->start_offset; + module->swap_names(old_port_output, new_port_output); + std::swap(old_port_output->port_id, new_port_output->port_id); + std::swap(old_port_output->port_input, new_port_output->port_input); + std::swap(old_port_output->port_output, new_port_output->port_output); + std::swap(old_port_output->upto, new_port_output->upto); + std::swap(old_port_output->is_signed, new_port_output->is_signed); + std::swap(old_port_output->attributes, new_port_output->attributes); + + // create new output FFs + SigSpec sig_o(old_port_output); + SigSpec sig_n(new_port_output); + for (int i = 0; i < new_port_output->width; i++) { + if (ioff_cells[i]) { + log("Promoting %s to output IOFF.\n", log_signal(sig_n[i])); + + RTLIL::Cell *new_cell = module->addCell(NEW_ID, ID(dff)); + new_cell->setPort(ID::C, ioff_cells[i]->getPort(ID::C)); + new_cell->setPort(ID::D, ioff_cells[i]->getPort(ID::D)); + new_cell->setPort(ID::Q, sig_n[i]); + new_cell->set_bool_attribute(ID::keep); + } else { + module->connect(sig_n[i], sig_o[i]); + } + } + } + } } } QlIoffPass; diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 16f660943..07ec769b5 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -334,9 +334,10 @@ struct SynthQuickLogicPass : public ScriptPass { run("opt_lut"); } - if (check_label("iomap", "(for qlf_k6n10f)") && (family == "qlf_k6n10f" || help_mode)) { + if (check_label("iomap", "(for qlf_k6n10f, skip if -noioff)") && (family == "qlf_k6n10f" || help_mode)) { if (ioff || help_mode) { - run("ql_ioff", "(unless -noioff)"); + run("ql_ioff"); + run("opt_clean"); } } diff --git a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys index 3144eda13..da1fb2946 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys @@ -21,6 +21,21 @@ EOF synth_quicklogic -family qlf_k6n10f -top top select -assert-count 4 t:dff +design -reset +# test: acceptable for output IOFF promotion; duplicate output FF +read_verilog <