From: Marcelina Kościelnicka Date: Sat, 11 Dec 2021 15:07:29 +0000 (+0100) Subject: Add clean_zerowidth pass, use it for Verilog output. X-Git-Tag: yosys-0.13~32 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=0aad88a2fb23e5481538122e1bd4c0fac9ba5e90;p=yosys.git Add clean_zerowidth pass, use it for Verilog output. This should remove instances of zero-width sigspecs in the netlist, avoiding problems in the Verilog backend with emitting them. See #3103. --- diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 13c78c526..e4781ef3e 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2300,6 +2300,8 @@ struct VerilogBackend : public Backend { extmem_prefix = filename.substr(0, filename.rfind('.')); } + Pass::call(design, "clean_zerowidth"); + design->sort(); *f << stringf("/* Generated by %s */\n", yosys_version_str); diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 01e052fdf..917856767 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -40,4 +40,5 @@ endif OBJS += passes/cmds/scratchpad.o OBJS += passes/cmds/logger.o OBJS += passes/cmds/printattrs.o -OBJS += passes/cmds/sta.o \ No newline at end of file +OBJS += passes/cmds/sta.o +OBJS += passes/cmds/clean_zerowidth.o diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc new file mode 100644 index 000000000..4e7c68093 --- /dev/null +++ b/passes/cmds/clean_zerowidth.cc @@ -0,0 +1,210 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * 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 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 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 == ID($pmux)) { + // 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 swizzle; + std::vector 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 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 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