From 2dba3450497627726bbd6bbd6b1ba5c286b573c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 13 Nov 2024 16:20:05 +0100 Subject: [PATCH] portarcs: New command to derive propagation arcs --- kernel/timinginfo.h | 8 + passes/cmds/Makefile.inc | 1 + passes/cmds/portarcs.cc | 310 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 passes/cmds/portarcs.cc diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index e7e4eab6e2e..8eb7eb738ac 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -37,6 +37,14 @@ struct TimingInfo bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; } bool operator!=(const NameBit& nb) const { return !operator==(nb); } unsigned int hash() const { return mkhash_add(name.hash(), offset); } + std::optional get_connection(RTLIL::Cell *cell) { + if (!cell->hasPort(name)) + return {}; + auto &port = cell->getPort(name); + if (offset >= port.size()) + return {}; + return port[offset]; + } }; struct BitBit { diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index af5451b8961..95c799f8f64 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -50,3 +50,4 @@ OBJS += passes/cmds/dft_tag.o OBJS += passes/cmds/future.o OBJS += passes/cmds/box_derive.o OBJS += passes/cmds/example_dt.o +OBJS += passes/cmds/portarcs.o diff --git a/passes/cmds/portarcs.cc b/passes/cmds/portarcs.cc new file mode 100644 index 00000000000..7a1d5497c26 --- /dev/null +++ b/passes/cmds/portarcs.cc @@ -0,0 +1,310 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin PoviĊĦer + * + * 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/timinginfo.h" +#include "kernel/rtlil.h" +#include "kernel/utils.h" +#include "kernel/celltypes.h" + +PRIVATE_NAMESPACE_BEGIN +USING_YOSYS_NAMESPACE + +static RTLIL::SigBit canonical_bit(RTLIL::SigBit bit) +{ + RTLIL::Wire *w; + while ((w = bit.wire) != NULL && !w->port_input && + w->driverCell()->type.in(ID($buf), ID($_BUF_))) { + bit = w->driverCell()->getPort(ID::A)[bit.offset]; + } + return bit; +} + +struct PortarcsPass : Pass { + PortarcsPass() : Pass("portarcs", "derive port arcs for propagation delay") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" portarcs [options] [selection]\n"); + log("\n"); + log("This command characterizes the combinational content of selected modules and\n"); + log("derives timing arcs going from module inputs to module outputs representing the\n"); + log("propagation delay of the module.\n"); + log("\n"); + log(" -draw\n"); + log(" plot the computed delay table to the terminal\n"); + log("\n"); + log(" -icells\n"); + log(" assign unit delay to gates from the internal Yosys cell library\n"); + log("\n"); + log(" -write\n"); + log(" write the computed arcs back into the module as $specify2 instances\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *d) override + { + log_header(d, "Executing PORTARCS pass. (derive propagation arcs)\n"); + + size_t argidx; + bool icells_mode = false, write_mode = false, draw_mode = false; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-icells") + icells_mode = true; + else if (args[argidx] == "-write") + write_mode = true; + else if (args[argidx] == "-draw") + draw_mode = true; + else + break; + } + extra_args(args, argidx, d); + + d->bufNormalize(true); + TimingInfo tinfo(d); + + if (icells_mode) { + CellTypes ct; + ct.setup_stdcells_eval(); + for (auto [id, type] : ct.cell_types) { + auto &tdata = tinfo.data[id]; + tdata.has_inputs = true; + for (auto inp : type.inputs) + for (auto out : type.outputs) + tdata.comb[TimingInfo::BitBit({inp, 0}, {out, 0})] = 1000; + } + } + + for (auto m : d->selected_whole_modules_warn()) { + bool ambiguous_ports = false; + SigSpec inputs, outputs; + for (auto port : m->ports) { + Wire *w = m->wire(port); + log_assert(w->port_input || w->port_output); + if (w->port_input && w->port_output) { + log_warning("Module '%s' with ambiguous direction on port %s ignored.\n", + log_id(m), log_id(w)); + ambiguous_ports = true; + break; + } + if (w->port_input) + inputs.append(w); + else + outputs.append(w); + } + if (ambiguous_ports) + continue; + + SigSpec ordering; + { + TopoSort sort; + + for (auto cell : m->cells()) + if (cell->type != ID($buf)) { + auto tdata = tinfo.find(cell->type); + if (tdata == tinfo.end()) + log_cmd_error("Missing timing data for module '%s'.\n", log_id(cell->type)); + for (auto [edge, delay] : tdata->second.comb) { + auto from = edge.first.get_connection(cell); + auto to = edge.second.get_connection(cell); + if (from && to) { + auto from_c = canonical_bit(from.value()); + if (from_c.wire) + sort.edge(from_c, to.value()); + } + } + } + + if (!sort.sort()) + log_error("Failed to sort instances in module %s.\n", log_id(m)); + + ordering = sort.sorted; + } + + dict annotations; + std::vector> allocated; + std::vector recycling; + + auto alloc_for_bit = [&](SigBit bit) { + if (!recycling.empty()) { + annotations[bit] = recycling.back(); + recycling.pop_back(); + } else { + int *p = new int[std::max(1, inputs.size())]; + allocated.emplace_back(p); + annotations[bit] = p; + } + }; + + for (auto bit : outputs) { + SigBit bit_c = canonical_bit(bit); + alloc_for_bit(bit_c); + + // consistency check + annotations.at(bit_c)[0] = (intptr_t) bit_c.wire; + } + + for (int i = ordering.size() - 1; i >= 0; i--) { + SigBit bit = ordering[i]; + + if (!bit.wire->port_input) { + auto cell = bit.wire->driverCell(); + auto tdata = tinfo.find(cell->type); + log_assert(tdata != tinfo.end()); + for (auto [edge, delay] : tdata->second.comb) { + auto from = edge.first.get_connection(cell); + auto to = edge.second.get_connection(cell); + if (from && to && to.value() == bit) { + auto from_c = canonical_bit(from.value()); + if (from_c.wire) { + if (!annotations.count(from_c)) { + alloc_for_bit(from_c); + + // consistency check + annotations.at(from_c)[0] = (intptr_t) from_c.wire; + } else { + // consistency check + log_assert(annotations.at(from_c)[0] == ((int) (intptr_t) from_c.wire)); + } + } + } + } + } + + if (annotations.count(bit)) { + // consistency check + log_assert(annotations.at(bit)[0] == ((int) (intptr_t) bit.wire)); + + recycling.push_back(annotations.at(ordering[i])); + } + } + log_debug("Allocated %lux%d\n", allocated.size(), inputs.size()); + + for (auto bit : outputs) { + int *p = annotations.at(canonical_bit(bit)); + for (int i = 0; i < inputs.size(); i++) + p[i] = 0; + } + + for (int i = 0; i < ordering.size(); i++) { + SigBit bit = ordering[i]; + int *p = annotations.at(bit); + if (bit.wire->port_input) { + for (int j = 0; j < inputs.size(); j++) + p[j] = (j == i) ? 0 : -1; + } else { + for (int j = 0; j < inputs.size(); j++) + p[j] = -1; + + auto cell = ordering[i].wire->driverCell(); + auto tdata = tinfo.find(cell->type); + log_assert(tdata != tinfo.end()); + for (auto [edge, delay] : tdata->second.comb) { + auto from = edge.first.get_connection(cell); + auto to = edge.second.get_connection(cell); + if (from && to && to.value() == ordering[i]) { + auto from_c = canonical_bit(from.value()); + if (from_c.wire) { + int *q = annotations.at(from_c); + for (int j = 0; j < inputs.size(); j++) + if (q[j] >= 0) + p[j] = std::max(p[j], q[j] + delay); + } + } + } + } + } + + if (draw_mode) { + auto bit_str = [](SigBit bit) { + return stringf("%s%d", RTLIL::unescape_id(bit.wire->name.str()).c_str(), bit.offset); + }; + + std::vector headings; + int top_length = 0; + for (auto bit : inputs) { + headings.push_back(bit_str(bit)); + top_length = std::max(top_length, (int) headings.back().size()); + } + + int max_delay = 0; + for (auto bit : outputs) { + int *p = annotations.at(canonical_bit(bit)); + for (auto i = 0; i < inputs.size(); i++) + if (p[i] > max_delay) + max_delay = p[i]; + } + + log("Delay legend:\n\n"); + log(" "); + for (int i = 0; i < 24; i++) + log("\033[48;5;%dm ", 232+i); + log("\033[0m\n"); + log(" |%22s|\n", ""); + log(" 0%22s%d\n", "", max_delay); + log("\n"); + for (int k = top_length - 1; k >= 0; k--) { + log(" %10s ", ""); + for (auto &h : headings) + log("%c", (k < (int) h.size()) ? h[k] : ' '); + log("\n"); + } + log("\n"); + + for (auto bit : outputs) { + log(" %10s ", bit_str(bit).c_str()); + int *p = annotations.at(canonical_bit(bit)); + for (auto i = 0; i < inputs.size(); i++) + log("\033[48;5;%dm ", 232 + ((std::max(p[i], 0) * 24) - 1) / max_delay); + log("\033[0m\n"); + } + } + + if (write_mode) { + for (auto bit : outputs) { + int *p = annotations.at(canonical_bit(bit)); + for (auto i = 0; i < inputs.size(); i++) { + if (p[i] >= 0) { + Cell *spec = m->addCell(NEW_ID, ID($specify2)); + spec->setParam(ID::SRC_WIDTH, 1); + spec->setParam(ID::DST_WIDTH, 1); + spec->setParam(ID::T_FALL_MAX, p[i]); + spec->setParam(ID::T_FALL_TYP, p[i]); + spec->setParam(ID::T_FALL_MIN, p[i]); + spec->setParam(ID::T_RISE_MAX, p[i]); + spec->setParam(ID::T_RISE_TYP, p[i]); + spec->setParam(ID::T_RISE_MIN, p[i]); + spec->setParam(ID::SRC_DST_POL, false); + spec->setParam(ID::SRC_DST_PEN, false); + spec->setParam(ID::FULL, false); + spec->setPort(ID::EN, Const(1, 1)); + spec->setPort(ID::SRC, inputs[i]); + spec->setPort(ID::DST, bit); + } + } + } + } + } + d->bufNormalize(false); + } +} PortarcsPass; + +PRIVATE_NAMESPACE_END