diff options
Diffstat (limited to 'passes/cmds')
-rw-r--r-- | passes/cmds/Makefile.inc | 3 | ||||
-rw-r--r-- | passes/cmds/bugpoint.cc | 2 | ||||
-rw-r--r-- | passes/cmds/clean_zerowidth.cc | 210 | ||||
-rw-r--r-- | passes/cmds/glift.cc | 599 | ||||
-rw-r--r-- | passes/cmds/select.cc | 18 | ||||
-rw-r--r-- | passes/cmds/show.cc | 60 | ||||
-rw-r--r-- | passes/cmds/sta.cc | 312 | ||||
-rw-r--r-- | passes/cmds/stat.cc | 6 |
8 files changed, 1174 insertions, 36 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 53bfd40c6..16a38b511 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -18,6 +18,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o +OBJS += passes/cmds/glift.o OBJS += passes/cmds/torder.o OBJS += passes/cmds/logcmd.o OBJS += passes/cmds/tee.o @@ -40,3 +41,5 @@ endif OBJS += passes/cmds/scratchpad.o OBJS += passes/cmds/logger.o OBJS += passes/cmds/printattrs.o +OBJS += passes/cmds/sta.o +OBJS += passes/cmds/clean_zerowidth.o diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 16ac5b6a7..7b621504d 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -377,7 +377,7 @@ struct BugpointPass : public Pass { if (wire->get_bool_attribute(ID::bugpoint_keep)) continue; - if (wire->name.begins_with("$delete_wire")) + if (wire->name.begins_with("$delete_wire") || wire->name.begins_with("$auto$bugpoint")) continue; if (index++ == seed) diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc new file mode 100644 index 000000000..bac6b1521 --- /dev/null +++ b/passes/cmds/clean_zerowidth.cc @@ -0,0 +1,210 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net> + * + * 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/celltypes.h" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CleanZeroWidthPass : public Pass { + CleanZeroWidthPass() : Pass("clean_zerowidth", "clean zero-width connections from the design") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" clean_zerowidth [selection]\n"); + log("\n"); + log("Fixes the selected cells and processes to contain no zero-width connections.\n"); + log("Depending on the cell type, this may be implemented by removing the connection,\n"); + log("widening it to 1-bit, or removing the cell altogether.\n"); + log("\n"); + } + + void clean_case(RTLIL::CaseRule *cs) + { + std::vector<SigSig> new_actions; + for (auto &action : cs->actions) + if (GetSize(action.first) != 0) + new_actions.push_back(action); + std::swap(new_actions, cs->actions); + for (auto sw : cs->switches) + for (auto scs : sw->cases) + clean_case(scs); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + CellTypes ct; + ct.setup(); + + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (!ct.cell_known(cell->type)) { + // User-defined cell: just prune zero-width connections. + for (auto it: cell->connections()) { + if (GetSize(it.second) == 0) { + cell->unsetPort(it.first); + } + } + } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + // Coarse FF cells: remove if WIDTH == 0 (no outputs). + // This will also trigger on fine cells, so use the Q port + // width instead of actual WIDTH parameter. + if (GetSize(cell->getPort(ID::Q)) == 0) { + module->remove(cell); + } + } else if (cell->type.in(ID($pmux), ID($bmux), ID($demux))) { + // Remove altogether if WIDTH is 0, replace with + // a connection if S_WIDTH is 0. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->remove(cell); + } + if (cell->getParam(ID::S_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); + module->remove(cell); + } + } else if (cell->type == ID($concat)) { + // If a concat has a zero-width input: replace with direct + // connection to the other input. + if (cell->getParam(ID::A_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::B)); + module->remove(cell); + } else if (cell->getParam(ID::B_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); + module->remove(cell); + } + } else if (cell->type == ID($fsm)) { + // TODO: not supported + } else if (cell->is_mem_cell()) { + // Skip — will be handled below. + } else if (cell->type == ID($lut)) { + // Zero-width LUT is just a const driver. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getParam(ID::LUT)[0]); + module->remove(cell); + } + } else if (cell->type == ID($sop)) { + // Zero-width SOP is just a const driver. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + // The value is 1 iff DEPTH is non-0. + bool val = cell->getParam(ID::DEPTH).as_int() != 0; + module->connect(cell->getPort(ID::Y), val); + module->remove(cell); + } + } else if (cell->hasParam(ID::WIDTH)) { + // For cells with WIDTH parameter: remove if zero. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->remove(cell); + } + } else if (cell->hasParam(ID::Y_WIDTH)) { + // For most operators: remove if Y width is 0, expand + // A and B to 1-bit if their width is 0. + if (cell->getParam(ID::Y_WIDTH).as_int() == 0) { + module->remove(cell); + } else if (cell->type == ID($macc)) { + // TODO: fixing zero-width A and B not supported. + } else { + if (cell->getParam(ID::A_WIDTH).as_int() == 0) { + cell->setPort(ID::A, State::S0); + cell->setParam(ID::A_WIDTH, 1); + } + if (cell->hasParam(ID::B_WIDTH) && cell->getParam(ID::B_WIDTH).as_int() == 0) { + cell->setPort(ID::B, State::S0); + cell->setParam(ID::B_WIDTH, 1); + } + } + } + } + + // NOTE: Zero-width switch signals are left alone for processes, as there + // is no simple way of cleaning them up. + for (auto &it: module->processes) { + if (!design->selected(module, it.second)) + continue; + clean_case(&it.second->root_case); + for (auto sync : it.second->syncs) { + std::vector<int> swizzle; + std::vector<RTLIL::MemWriteAction> new_memwr_actions; + for (int i = 0; i < GetSize(sync->mem_write_actions); i++) { + auto &memwr = sync->mem_write_actions[i]; + if (GetSize(memwr.data) == 0) + continue; + if (GetSize(memwr.address) == 0) + memwr.address = State::S0; + Const priority_mask; + for (auto x : swizzle) { + priority_mask.bits.push_back(memwr.priority_mask.bits[x]); + } + memwr.priority_mask = priority_mask; + swizzle.push_back(i); + new_memwr_actions.push_back(memwr); + } + std::swap(new_memwr_actions, sync->mem_write_actions); + std::vector<SigSig> new_actions; + for (auto &action : sync->actions) + if (GetSize(action.first) != 0) + new_actions.push_back(action); + std::swap(new_actions, sync->actions); + } + } + + for (auto &mem : Mem::get_selected_memories(module)) { + if (mem.width == 0) { + mem.remove(); + continue; + } + for (auto &init : mem.inits) { + if (GetSize(init.addr) == 0) { + init.addr = State::S0; + } + } + for (auto &port : mem.rd_ports) { + if (GetSize(port.addr) == 0) { + port.addr = State::S0; + } + } + for (auto &port : mem.wr_ports) { + if (GetSize(port.addr) == 0) { + port.addr = State::S0; + } + } + mem.emit(); + } + + std::vector<SigSig> new_conns; + for (auto &conn : module->connections()) + if (GetSize(conn.first) != 0) + new_conns.push_back(conn); + module->new_connections(new_conns); + } + } +} CleanZeroWidthPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc new file mode 100644 index 000000000..b398c3e04 --- /dev/null +++ b/passes/cmds/glift.cc @@ -0,0 +1,599 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Alberto Gonzalez <boqwxp@airmail.cc> + * + * 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/register.h" +#include "kernel/rtlil.h" +#include "kernel/utils.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct GliftWorker { +private: + bool is_top_module = false; + bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false; + bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false; + bool opt_instrumentmore = false; + std::vector<RTLIL::Wire *> new_taint_outputs; + std::vector<std::pair<RTLIL::SigSpec, RTLIL::IdString>> meta_mux_selects; + RTLIL::Module *module = nullptr; + + const RTLIL::IdString cost_model_wire_name = ID(__glift_weight); + const RTLIL::IdString glift_attribute_name = ID(glift); + + + RTLIL::SigSpec get_corresponding_taint_signal(RTLIL::SigSpec sig) { + RTLIL::SigSpec ret; + + //Get the connected wire for the cell port: + log_assert(sig.is_wire() || sig.is_fully_const()); + log_assert(sig.is_wire() || sig.is_fully_const()); + + //Get a SigSpec for the corresponding taint signal for the cell port, creating one if necessary: + if (sig.is_wire()) { + RTLIL::Wire *w = module->wire(sig.as_wire()->name.str() + "_t"); + if (w == nullptr) w = module->addWire(sig.as_wire()->name.str() + "_t", 1); + ret = w; + } + else if (sig.is_fully_const() && opt_taintconstants) + ret = RTLIL::State::S1; + else if (sig.is_fully_const()) + ret = RTLIL::State::S0; + else + log_cmd_error("Cell port SigSpec has unexpected type.\n"); + + //Finally, if the cell port was a module input or output, make sure the corresponding taint signal is marked, too: + if(sig.is_wire() && sig.as_wire()->port_input) + ret.as_wire()->port_input = true; + if(sig.is_wire() && sig.as_wire()->port_output) + new_taint_outputs.push_back(ret.as_wire()); + + return ret; + } + + void add_precise_GLIFT_logic(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH2 or OR2_SH2 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_1_1", port_a, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_1_2", port_b, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_1_3", is_and? port_a : n_port_a, port_b_taint, false, cell->get_src_attribute()); + auto subexpr2 = module->And(cell->name.str() + "_t_1_4", is_and? port_b : n_port_b, port_a_taint, false, cell->get_src_attribute()); + auto subexpr3 = module->And(cell->name.str() + "_t_1_5", port_a_taint, port_b_taint, false, cell->get_src_attribute()); + auto subexpr4 = module->Or(cell->name.str() + "_t_1_6", subexpr1, subexpr2, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_1_7", subexpr4, subexpr3, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_1(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH3 or OR2_SH3 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_2_1", port_a, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_2_2", is_and? port_b : n_port_a, is_and? port_a_taint : port_b_taint, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_2_3", is_and? port_b_taint : port_a_taint, subexpr1, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_2(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH4 or OR2_SH4 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_3_1", port_b, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_3_2", is_and? port_a : n_port_b, is_and? port_b_taint : port_a_taint, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_3_3", is_and? port_a_taint : port_b_taint, subexpr1, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_3(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH5 or OR2_SH5 or XR2_SH2 + module->addOr(cell->name.str() + "_t_4_1", port_a_taint, port_b_taint, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_4(RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, port_a_taint); + } + + void add_imprecise_GLIFT_logic_5(RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, port_b_taint); + } + + void add_imprecise_GLIFT_logic_6(RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, RTLIL::Const(1, 1)); + } + + void add_imprecise_GLIFT_logic_7(RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, RTLIL::Const(0, 1)); + } + + void add_precise_GLIFT_mux(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_s, RTLIL::SigSpec &port_s_taint, RTLIL::SigSpec &port_y_taint) { + //S&At | ~S&Bt | ~A&B&St | A&~B&St | At&St | Bt&St + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_4_1", port_a, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_4_2", port_b, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_s = module->LogicNot(cell->name.str() + "_t_4_3", port_s, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_4_4", port_s, port_a_taint, false, cell->get_src_attribute()); + auto subexpr2 = module->And(cell->name.str() + "_t_4_5", n_port_s, port_b_taint, false, cell->get_src_attribute()); + auto subexpr3 = module->And(cell->name.str() + "_t_4_6", n_port_a, port_b, false, cell->get_src_attribute()); + auto subexpr4 = module->And(cell->name.str() + "_t_4_7", subexpr3, port_s_taint, false, cell->get_src_attribute()); + auto subexpr5 = module->And(cell->name.str() + "_t_4_8", port_a, n_port_b, false, cell->get_src_attribute()); + auto subexpr6 = module->And(cell->name.str() + "_t_4_9", subexpr5, port_s_taint, false, cell->get_src_attribute()); + auto subexpr7 = module->And(cell->name.str() + "_t_4_10", port_a_taint, port_s_taint, false, cell->get_src_attribute()); + auto subexpr8 = module->And(cell->name.str() + "_t_4_11", port_b_taint, port_s_taint, false, cell->get_src_attribute()); + auto subexpr9 = module->Or(cell->name.str() + "_t_4_12", subexpr1, subexpr2, false, cell->get_src_attribute()); + auto subexpr10 = module->Or(cell->name.str() + "_t_4_13", subexpr4, subexpr6, false, cell->get_src_attribute()); + auto subexpr11 = module->Or(cell->name.str() + "_t_4_14", subexpr7, subexpr8, false, cell->get_src_attribute()); + auto subexpr12 = module->Or(cell->name.str() + "_t_4_15", subexpr9, subexpr10, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_4_16", subexpr11, subexpr12, port_y_taint, false, cell->get_src_attribute()); + } + + RTLIL::SigSpec score_metamux_select(const RTLIL::SigSpec &metamux_select, const RTLIL::IdString celltype) { + log_assert(metamux_select.is_wire()); + + if (opt_simplecostmodel) { + //The complex model is an area model, so a lower score should mean smaller. + //In this case, a nonzero hole metamux select value means less logic. + //Thus we should invert the ReduceOr over the metamux_select signal. + RTLIL::SigSpec pmux_select = module->ReduceOr(metamux_select.as_wire()->name.str() + "_nonzero", metamux_select); + return module->Pmux(NEW_ID, RTLIL::Const(1), RTLIL::Const(0), pmux_select, metamux_select.as_wire()->get_src_attribute()); + } else { + auto select_width = metamux_select.as_wire()->width; + + std::vector<RTLIL::Const> costs; + if (celltype == ID($_AND_) || celltype == ID($_OR_)) { + costs = {5, 2, 2, 1, 0, 0, 0, 0}; + log_assert(select_width == 2 || select_width == 3); + log_assert(opt_instrumentmore || select_width == 2); + log_assert(!opt_instrumentmore || select_width == 3); + } + else if (celltype == ID($_XOR_) || celltype == ID($_XNOR_)) { + costs = {1, 0, 0, 0}; + log_assert(select_width == 2); + } + + std::vector<RTLIL::SigSpec> next_pmux_y_ports, pmux_y_ports(costs.begin(), costs.begin() + exp2(select_width)); + for (auto i = 0; pmux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(pmux_y_ports); j += 2) { + next_pmux_y_ports.emplace_back(module->Pmux(stringf("%s_mux_%d_%d", metamux_select.as_wire()->name.c_str(), i, j), pmux_y_ports[j], pmux_y_ports[j+1], metamux_select[GetSize(metamux_select) - 1 - i], metamux_select.as_wire()->get_src_attribute())); + } + if (GetSize(pmux_y_ports) % 2 == 1) + next_pmux_y_ports.push_back(pmux_y_ports[GetSize(pmux_y_ports) - 1]); + pmux_y_ports.swap(next_pmux_y_ports); + next_pmux_y_ports.clear(); + } + + log_assert(pmux_y_ports.size() == 1); + return pmux_y_ports[0]; + } + } + + void create_glift_logic() { + if (module->get_bool_attribute(glift_attribute_name)) + return; + + std::vector<RTLIL::SigSig> connections(module->connections()); + + for(auto &cell : module->cells().to_vector()) { + if (!cell->type.in({ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_MUX_), ID($_NMUX_), ID($_NOT_), ID($anyconst), ID($allconst), ID($assume), ID($assert)}) && module->design->module(cell->type) == nullptr) { + log_cmd_error("Unsupported cell type \"%s\" found. Run `techmap` first.\n", cell->type.c_str()); + } + if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_))) { + const unsigned int A = 0, B = 1, Y = 2; + const unsigned int NUM_PORTS = 3; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (opt_create_precise_model) + add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], port_taints[Y]); + else if (opt_create_imprecise_model) + add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]); + else if (opt_create_instrumented_model) { + std::vector<RTLIL::SigSpec> taint_version; + int num_versions = opt_instrumentmore? 8 : 4; + + for (auto i = 1; i <= num_versions; ++i) + taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1))); + + for (auto i = 0; i < num_versions; ++i) { + switch(i) { + case 0: add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 1: add_imprecise_GLIFT_logic_1(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 2: add_imprecise_GLIFT_logic_2(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 3: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]); + break; + case 4: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]); + break; + case 5: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]); + break; + case 6: add_imprecise_GLIFT_logic_6(taint_version[i]); + break; + case 7: add_imprecise_GLIFT_logic_7(taint_version[i]); + break; + default: log_assert(false); + } + } + + auto select_width = log2(num_versions); + log_assert(exp2(select_width) == num_versions); + RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width)); + meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type)); + module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute())); + + std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version); + for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) { + next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i])); + } + if (GetSize(meta_mux_y_ports) % 2 == 1) + next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]); + meta_mux_y_ports.swap(next_meta_mux_y_ports); + next_meta_mux_y_ports.clear(); + } + log_assert(meta_mux_y_ports.size() == 1); + module->connect(port_taints[Y], meta_mux_y_ports[0]); + } + else log_cmd_error("This is a bug (1).\n"); + } + else if (cell->type.in(ID($_XOR_), ID($_XNOR_))) { + const unsigned int A = 0, B = 1, Y = 2; + const unsigned int NUM_PORTS = 3; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (opt_create_precise_model || opt_create_imprecise_model) + add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]); + else if (opt_create_instrumented_model) { + std::vector<RTLIL::SigSpec> taint_version; + int num_versions = 4; + auto select_width = log2(num_versions); + log_assert(exp2(select_width) == num_versions); + + for (auto i = 1; i <= num_versions; ++i) + taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1))); + + for (auto i = 0; i < num_versions; ++i) { + switch(i) { + case 0: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]); + break; + case 1: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]); + break; + case 2: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]); + break; + case 3: add_imprecise_GLIFT_logic_6(taint_version[i]); + break; + default: log_assert(false); + } + } + + RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width)); + meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type)); + module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute())); + + std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version); + for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) { + next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i])); + } + if (GetSize(meta_mux_y_ports) % 2 == 1) + next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]); + meta_mux_y_ports.swap(next_meta_mux_y_ports); + next_meta_mux_y_ports.clear(); + } + log_assert(meta_mux_y_ports.size() == 1); + module->connect(port_taints[Y], meta_mux_y_ports[0]); + } + else log_cmd_error("This is a bug (2).\n"); + + } + else if (cell->type.in(ID($_MUX_), ID($_NMUX_))) { + const unsigned int A = 0, B = 1, S = 2, Y = 3; + const unsigned int NUM_PORTS = 4; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::S), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[S].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + add_precise_GLIFT_mux(cell, ports[A], port_taints[A], ports[B], port_taints[B], ports[S], port_taints[S], port_taints[Y]); + } + else if (cell->type.in(ID($_NOT_))) { + const unsigned int A = 0, Y = 1; + const unsigned int NUM_PORTS = 2; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (cell->type == ID($_NOT_)) { + module->connect(port_taints[Y], port_taints[A]); + } + else log_cmd_error("This is a bug (3).\n"); + } + else if (module->design->module(cell->type) != nullptr) { + //User cell type + //This function is called on modules according to topological order, so we do not need to + //recurse to GLIFT model the child module. However, we need to augment the ports list + //with taint signals and connect the new ports to the corresponding taint signals. + RTLIL::Module *cell_module_def = module->design->module(cell->type); + dict<RTLIL::IdString, RTLIL::SigSpec> orig_ports = cell->connections(); + log("Adding cell %s\n", cell_module_def->name.c_str()); + for (auto &it : orig_ports) { + RTLIL::SigSpec port = it.second; + RTLIL::SigSpec port_taint = get_corresponding_taint_signal(port); + + log_assert(port_taint.is_wire()); + log_assert(std::find(cell_module_def->ports.begin(), cell_module_def->ports.end(), port_taint.as_wire()->name) != cell_module_def->ports.end()); + cell->setPort(port_taint.as_wire()->name, port_taint); + } + } + else log_cmd_error("This is a bug (4).\n"); + } //end foreach cell in cells + + for (auto &conn : connections) { + RTLIL::SigSpec first = get_corresponding_taint_signal(conn.first); + RTLIL::SigSpec second = get_corresponding_taint_signal(conn.second); + + module->connect(first, second); + + if(conn.second.is_wire() && conn.second.as_wire()->port_input) + second.as_wire()->port_input = true; + if(conn.first.is_wire() && conn.first.as_wire()->port_output) + new_taint_outputs.push_back(first.as_wire()); + } //end foreach conn in connections + + //Create a rough model of area by summing the (potentially simplified) "weight" score of each meta-mux select: + if (!opt_nocostmodel) { + std::vector<RTLIL::SigSpec> meta_mux_select_sums; + std::vector<RTLIL::SigSpec> meta_mux_select_sums_buf; + for (auto &it : meta_mux_selects) { + meta_mux_select_sums.emplace_back(score_metamux_select(it.first, it.second)); + } + for (unsigned int i = 0; meta_mux_select_sums.size() > 1; ) { + meta_mux_select_sums_buf.clear(); + for (i = 0; i + 1 < meta_mux_select_sums.size(); i += 2) { + meta_mux_select_sums_buf.push_back(module->Add(meta_mux_select_sums[i].as_wire()->name.str() + "_add", meta_mux_select_sums[i], meta_mux_select_sums[i+1], false)); + } + if (meta_mux_select_sums.size() % 2 == 1) + meta_mux_select_sums_buf.push_back(meta_mux_select_sums[meta_mux_select_sums.size()-1]); + meta_mux_select_sums.swap(meta_mux_select_sums_buf); + } + if (meta_mux_select_sums.size() > 0) { + meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\minimize"); + meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\keep"); + module->rename(meta_mux_select_sums[0].as_wire(), cost_model_wire_name); + } + } + + //Mark new module outputs: + for (auto &port_name : module->ports) { + RTLIL::Wire *port = module->wire(port_name); + log_assert(port != nullptr); + if (is_top_module && port->port_output && !opt_keepoutputs) + port->port_output = false; + } + for (auto &output : new_taint_outputs) + output->port_output = true; + module->fixup_ports(); //we have some new taint signals in the module interface + module->set_bool_attribute(glift_attribute_name, true); + } + +public: + GliftWorker(RTLIL::Module *_module, bool _is_top_module, bool _opt_create_precise_model, bool _opt_create_imprecise_model, bool _opt_create_instrumented_model, bool _opt_taintconstants, bool _opt_keepoutputs, bool _opt_simplecostmodel, bool _opt_nocostmodel, bool _opt_instrumentmore) { + module = _module; + is_top_module = _is_top_module; + opt_create_precise_model = _opt_create_precise_model; + opt_create_imprecise_model = _opt_create_imprecise_model; + opt_create_instrumented_model = _opt_create_instrumented_model; + opt_taintconstants = _opt_taintconstants; + opt_keepoutputs = _opt_keepoutputs; + opt_simplecostmodel = _opt_simplecostmodel; + opt_nocostmodel = _opt_nocostmodel; + opt_instrumentmore = _opt_instrumentmore; + + create_glift_logic(); + } +}; + +struct GliftPass : public Pass { + GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" glift <command> [options] [selection]\n"); + log("\n"); + log("Augments the current or specified module with gate-level information flow tracking\n"); + log("(GLIFT) logic using the \"constructive mapping\" approach. Also can set up QBF-SAT\n"); + log("optimization problems in order to optimize GLIFT models or trade off precision and\n"); + log("complexity.\n"); + log("\n"); + log("\n"); + log("Commands:\n"); + log("\n"); + log(" -create-precise-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with precise taint tracking logic.\n"); + log(" For example, precise taint tracking logic for an AND gate is:\n"); + log("\n"); + log(" y_t = a & b_t | b & a_t | a_t & b_t\n"); + log("\n"); + log("\n"); + log(" -create-imprecise-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with imprecise \"All OR\" taint tracking\n"); + log(" logic:\n"); + log("\n"); + log(" y_t = a_t | b_t\n"); + log("\n"); + log("\n"); + log(" -create-instrumented-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with 4 varying-precision versions of taint\n"); + log(" tracking logic. Which version of taint tracking logic is used for a given gate is\n"); + log(" determined by a MUX selected by an $anyconst cell. By default, unless the\n"); + log(" `-no-cost-model` option is provided, an additional wire named `__glift_weight` with\n"); + log(" the `keep` and `minimize` attributes is added to the module along with pmuxes and\n"); + log(" adders to calculate a rough estimate of the number of logic gates in the GLIFT model\n"); + log(" given an assignment for the $anyconst cells. The four versions of taint tracking logic\n"); + log(" for an AND gate are:"); + log("\n"); + log(" y_t = a & b_t | b & a_t | a_t & b_t (like `-create-precise-model`)\n"); + log(" y_t = a_t | a & b_t\n"); + log(" y_t = b_t | b & a_t\n"); + log(" y_t = a_t | b_t (like `-create-imprecise-model`)\n"); + log("\n"); + log("\n"); + log("Options:\n"); + log("\n"); + log(" -taint-constants\n"); + log(" Constant values in the design are labeled as tainted.\n"); + log(" (default: label constants as un-tainted)\n"); + log("\n"); + log(" -keep-outputs\n"); + log(" Do not remove module outputs. Taint tracking outputs will appear in the module ports\n"); + log(" alongside the orignal outputs.\n"); + log(" (default: original module outputs are removed)\n"); + log("\n"); + log(" -simple-cost-model\n"); + log(" Do not model logic area. Instead model the number of non-zero assignments to $anyconsts.\n"); + log(" Taint tracking logic versions vary in their size, but all reduced-precision versions are\n"); + log(" significantly smaller than the fully-precise version. A non-zero $anyconst assignment means\n"); + log(" that reduced-precision taint tracking logic was chosen for some gate.\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: use a complex model and give that wire the \"keep\" and \"minimize\" attributes)\n"); + log("\n"); + log(" -no-cost-model\n"); + log(" Do not model taint tracking logic area and do not create a `__glift_weight` wire.\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: model area and give that wire the \"keep\" and \"minimize\" attributes)\n"); + log("\n"); + log(" -instrument-more\n"); + log(" Allow choice from more versions of (even simpler) taint tracking logic. A total\n"); + log(" of 8 versions of taint tracking logic will be added per gate, including the 4\n"); + log(" versions from `-create-instrumented-model` and these additional versions:\n"); + log("\n"); + log(" y_t = a_t\n"); + log(" y_t = b_t\n"); + log(" y_t = 1\n"); + log(" y_t = 0\n"); + log("\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: do not add more versions of taint tracking logic.\n"); + log("\n"); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false; + bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false; + bool opt_instrumentmore = false; + log_header(design, "Executing GLIFT pass (creating and manipulating GLIFT models).\n"); + std::vector<std::string>::size_type argidx; + + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-create-precise-model") { + opt_create_precise_model = true; + continue; + } + if (args[argidx] == "-create-imprecise-model") { + opt_create_imprecise_model = true; + continue; + } + if (args[argidx] == "-create-instrumented-model") { + opt_create_instrumented_model = true; + continue; + } + if (args[argidx] == "-taint-constants") { + opt_taintconstants = true; + continue; + } + if (args[argidx] == "-keep-outputs") { + opt_keepoutputs = true; + continue; + } + if (args[argidx] == "-simple-cost-model") { + opt_simplecostmodel = true; + continue; + } + if (args[argidx] == "-no-cost-model") { + opt_nocostmodel = true; + continue; + } + if (args[argidx] == "-instrument-more") { + opt_instrumentmore = true; + continue; + } + break; + } + if(!opt_create_precise_model && !opt_create_imprecise_model && !opt_create_instrumented_model) + log_cmd_error("No command provided. See help for usage.\n"); + if(static_cast<int>(opt_create_precise_model) + static_cast<int>(opt_create_imprecise_model) + static_cast<int>(opt_create_instrumented_model) != 1) + log_cmd_error("Only one command may be specified. See help for usage.\n"); + if(opt_simplecostmodel && opt_nocostmodel) + log_cmd_error("Only one of `-simple-cost-model` and `-no-cost-model` may be specified. See help for usage.\n"); + if((opt_simplecostmodel || opt_nocostmodel) && !opt_create_instrumented_model) + log_cmd_error("Options `-simple-cost-model` and `-no-cost-model` may only be used with `-create-instrumented-model`. See help for usage.\n"); + extra_args(args, argidx, design); + + if (GetSize(design->selected_modules()) == 0) + log_cmd_error("Can't operate on an empty selection!\n"); + + TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules; //cribbed from passes/techmap/flatten.cc + auto worklist = design->selected_modules(); + pool<RTLIL::IdString> non_top_modules; + while (!worklist.empty()) { + RTLIL::Module *module = *(worklist.begin()); + worklist.erase(worklist.begin()); + topo_modules.node(module); + + for (auto cell : module->selected_cells()) { + RTLIL::Module *tpl = design->module(cell->type); + if (tpl != nullptr) { + if (topo_modules.database.count(tpl) == 0) + worklist.push_back(tpl); + topo_modules.edge(tpl, module); + non_top_modules.insert(cell->type); + } + } + } + + if (!topo_modules.sort()) + log_cmd_error("Cannot handle recursive module instantiations.\n"); + + for (auto i = 0; i < GetSize(topo_modules.sorted); ++i) { + RTLIL::Module *module = topo_modules.sorted[i]; + GliftWorker(module, !non_top_modules[module->name], opt_create_precise_model, opt_create_imprecise_model, opt_create_instrumented_model, opt_taintconstants, opt_keepoutputs, opt_simplecostmodel, opt_nocostmodel, opt_instrumentmore); + } + } +} GliftPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index bb7b78cfe..d609c8d0f 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -944,12 +944,14 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp } } -static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel) +static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel, bool whole_modules = false) { std::string desc = "Selection contains:\n"; for (auto mod : design->modules()) { if (sel->selected_module(mod->name)) { + if (whole_modules && sel->selected_whole_module(mod->name)) + desc += stringf("%s\n", id2cstr(mod->name)); for (auto wire : mod->wires()) if (sel->selected_member(mod->name, wire->name)) desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(wire->name)); @@ -1051,17 +1053,17 @@ struct SelectPass : public Pass { log("\n"); log(" -unset <name>\n"); log(" do not modify the current selection. instead remove a previously saved\n"); - log(" selection under the given name (see @<name> below)."); + log(" selection under the given name (see @<name> below).\n"); log("\n"); log(" -assert-none\n"); log(" do not modify the current selection. instead assert that the given\n"); - log(" selection is empty. i.e. produce an error if any object matching the\n"); - log(" selection is found.\n"); + log(" selection is empty. i.e. produce an error if any object or module\n"); + log(" matching the selection is found.\n"); log("\n"); log(" -assert-any\n"); log(" do not modify the current selection. instead assert that the given\n"); - log(" selection is non-empty. i.e. produce an error if no object matching\n"); - log(" the selection is found.\n"); + log(" selection is non-empty. i.e. produce an error if no object or module\n"); + log(" matching the selection is found.\n"); log("\n"); log(" -assert-count N\n"); log(" do not modify the current selection. instead assert that the given\n"); @@ -1488,7 +1490,7 @@ struct SelectPass : public Pass { { RTLIL::Selection *sel = &work_stack.back(); sel->optimize(design); - std::string desc = describe_selection_for_assert(design, sel); + std::string desc = describe_selection_for_assert(design, sel, true); log_error("Assertion failed: selection is not empty:%s\n%s", sel_str.c_str(), desc.c_str()); } return; @@ -1503,7 +1505,7 @@ struct SelectPass : public Pass { { RTLIL::Selection *sel = &work_stack.back(); sel->optimize(design); - std::string desc = describe_selection_for_assert(design, sel); + std::string desc = describe_selection_for_assert(design, sel, true); log_error("Assertion failed: selection is empty:%s\n%s", sel_str.c_str(), desc.c_str()); } return; diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 750fe0e10..35aa410e4 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -52,7 +52,7 @@ struct ShowWorker std::map<RTLIL::IdString, int> autonames; int single_idx_count; - struct net_conn { std::set<std::string> in, out; int bits; std::string color; }; + struct net_conn { std::set<std::pair<std::string, int>> in, out; std::string color; }; std::map<std::string, net_conn> net_conn_map; FILE *f; @@ -239,6 +239,19 @@ struct ShowWorker int idx = single_idx_count++; for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) { const RTLIL::SigChunk &c = sig.chunks().at(i); + int cl, cr; + if (c.wire) { + if (c.wire->upto) { + cr = c.wire->start_offset + (c.wire->width - c.offset - 1); + cl = cr - (c.width - 1); + } else { + cr = c.wire->start_offset + c.offset; + cl = cr + c.width - 1; + } + } else { + cl = c.offset + c.width - 1; + cr = c.offset; + } if (!driver && c.wire == nullptr) { RTLIL::State s1 = c.data.front(); for (auto s2 : c.data) @@ -254,9 +267,8 @@ struct ShowWorker std::string repinfo = rep > 1 ? stringf("%dx ", rep) : ""; if (driver) { log_assert(!net.empty()); - label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset); - net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); - net_conn_map[net].bits = rep*c.width; + label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), cl, cr); + net_conn_map[net].in.insert({stringf("x%d:s%d", idx, i), rep*c.width}); net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } else if (net.empty()) { @@ -268,9 +280,8 @@ struct ShowWorker c.data.front() == State::Sz ? 'Z' : '?', pos, pos-rep*c.width+1); } else { - label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1); - net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); - net_conn_map[net].bits = rep*c.width; + label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), cl, cr, pos, pos-rep*c.width+1); + net_conn_map[net].out.insert({stringf("x%d:s%d", idx, i), rep*c.width}); net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } pos -= rep * c.width; @@ -280,6 +291,7 @@ struct ShowWorker code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str()); if (!port.empty()) { currentColor = xorshift32(currentColor); + log_warning("WIDTHLABEL %s %d\n", log_signal(sig), GetSize(sig)); if (driver) code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); else @@ -292,10 +304,9 @@ struct ShowWorker { if (!port.empty()) { if (driver) - net_conn_map[net].in.insert(port); + net_conn_map[net].in.insert({port, GetSize(sig)}); else - net_conn_map[net].out.insert(port); - net_conn_map[net].bits = sig.size(); + net_conn_map[net].out.insert({port, GetSize(sig)}); net_conn_map[net].color = nextColor(sig, net_conn_map[net].color); } if (node != nullptr) @@ -465,8 +476,7 @@ struct ShowWorker std::string code, node; code += gen_portbox("", sig, false, &node); fprintf(f, "%s", code.c_str()); - net_conn_map[node].out.insert(stringf("p%d", pidx)); - net_conn_map[node].bits = sig.size(); + net_conn_map[node].out.insert({stringf("p%d", pidx), GetSize(sig)}); net_conn_map[node].color = nextColor(sig, net_conn_map[node].color); } @@ -474,8 +484,7 @@ struct ShowWorker std::string code, node; code += gen_portbox("", sig, true, &node); fprintf(f, "%s", code.c_str()); - net_conn_map[node].in.insert(stringf("p%d", pidx)); - net_conn_map[node].bits = sig.size(); + net_conn_map[node].in.insert({stringf("p%d", pidx), GetSize(sig)}); net_conn_map[node].color = nextColor(sig, net_conn_map[node].color); } @@ -509,17 +518,15 @@ struct ShowWorker currentColor = xorshift32(currentColor); fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor(conn).c_str(), widthLabel(conn.first.size()).c_str()); } else { - net_conn_map[right_node].bits = conn.first.size(); net_conn_map[right_node].color = nextColor(conn, net_conn_map[right_node].color); - net_conn_map[left_node].bits = conn.first.size(); net_conn_map[left_node].color = nextColor(conn, net_conn_map[left_node].color); if (left_node[0] == 'x') { - net_conn_map[right_node].in.insert(left_node); + net_conn_map[right_node].in.insert({left_node, GetSize(conn.first)}); } else if (right_node[0] == 'x') { - net_conn_map[left_node].out.insert(right_node); + 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)); - net_conn_map[left_node].out.insert(stringf("x%d:w", single_idx_count)); + 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++); } } @@ -529,12 +536,13 @@ struct ShowWorker { currentColor = xorshift32(currentColor); if (wires_on_demand.count(it.first) > 0) { - if (it.second.in.size() == 1 && it.second.out.size() > 1 && it.second.in.begin()->compare(0, 1, "p") == 0) + if (it.second.in.size() == 1 && it.second.out.size() > 1 && it.second.in.begin()->first.compare(0, 1, "p") == 0) it.second.out.erase(*it.second.in.begin()); if (it.second.in.size() == 1 && it.second.out.size() == 1) { - std::string from = *it.second.in.begin(), to = *it.second.out.begin(); + std::string from = it.second.in.begin()->first, to = it.second.out.begin()->first; + int bits = it.second.in.begin()->second; if (from != to || from.compare(0, 1, "p") != 0) - fprintf(f, "%s:e -> %s:w [%s, %s];\n", from.c_str(), to.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", from.c_str(), to.c_str(), nextColor(it.second.color).c_str(), widthLabel(bits).c_str()); continue; } if (it.second.in.size() == 0 || it.second.out.size() == 0) @@ -543,9 +551,9 @@ struct ShowWorker fprintf(f, "%s [ shape=point ];\n", it.first.c_str()); } for (auto &it2 : it.second.in) - fprintf(f, "%s:e -> %s:w [%s, %s];\n", it2.c_str(), it.first.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", it2.first.c_str(), it.first.c_str(), nextColor(it.second.color).c_str(), widthLabel(it2.second).c_str()); for (auto &it2 : it.second.out) - fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.first.c_str(), nextColor(it.second.color).c_str(), widthLabel(it2.second).c_str()); } fprintf(f, "}\n"); @@ -653,7 +661,7 @@ struct ShowPass : public Pass { log(" (including inout ports) are on the right side.\n"); log("\n"); log(" -pause\n"); - log(" wait for the use to press enter to before returning\n"); + log(" wait for the user to press enter to before returning\n"); log("\n"); log(" -enum\n"); log(" enumerate objects with internal ($-prefixed) names\n"); diff --git a/passes/cmds/sta.cc b/passes/cmds/sta.cc new file mode 100644 index 000000000..13e1ee13c --- /dev/null +++ b/passes/cmds/sta.cc @@ -0,0 +1,312 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * (C) 2019 Eddie Hung <eddie@fpgeh.com> + * + * 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/timinginfo.h" +#include <deque> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct StaWorker +{ + Design *design; + Module *module; + SigMap sigmap; + + struct t_data { + Cell* driver; + IdString dst_port, src_port; + vector<tuple<SigBit,int,IdString>> fanouts; + SigBit backtrack; + t_data() : driver(nullptr) {} + }; + dict<SigBit, t_data> data; + std::deque<SigBit> queue; + struct t_endpoint { + Cell *sink; + IdString port; + int required; + t_endpoint() : sink(nullptr), required(0) {} + }; + dict<SigBit, t_endpoint> endpoints; + + int maxarrival; + SigBit maxbit; + + pool<SigBit> driven; + + StaWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), maxarrival(0) + { + TimingInfo timing; + + for (auto cell : module->cells()) + { + Module *inst_module = design->module(cell->type); + if (!inst_module) { + log_warning("Cell type '%s' not recognised! Ignoring.\n", log_id(cell->type)); + continue; + } + + if (!inst_module->get_blackbox_attribute()) { + log_warning("Cell type '%s' is not a black- nor white-box! Ignoring.\n", log_id(cell->type)); + continue; + } + + IdString derived_type = inst_module->derive(design, cell->parameters); + inst_module = design->module(derived_type); + log_assert(inst_module); + + if (!timing.count(derived_type)) { + auto &t = timing.setup_module(inst_module); + if (t.has_inputs && t.comb.empty() && t.arrival.empty() && t.required.empty()) + log_warning("Module '%s' has no timing arcs!\n", log_id(cell->type)); + } + + auto &t = timing.at(derived_type); + if (t.comb.empty() && t.arrival.empty() && t.required.empty()) + continue; + + pool<std::pair<SigBit,TimingInfo::NameBit>> src_bits, dst_bits; + + for (auto &conn : cell->connections()) { + auto rhs = sigmap(conn.second); + for (auto i = 0; i < GetSize(rhs); i++) { + const auto &bit = rhs[i]; + if (!bit.wire) + continue; + TimingInfo::NameBit namebit(conn.first,i); + if (cell->input(conn.first)) { + src_bits.insert(std::make_pair(bit,namebit)); + + auto it = t.required.find(namebit); + if (it == t.required.end()) + continue; + auto r = endpoints.insert(bit); + if (r.second || r.first->second.required < it->second.first) { + r.first->second.sink = cell; + r.first->second.port = conn.first; + r.first->second.required = it->second.first; + } + } + if (cell->output(conn.first)) { + dst_bits.insert(std::make_pair(bit,namebit)); + auto &d = data[bit]; + d.driver = cell; + d.dst_port = conn.first; + driven.insert(bit); + + auto it = t.arrival.find(namebit); + if (it == t.arrival.end()) + continue; + const auto &s = it->second.second; + if (cell->hasPort(s.name)) { + auto s_bit = sigmap(cell->getPort(s.name)[s.offset]); + if (s_bit.wire) + data[s_bit].fanouts.emplace_back(bit,it->second.first,s.name); + } + } + } + } + + for (const auto &s : src_bits) + for (const auto &d : dst_bits) { + auto it = t.comb.find(TimingInfo::BitBit(s.second,d.second)); + if (it == t.comb.end()) + continue; + data[s.first].fanouts.emplace_back(d.first,it->second,s.second.name); + } + } + + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (wire->port_input) { + for (const auto &b : sigmap(wire)) { + queue.emplace_back(b); + driven.insert(b); + } + // All primary inputs to arrive at time zero + wire->set_intvec_attribute(ID::sta_arrival, std::vector<int>(GetSize(wire), 0)); + } + if (wire->port_output) + for (const auto &b : sigmap(wire)) + if (b.wire) + endpoints.insert(b); + } + } + + void run() + { + while (!queue.empty()) { + auto b = queue.front(); + queue.pop_front(); + auto it = data.find(b); + if (it == data.end()) + continue; + const auto& src_arrivals = b.wire->get_intvec_attribute(ID::sta_arrival); + log_assert(GetSize(src_arrivals) == GetSize(b.wire)); + auto src_arrival = src_arrivals[b.offset]; + for (const auto &d : it->second.fanouts) { + const auto &dst_bit = std::get<0>(d); + auto dst_arrivals = dst_bit.wire->get_intvec_attribute(ID::sta_arrival); + if (dst_arrivals.empty()) + dst_arrivals = std::vector<int>(GetSize(dst_bit.wire), -1); + else + log_assert(GetSize(dst_arrivals) == GetSize(dst_bit.wire)); + auto &dst_arrival = dst_arrivals[dst_bit.offset]; + auto new_arrival = src_arrival + std::get<1>(d); + if (dst_arrival < new_arrival) { + auto dst_wire = dst_bit.wire; + dst_arrival = std::max(dst_arrival, new_arrival); + dst_wire->set_intvec_attribute(ID::sta_arrival, dst_arrivals); + queue.emplace_back(dst_bit); + + data[dst_bit].backtrack = b; + data[dst_bit].src_port = std::get<2>(d); + + auto it = endpoints.find(dst_bit); + if (it != endpoints.end()) + new_arrival += it->second.required; + if (new_arrival > maxarrival && driven.count(b)) { + maxarrival = new_arrival; + maxbit = dst_bit; + } + } + } + } + + auto b = maxbit; + if (b == SigBit()) { + log("No timing paths found.\n"); + return; + } + + log("Latest arrival time in '%s' is %d:\n", log_id(module), maxarrival); + auto it = endpoints.find(maxbit); + if (it != endpoints.end() && it->second.sink) + log(" %6d %s (%s.%s)\n", maxarrival, log_id(it->second.sink), log_id(it->second.sink->type), log_id(it->second.port)); + else { + log(" %6d (%s)\n", maxarrival, b.wire->port_output ? "<primary output>" : "<unknown>"); + if (!b.wire->port_output) + log_warning("Critical-path does not terminate in a recognised endpoint.\n"); + } + auto jt = data.find(b); + while (jt != data.end()) { + int arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset]; + if (jt->second.driver) { + log(" %s\n", log_signal(b)); + log(" %6d %s (%s.%s->%s)\n", arrival, log_id(jt->second.driver), log_id(jt->second.driver->type), log_id(jt->second.src_port), log_id(jt->second.dst_port)); + } + else if (b.wire->port_input) + log(" %6d %s (%s)\n", arrival, log_signal(b), "<primary input>"); + else + log_abort(); + b = jt->second.backtrack; + jt = data.find(b); + } + + std::map<int, unsigned> arrival_histogram; + for (const auto &i : endpoints) { + const auto &b = i.first; + if (!driven.count(b)) + continue; + + if (!b.wire->attributes.count(ID::sta_arrival)) { + log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b)); + continue; + } + + auto arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset]; + if (arrival < 0) { + log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b)); + continue; + } + arrival += i.second.required; + arrival_histogram[arrival]++; + } + // Adapted from https://github.com/YosysHQ/nextpnr/blob/affb12cc27ebf409eade062c4c59bb98569d8147/common/timing.cc#L946-L969 + if (arrival_histogram.size() > 0) { + unsigned num_bins = 20; + unsigned bar_width = 60; + auto min_arrival = arrival_histogram.begin()->first; + auto max_arrival = arrival_histogram.rbegin()->first; + auto bin_size = std::max<unsigned>(1, ceil((max_arrival - min_arrival + 1) / float(num_bins))); + std::vector<unsigned> bins(num_bins); + unsigned max_freq = 0; + for (const auto &i : arrival_histogram) { + auto &bin = bins[(i.first - min_arrival) / bin_size]; + bin += i.second; + max_freq = std::max(max_freq, bin); + } + bar_width = std::min(bar_width, max_freq); + + log("\n"); + log("Arrival histogram:\n"); + log(" legend: * represents %d endpoint(s)\n", max_freq / bar_width); + log(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width); + for (int i = num_bins-1; i >= 0; --i) + log("(%6d, %6d] |%s%c\n", min_arrival + bin_size * (i + 1), min_arrival + bin_size * i, + std::string(bins[i] * bar_width / max_freq, '*').c_str(), + (bins[i] * bar_width) % max_freq > 0 ? '+' : ' '); + } + } +}; + +struct StaPass : public Pass { + StaPass() : Pass("sta", "perform static timing analysis") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sta [options] [selection]\n"); + log("\n"); + log("This command performs static timing analysis on the design. (Only considers\n"); + log("paths within a single module, so the design must be flattened.)\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing STA pass (static timing analysis).\n"); + + /* + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-TODO") { + continue; + } + break; + } + */ + + extra_args(args, 1, design); + + for (Module *module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + StaWorker worker(module); + worker.run(); + } + } +} StaPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 422810526..fffdda48e 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -117,10 +117,14 @@ struct statdata_t } else if (cell_type.in(ID($mux), ID($pmux))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y))); + else if (cell_type == ID($bmux)) + cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S))); + else if (cell_type == ID($demux)) + cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S))); else if (cell_type.in( ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), - ID($dlatch), ID($adlatch), ID($dlatchsr))) + ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Q))); } |