3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 17:15:33 +00:00

Merge branch 'master' into clk2ff-better-names

This commit is contained in:
Claire Xen 2022-02-11 16:03:12 +01:00 committed by GitHub
commit 49545c73f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
757 changed files with 49469 additions and 8717 deletions

View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -22,25 +22,20 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
int autoname_worker(Module *module)
int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
{
dict<Cell*, pair<int, IdString>> proposed_cell_names;
dict<Wire*, pair<int, IdString>> proposed_wire_names;
dict<Wire*, int> wire_score;
int best_score = -1;
for (auto cell : module->selected_cells())
for (auto &conn : cell->connections())
for (auto bit : conn.second)
if (bit.wire != nullptr)
wire_score[bit.wire]++;
for (auto cell : module->selected_cells()) {
if (cell->name[0] == '$') {
for (auto &conn : cell->connections()) {
string suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
string suffix;
for (auto bit : conn.second)
if (bit.wire != nullptr && bit.wire->name[0] != '$') {
if (suffix.empty())
suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
IdString new_name(bit.wire->name.str() + suffix);
int score = wire_score.at(bit.wire);
if (cell->output(conn.first)) score = 0;
@ -54,9 +49,11 @@ int autoname_worker(Module *module)
}
} else {
for (auto &conn : cell->connections()) {
string suffix = stringf("_%s", log_id(conn.first));
string suffix;
for (auto bit : conn.second)
if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
if (suffix.empty())
suffix = stringf("_%s", log_id(conn.first));
IdString new_name(cell->name.str() + suffix);
int score = wire_score.at(bit.wire);
if (cell->output(conn.first)) score = 0;
@ -118,10 +115,17 @@ struct AutonamePass : public Pass {
for (auto module : design->selected_modules())
{
dict<Wire*, int> wire_score;
for (auto cell : module->selected_cells())
for (auto &conn : cell->connections())
for (auto bit : conn.second)
if (bit.wire != nullptr)
wire_score[bit.wire]++;
int count = 0, iter = 0;
while (1) {
iter++;
int n = autoname_worker(module);
int n = autoname_worker(module, wire_score);
if (!n) break;
count += n;
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -46,10 +46,11 @@ struct BlackboxPass : public Pass {
}
extra_args(args, argidx, design);
for (auto module : design->selected_whole_modules_warn())
for (auto module : design->selected_whole_modules_warn(true))
{
module->makeblackbox();
module->set_bool_attribute(ID::blackbox);
module->set_bool_attribute(ID::whitebox, false);
}
}
} BlackboxPass;

View file

@ -38,6 +38,8 @@ struct BugpointPass : public Pass {
log("and the same script, repeating these steps while it can find a smaller design that\n");
log("still causes a crash. Once this command finishes, it replaces the current design\n");
log("with the smallest testcase it was able to produce.\n");
log("In order to save the reduced testcase you must write this out to a file with\n");
log("another command after `bugpoint` like `write_rtlil` or `write_verilog`.\n");
log("\n");
log(" -script <filename> | -command \"<command>\"\n");
log(" use this script file or command to crash Yosys. required.\n");
@ -87,9 +89,12 @@ struct BugpointPass : public Pass {
log(" -updates\n");
log(" try to remove process updates from syncs.\n");
log("\n");
log(" -runner \"<prefix>\"\n");
log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n");
log("\n");
}
bool run_yosys(RTLIL::Design *design, string yosys_cmd, string yosys_arg)
bool run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg)
{
design->sort();
@ -97,7 +102,7 @@ struct BugpointPass : public Pass {
RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
f.close();
string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log %s bugpoint-case.il", yosys_cmd.c_str(), yosys_arg.c_str());
string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str());
return run_command(yosys_cmdline) == 0;
}
@ -270,7 +275,7 @@ struct BugpointPass : public Pass {
if (mod->get_blackbox_attribute())
continue;
RTLIL::IdString removed_process;
RTLIL::Process *removed_process = nullptr;
for (auto process : mod->processes)
{
if (process.second->get_bool_attribute(ID::bugpoint_keep))
@ -279,13 +284,12 @@ struct BugpointPass : public Pass {
if (index++ == seed)
{
log_header(design, "Trying to remove process %s.%s.\n", log_id(mod), log_id(process.first));
removed_process = process.first;
removed_process = process.second;
break;
}
}
if (!removed_process.empty()) {
delete mod->processes[removed_process];
mod->processes.erase(removed_process);
if (removed_process) {
mod->remove(removed_process);
return design_copy;
}
}
@ -339,6 +343,23 @@ struct BugpointPass : public Pass {
return design_copy;
}
}
int i = 0;
for (auto it = sy->mem_write_actions.begin(); it != sy->mem_write_actions.end(); ++it, ++i)
{
if (index++ == seed)
{
log_header(design, "Trying to remove sync %s memwr %s %s %s %s in %s.%s.\n", log_signal(sy->signal), log_id(it->memid), log_signal(it->address), log_signal(it->data), log_signal(it->enable), log_id(mod), log_id(pr.first));
sy->mem_write_actions.erase(it);
// Remove the bit for removed action from other actions' priority masks.
for (auto it2 = sy->mem_write_actions.begin(); it2 != sy->mem_write_actions.end(); ++it2) {
auto &mask = it2->priority_mask;
if (GetSize(mask) > i) {
mask.bits.erase(mask.bits.begin() + i);
}
}
return design_copy;
}
}
}
}
}
@ -356,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)
@ -377,7 +398,7 @@ struct BugpointPass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
string yosys_cmd = "yosys", yosys_arg, grep;
string yosys_cmd = "yosys", yosys_arg, grep, runner;
bool fast = false, clean = false;
bool modules = false, ports = false, cells = false, connections = false, processes = false, assigns = false, updates = false, wires = false, has_part = false;
@ -455,6 +476,14 @@ struct BugpointPass : public Pass {
has_part = true;
continue;
}
if (args[argidx] == "-runner" && argidx + 1 < args.size()) {
runner = args[++argidx];
if (runner.size() && runner.at(0) == '"') {
log_assert(runner.back() == '"');
runner = runner.substr(1, runner.size() - 2);
}
continue;
}
break;
}
extra_args(args, argidx, design);
@ -478,7 +507,7 @@ struct BugpointPass : public Pass {
log_cmd_error("This command only operates on fully selected designs!\n");
RTLIL::Design *crashing_design = clean_design(design, clean);
if (run_yosys(crashing_design, yosys_cmd, yosys_arg))
if (run_yosys(crashing_design, runner, yosys_cmd, yosys_arg))
log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n");
if (!check_logfile(grep))
log_cmd_error("The provided grep string is not found in the log file!\n");
@ -495,12 +524,12 @@ struct BugpointPass : public Pass {
if (clean)
{
RTLIL::Design *testcase = clean_design(simplified);
crashes = !run_yosys(testcase, yosys_cmd, yosys_arg);
crashes = !run_yosys(testcase, runner, yosys_cmd, yosys_arg);
delete testcase;
}
else
{
crashes = !run_yosys(simplified, yosys_cmd, yosys_arg);
crashes = !run_yosys(simplified, runner, yosys_cmd, yosys_arg);
}
if (crashes && check_logfile(grep))

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -141,6 +141,14 @@ struct CheckPass : public Pass {
for (auto bit : sigmap(action.second))
if (bit.wire) used_wires.insert(bit);
}
for (auto memwr : sync->mem_write_actions) {
for (auto bit : sigmap(memwr.address))
if (bit.wire) used_wires.insert(bit);
for (auto bit : sigmap(memwr.data))
if (bit.wire) used_wires.insert(bit);
for (auto bit : sigmap(memwr.enable))
if (bit.wire) used_wires.insert(bit);
}
}
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -60,7 +60,7 @@ struct ConnectPass : public Pass {
log("Unconnect all existing drivers for the specified expression.\n");
log("\n");
log("\n");
log(" connect [-nomap] -port <cell> <port> <expr>\n");
log(" connect [-nomap] [-assert] -port <cell> <port> <expr>\n");
log("\n");
log("Connect the specified cell port to the specified cell port.\n");
log("\n");
@ -72,6 +72,9 @@ struct ConnectPass : public Pass {
log("The connect command operates in one module only. Either only one module must\n");
log("be selected or an active module must be set using the 'cd' command.\n");
log("\n");
log("The -assert option verifies that the connection already exists, instead of\n");
log("making it.\n");
log("\n");
log("This command does not operate on module with processes.\n");
log("\n");
}
@ -88,7 +91,7 @@ struct ConnectPass : public Pass {
if (!module->processes.empty())
log_cmd_error("Found processes in selected module.\n");
bool flag_nounset = false, flag_nomap = false;
bool flag_nounset = false, flag_nomap = false, flag_assert = false;
std::string set_lhs, set_rhs, unset_expr;
std::string port_cell, port_port, port_expr;
@ -104,6 +107,10 @@ struct ConnectPass : public Pass {
flag_nomap = true;
continue;
}
if (arg == "-assert") {
flag_assert = true;
continue;
}
if (arg == "-set" && argidx+2 < args.size()) {
set_lhs = args[++argidx];
set_rhs = args[++argidx];
@ -126,7 +133,7 @@ struct ConnectPass : public Pass {
if (!flag_nomap)
for (auto &it : module->connections()) {
std::vector<RTLIL::SigBit> lhs = it.first.to_sigbit_vector();
std::vector<RTLIL::SigBit> rhs = it.first.to_sigbit_vector();
std::vector<RTLIL::SigBit> rhs = it.second.to_sigbit_vector();
for (size_t i = 0; i < lhs.size(); i++)
if (rhs[i].wire != nullptr)
sigmap.add(lhs[i], rhs[i]);
@ -137,6 +144,9 @@ struct ConnectPass : public Pass {
if (!unset_expr.empty() || !port_cell.empty())
log_cmd_error("Can't use -set together with -unset and/or -port.\n");
if (flag_assert)
log_cmd_error("The -assert option is only supported with -port.\n");
RTLIL::SigSpec sig_lhs, sig_rhs;
if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs))
log_cmd_error("Failed to parse set lhs expression `%s'.\n", set_lhs.c_str());
@ -157,6 +167,9 @@ struct ConnectPass : public Pass {
if (!port_cell.empty() || flag_nounset)
log_cmd_error("Can't use -unset together with -port and/or -nounset.\n");
if (flag_assert)
log_cmd_error("The -assert option is only supported with -port.\n");
RTLIL::SigSpec sig;
if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr))
log_cmd_error("Failed to parse unset expression `%s'.\n", unset_expr.c_str());
@ -177,7 +190,14 @@ struct ConnectPass : public Pass {
if (!RTLIL::SigSpec::parse_sel(sig, design, module, port_expr))
log_cmd_error("Failed to parse port expression `%s'.\n", port_expr.c_str());
module->cell(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig));
if (!flag_assert) {
module->cell(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig));
} else {
SigSpec cur = module->cell(RTLIL::escape_id(port_cell))->getPort(RTLIL::escape_id(port_port));
if (sigmap(sig) != sigmap(cur)) {
log_cmd_error("Expected connection not present: expected %s, found %s.\n", log_signal(sig), log_signal(cur));
}
}
}
else
log_cmd_error("Expected -set, -unset, or -port.\n");

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -90,7 +90,7 @@ struct DeletePass : public Pass {
pool<RTLIL::Wire*> delete_wires;
pool<RTLIL::Cell*> delete_cells;
pool<RTLIL::IdString> delete_procs;
pool<RTLIL::Process*> delete_procs;
pool<RTLIL::IdString> delete_mems;
for (auto wire : module->selected_wires())
@ -103,14 +103,14 @@ struct DeletePass : public Pass {
for (auto cell : module->cells()) {
if (design->selected(module, cell))
delete_cells.insert(cell);
if (cell->type.in(ID($memrd), ID($memwr)) &&
if (cell->has_memid() &&
delete_mems.count(cell->parameters.at(ID::MEMID).decode_string()) != 0)
delete_cells.insert(cell);
}
for (auto &it : module->processes)
if (design->selected(module, it.second))
delete_procs.insert(it.first);
delete_procs.insert(it.second);
for (auto &it : delete_mems) {
delete module->memories.at(it);
@ -120,10 +120,8 @@ struct DeletePass : public Pass {
for (auto &it : delete_cells)
module->remove(it);
for (auto &it : delete_procs) {
delete module->processes.at(it);
module->processes.erase(it);
}
for (auto &it : delete_procs)
module->remove(it);
module->remove(delete_wires);

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 - 2020 Claire Wolf <claire@symbioticeda.com>
* Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.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

599
passes/cmds/glift.cc Normal file
View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2020 Miodrag Milanovic <clifford@clifford.at>
* Copyright (C) 2020 Miodrag Milanovic <micko@yosyshq.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
@ -64,6 +64,11 @@ struct LoggerPass : public Pass {
log(" -expect-no-warnings\n");
log(" gives error in case there is at least one warning that is not expected.\n");
log("\n");
log(" -check-expected\n");
log(" verifies that the patterns previously set up by -expect have actually\n");
log(" been met, then clears the expected log list. If this is not called\n");
log(" manually, the check will happen at yosys exist time instead.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design * design) override
@ -176,6 +181,10 @@ struct LoggerPass : public Pass {
log_expect_no_warnings = true;
continue;
}
if (args[argidx] == "-check-expected") {
log_check_expected();
continue;
}
break;
}
extra_args(args, argidx, design, false);

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,8 +1,8 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* 2019 Nina Engelhardt <nak@symbioticeda.com>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 N. Engelhardt <nak@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -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,7 +267,7 @@ 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);
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));
net_conn_map[net].bits = rep*c.width;
net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
@ -268,7 +281,7 @@ 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);
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));
net_conn_map[net].bits = rep*c.width;
net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
@ -339,6 +352,11 @@ struct ShowWorker
{
input_signals.insert(obj->signal);
collect_proc_signals(obj->actions, input_signals, output_signals);
for (auto it : obj->mem_write_actions) {
input_signals.insert(it.address);
input_signals.insert(it.data);
input_signals.insert(it.enable);
}
}
void collect_proc_signals(RTLIL::Process *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
@ -648,7 +666,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");

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

312
passes/cmds/sta.cc Normal file
View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -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)));
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -83,7 +83,7 @@ struct TorderPass : public Pass {
if (!noautostop && yosys_celltypes.cell_known(cell->type)) {
if (conn.first.in(ID::Q, ID::CTRL_OUT, ID::RD_DATA))
continue;
if (cell->type == ID($memrd) && conn.first == ID::DATA)
if (cell->type.in(ID($memrd), ID($memrd_v2)) && conn.first == ID::DATA)
continue;
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -55,7 +55,10 @@ struct EquivInductWorker
for (auto cell : cells) {
if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) {
log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
if (RTLIL::builtin_ff_cell_types().count(cell->type))
log_warning("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
else
log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
cell_warn_cache.insert(cell);
}
if (cell->type == ID($equiv)) {

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -184,8 +184,12 @@ struct EquivSimpleWorker
for (auto cell : problem_cells) {
auto key = pair<Cell*, int>(cell, step+1);
if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1))
log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) {
if (RTLIL::builtin_ff_cell_types().count(cell->type))
log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
else
log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
}
imported_cells_cache.insert(key);
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.at>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@ -156,6 +156,289 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
return basicType;
}
// A helper struct for expanding a module's interface connections in expand_module
struct IFExpander
{
IFExpander (RTLIL::Design &design, RTLIL::Module &m)
: module(m), has_interfaces_not_found(false)
{
// Keep track of all derived interfaces available in the current
// module in 'interfaces_in_module':
for (auto cell : module.cells()) {
if(!cell->get_bool_attribute(ID::is_interface))
continue;
interfaces_in_module[cell->name] = design.module(cell->type);
}
}
RTLIL::Module &module;
dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
bool has_interfaces_not_found;
std::vector<RTLIL::IdString> connections_to_remove;
std::vector<RTLIL::IdString> connections_to_add_name;
std::vector<RTLIL::SigSpec> connections_to_add_signal;
dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
// Reset the per-cell state
void start_cell()
{
has_interfaces_not_found = false;
connections_to_remove.clear();
connections_to_add_name.clear();
connections_to_add_signal.clear();
interfaces_to_add_to_submodule.clear();
modports_used_in_submodule.clear();
}
// Set has_interfaces_not_found if there are pending interfaces that
// haven't been found yet (and might be found in the future). Print a
// warning if we've already gone over all the cells in the module.
void on_missing_interface(RTLIL::IdString interface_name)
{
// If there are cells that haven't yet been processed, maybe
// we'll find this interface in the future.
if (module.get_bool_attribute(ID::cells_not_processed)) {
has_interfaces_not_found = true;
return;
}
// Otherwise, we have already gone over all cells in this
// module and the interface has still not been found. Warn
// about it and don't set has_interfaces_not_found (to avoid a
// loop).
log_warning("Could not find interface instance for `%s' in `%s'\n",
log_id(interface_name), log_id(&module));
}
// Handle an interface connection from the module
void on_interface(RTLIL::Module &submodule,
RTLIL::IdString conn_name,
const RTLIL::SigSpec &conn_signals)
{
// Check if the connected wire is a potential interface in the parent module
std::string interface_name_str = conn_signals.bits()[0].wire->name.str();
// Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
interface_name_str.replace(0,23,"");
interface_name_str = "\\" + interface_name_str;
RTLIL::IdString interface_name = interface_name_str;
// If 'interfaces' in the cell have not be been handled yet, we aren't
// ready to derive the sub-module either
if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) {
on_missing_interface(interface_name);
return;
}
// Check if the interface instance is present in module. Interface
// instances may either have the plain name or the name appended with
// '_inst_from_top_dummy'. Check for both of them here
int nexactmatch = interfaces_in_module.count(interface_name) > 0;
std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
RTLIL::IdString interface_name2 = interface_name_str2;
int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
// If we can't find either name, this is a missing interface.
if (! (nexactmatch || nmatch2)) {
on_missing_interface(interface_name);
return;
}
if (nexactmatch != 0) // Choose the one with the plain name if it exists
interface_name2 = interface_name;
RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
// Go over all wires in interface, and add replacements to lists.
for (auto mod_wire : mod_replace_ports->wires()) {
std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name);
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
if(module.wire(signal_name2) == nullptr) {
log_error("Could not find signal '%s' in '%s'\n",
signal_name2.c_str(), log_id(module.name));
}
else {
RTLIL::Wire *wire_in_parent = module.wire(signal_name2);
connections_to_add_signal.push_back(wire_in_parent);
}
}
connections_to_remove.push_back(conn_name);
interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2);
// Find if the sub-module has set a modport for the current interface
// connection. Add any modports to a dict which will be passed to
// AstModule::derive
string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport);
if (!modport_name.empty()) {
modports_used_in_submodule[conn_name] = "\\" + modport_name;
}
}
// Handle a single connection from the module, making a note to expand
// it if it's an interface connection.
void on_connection(RTLIL::Module &submodule,
RTLIL::IdString conn_name,
const RTLIL::SigSpec &conn_signals)
{
// Check if the connection is present as an interface in the sub-module's port list
const RTLIL::Wire *wire = submodule.wire(conn_name);
if (!wire || !wire->get_bool_attribute(ID::is_interface))
return;
// If the connection looks like an interface, handle it.
const auto &bits = conn_signals.bits();
if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface))
on_interface(submodule, conn_name, conn_signals);
}
// Iterate over the connections in a cell, tracking any interface
// connections
void visit_connections(const RTLIL::Cell &cell,
RTLIL::Module &submodule)
{
for (const auto &conn : cell.connections()) {
on_connection(submodule, conn.first, conn.second);
}
}
// Add/remove connections to the cell as necessary, replacing any SV
// interface port connection with the individual signal connections.
void rewrite_interface_connections(RTLIL::Cell &cell) const
{
for(unsigned int i=0;i<connections_to_add_name.size();i++) {
cell.connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
}
// Remove the connection for the interface itself:
for(unsigned int i=0;i<connections_to_remove.size();i++) {
cell.connections_.erase(connections_to_remove[i]);
}
}
};
// Get a module needed by a cell, either by deriving an abstract module or by
// loading one from a directory in libdirs.
//
// If the module can't be found and check is true then exit with an error
// message. Otherwise, return a pointer to the module if we derived or loaded
// something. or null otherwise (the module should be blackbox or we couldn't
// find it and check is not set).
RTLIL::Module *get_module(RTLIL::Design &design,
RTLIL::Cell &cell,
RTLIL::Module &parent,
bool check,
const std::vector<std::string> &libdirs)
{
std::string cell_type = cell.type.str();
RTLIL::Module *abs_mod = design.module("$abstract" + cell_type);
if (abs_mod) {
cell.type = abs_mod->derive(&design, cell.parameters);
cell.parameters.clear();
RTLIL::Module *mod = design.module(cell.type);
log_assert(mod);
return mod;
}
// If the cell type starts with '$' and isn't '$abstract', we should
// treat it as a black box and skip.
if (cell_type[0] == '$')
return nullptr;
for (auto &dir : libdirs) {
static const vector<pair<string, string>> extensions_list =
{
{".v", "verilog"},
{".sv", "verilog -sv"},
{".il", "rtlil"}
};
for (auto &ext : extensions_list) {
std::string filename = dir + "/" + RTLIL::unescape_id(cell.type) + ext.first;
if (!check_file_exists(filename))
continue;
Frontend::frontend_call(&design, NULL, filename, ext.second);
RTLIL::Module *mod = design.module(cell.type);
if (!mod)
log_error("File `%s' from libdir does not declare module `%s'.\n",
filename.c_str(), cell_type.c_str());
return mod;
}
}
// We couldn't find the module anywhere. Complain if check is set.
if (check)
log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
cell_type.c_str(), parent.name.c_str(), cell.name.c_str());
return nullptr;
}
// Try to read an IdString as a numbered connection name ("$123" or similar),
// writing the result to dst. If the string isn't of the right format, ignore
// dst and return false.
bool read_id_num(RTLIL::IdString str, int *dst)
{
log_assert(dst);
const char *c_str = str.c_str();
if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9'))
return false;
*dst = atoi(c_str + 1);
return true;
}
// Check that the connections on the cell match those that are defined
// on the type: each named connection should match the name of a port
// and each positional connection should have an index smaller than
// the number of ports.
//
// Also do the same checks on the specified parameters.
void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLIL::Module &mod)
{
int id;
for (auto &conn : cell.connections()) {
if (read_id_num(conn.first, &id)) {
if (id <= 0 || id > GetSize(mod.ports))
log_error("Module `%s' referenced in module `%s' in cell `%s' "
"has only %d ports, requested port %d.\n",
log_id(cell.type), log_id(&module), log_id(&cell),
GetSize(mod.ports), id);
continue;
}
const RTLIL::Wire* wire = mod.wire(conn.first);
if (!wire || wire->port_id == 0) {
log_error("Module `%s' referenced in module `%s' in cell `%s' "
"does not have a port named '%s'.\n",
log_id(cell.type), log_id(&module), log_id(&cell),
log_id(conn.first));
}
}
for (auto &param : cell.parameters) {
if (read_id_num(param.first, &id)) {
if (id <= 0 || id > GetSize(mod.avail_parameters))
log_error("Module `%s' referenced in module `%s' in cell `%s' "
"has only %d parameters, requested parameter %d.\n",
log_id(cell.type), log_id(&module), log_id(&cell),
GetSize(mod.avail_parameters), id);
continue;
}
if (mod.avail_parameters.count(param.first) == 0 &&
param.first[0] != '$' &&
strchr(param.first.c_str(), '.') == NULL) {
log_error("Module `%s' referenced in module `%s' in cell `%s' "
"does not have a parameter named '%s'.\n",
log_id(cell.type), log_id(&module), log_id(&cell),
log_id(param.first));
}
}
}
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
{
bool did_something = false;
@ -173,23 +456,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
}
// Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
for (auto cell : module->cells())
{
if(cell->get_bool_attribute(ID::is_interface)) {
RTLIL::Module *intf_module = design->module(cell->type);
interfaces_in_module[cell->name] = intf_module;
}
}
IFExpander if_expander(*design, *module);
for (auto cell : module->cells())
{
bool has_interfaces_not_found = false;
std::vector<RTLIL::IdString> connections_to_remove;
std::vector<RTLIL::IdString> connections_to_add_name;
std::vector<RTLIL::SigSpec> connections_to_add_signal;
if_expander.start_cell();
if (cell->type.begins_with("$array:")) {
int pos[3];
@ -202,147 +473,32 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
array_cells[cell] = std::pair<int, int>(idx, num);
cell->type = cell->type.substr(pos_type + 1);
}
dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
if (design->module(cell->type) == nullptr)
{
if (design->module("$abstract" + cell->type.str()) != nullptr)
{
cell->type = design->module("$abstract" + cell->type.str())->derive(design, cell->parameters);
cell->parameters.clear();
did_something = true;
continue;
}
if (cell->type[0] == '$')
continue;
for (auto &dir : libdirs)
{
static const vector<pair<string, string>> extensions_list =
{
{".v", "verilog"},
{".sv", "verilog -sv"},
{".il", "rtlil"}
};
for (auto &ext : extensions_list)
{
filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first;
if (check_file_exists(filename)) {
Frontend::frontend_call(design, NULL, filename, ext.second);
goto loaded_module;
}
}
}
if ((flag_check || flag_simcheck) && cell->type[0] != '$')
log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
continue;
loaded_module:
if (design->module(cell->type) == nullptr)
log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
did_something = true;
} else {
RTLIL::Module *mod = design->module(cell->type);
if (!mod)
{
mod = get_module(*design, *cell, *module, flag_check || flag_simcheck, libdirs);
// Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
// some lists, so that the ports for sub-modules can be replaced further down:
for (auto &conn : cell->connections()) {
if(mod->wire(conn.first) != nullptr && mod->wire(conn.first)->get_bool_attribute(ID::is_interface)) { // Check if the connection is present as an interface in the sub-module's port list
if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute(ID::is_interface)) { // Check if the connected wire is a potential interface in the parent module
std::string interface_name_str = conn.second.bits()[0].wire->name.str();
interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
interface_name_str = "\\" + interface_name_str;
RTLIL::IdString interface_name = interface_name_str;
bool not_found_interface = false;
if(module->get_bool_attribute(ID::interfaces_replaced_in_module)) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
// Check if the interface instance is present in module:
// Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
// Check for both of them here
int nexactmatch = interfaces_in_module.count(interface_name) > 0;
std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
RTLIL::IdString interface_name2 = interface_name_str2;
int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
if (nexactmatch > 0 || nmatch2 > 0) {
if (nexactmatch != 0) // Choose the one with the plain name if it exists
interface_name2 = interface_name;
RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
for (auto mod_wire : mod_replace_ports->wires()) { // Go over all wires in interface, and add replacements to lists.
std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire->name);
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
if(module->wire(signal_name2) == nullptr) {
log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
}
else {
RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
connections_to_add_signal.push_back(wire_in_parent);
}
}
connections_to_remove.push_back(conn.first);
interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
// If we still don't have a module, treat the cell as a black box and skip
// it. Otherwise, we either loaded or derived something so should set the
// did_something flag before returning (to ensure we come back and expand
// the thing we just loaded).
if (mod)
did_something = true;
// Find if the sub-module has set a modport for the current
// interface connection. Add any modports to a dict which will
// be passed to AstModule::derive
string modport_name = mod->wire(conn.first)->get_string_attribute(ID::interface_modport);
if (!modport_name.empty()) {
modports_used_in_submodule[conn.first] = "\\" + modport_name;
}
}
else not_found_interface = true;
}
else not_found_interface = true;
// If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
// which will delay the expansion of this cell:
if (not_found_interface) {
// If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
if(!(module->get_bool_attribute(ID::cells_not_processed))) {
log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
}
else {
// Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
has_interfaces_not_found = true;
}
}
}
}
continue;
}
//
log_assert(mod);
// Go over all connections and check if any of them are SV
// interfaces.
if_expander.visit_connections(*cell, *mod);
if (flag_check || flag_simcheck)
{
for (auto &conn : cell->connections()) {
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
int id = atoi(conn.first.c_str()+1);
if (id <= 0 || id > GetSize(mod->ports))
log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d ports, requested port %d.\n",
log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->ports), id);
} else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
}
for (auto &param : cell->parameters) {
if (param.first[0] == '$' && '0' <= param.first[1] && param.first[1] <= '9') {
int id = atoi(param.first.c_str()+1);
if (id <= 0 || id > GetSize(mod->avail_parameters))
log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d parameters, requested parameter %d.\n",
log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->avail_parameters), id);
} else if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
}
check_cell_connections(*module, *cell, *mod);
}
}
RTLIL::Module *mod = design->module(cell->type);
if (design->module(cell->type)->get_blackbox_attribute()) {
if (mod->get_blackbox_attribute()) {
if (flag_simcheck)
log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
@ -350,23 +506,18 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
// If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
if(has_interfaces_not_found) {
if(if_expander.has_interfaces_not_found) {
did_something = true; // waiting for interfaces to be handled
continue;
}
// Do the actual replacements of the SV interface port connection with the individual signal connections:
for(unsigned int i=0;i<connections_to_add_name.size();i++) {
cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
}
// Remove the connection for the interface itself:
for(unsigned int i=0;i<connections_to_remove.size();i++) {
cell->connections_.erase(connections_to_remove[i]);
}
if_expander.rewrite_interface_connections(*cell);
// If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
// for the cell:
if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute(ID::module_not_derived)))) {
if (cell->parameters.size() == 0 &&
(if_expander.interfaces_to_add_to_submodule.size() == 0 ||
!(cell->get_bool_attribute(ID::module_not_derived)))) {
// If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
// so that the signals of the interface are added to the parent module.
if (mod->get_bool_attribute(ID::is_interface)) {
@ -375,7 +526,10 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
continue;
}
cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
cell->type = mod->derive(design,
cell->parameters,
if_expander.interfaces_to_add_to_submodule,
if_expander.modports_used_in_submodule);
cell->parameters.clear();
did_something = true;
@ -386,7 +540,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
if (mod->get_bool_attribute(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) {
cell->set_bool_attribute(ID::is_interface);
RTLIL::Module *derived_module = design->module(cell->type);
interfaces_in_module[cell->name] = derived_module;
if_expander.interfaces_in_module[cell->name] = derived_module;
did_something = true;
}
// We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
@ -399,11 +553,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
// If any interface instances or interface ports were found in the module, we need to rederive it completely:
if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
module->reprocess_module(design, interfaces_in_module);
if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
module->expand_interfaces(design, if_expander.interfaces_in_module);
return did_something;
}
// Now that modules have been derived, we may want to reprocess this
// module given the additional available context.
if (module->reprocess_if_necessary(design))
return true;
for (auto &it : array_cells)
{
@ -952,8 +1110,8 @@ struct HierarchyPass : public Pass {
pool<std::pair<IdString, IdString>> params_rename;
for (const auto &p : cell->parameters) {
if (p.first[0] == '$' && '0' <= p.first[1] && p.first[1] <= '9') {
int id = atoi(p.first.c_str()+1);
int id;
if (read_id_num(p.first, &id)) {
if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) {
log(" Failed to map positional parameter %d of cell %s.%s (%s).\n",
id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
@ -980,9 +1138,9 @@ struct HierarchyPass : public Pass {
log("Mapping positional arguments of cell %s.%s (%s).\n",
RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
dict<RTLIL::IdString, RTLIL::SigSpec> new_connections;
for (auto &conn : cell->connections())
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
int id = atoi(conn.first.c_str()+1);
for (auto &conn : cell->connections()) {
int id;
if (read_id_num(conn.first, &id)) {
std::pair<RTLIL::Module*,int> key(design->module(cell->type), id);
if (pos_map.count(key) == 0) {
log(" Failed to map positional argument %d of cell %s.%s (%s).\n",
@ -992,6 +1150,7 @@ struct HierarchyPass : public Pass {
new_connections[pos_map.at(key)] = conn.second;
} else
new_connections[conn.first] = conn.second;
}
cell->connections_ = new_connections;
}
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -8,4 +8,5 @@ OBJS += passes/memory/memory_bram.o
OBJS += passes/memory/memory_map.o
OBJS += passes/memory/memory_memx.o
OBJS += passes/memory/memory_nordff.o
OBJS += passes/memory/memory_narrow.o

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -31,16 +31,19 @@ struct MemoryPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]\n");
log(" memory [-nomap] [-nordff] [-nowiden] [-nosat] [-memx] [-bram <bram_rules>] [selection]\n");
log("\n");
log("This pass calls all the other memory_* passes in a useful order:\n");
log("\n");
log(" opt_mem\n");
log(" memory_dff [-nordff] (-memx implies -nordff)\n");
log(" opt_clean\n");
log(" memory_share\n");
log(" opt_mem_priority\n");
log(" opt_mem_feedback\n");
log(" memory_dff (skipped if called with -nordff or -memx)\n");
log(" opt_clean\n");
log(" memory_share [-nowiden] [-nosat]\n");
log(" opt_mem_widen\n");
log(" memory_memx (when called with -memx)\n");
log(" opt_clean\n");
log(" memory_collect\n");
log(" memory_bram -rules <bram_rules> (when called with -bram)\n");
log(" memory_map (skipped if called with -nomap)\n");
@ -55,6 +58,7 @@ struct MemoryPass : public Pass {
bool flag_nordff = false;
bool flag_memx = false;
string memory_bram_opts;
string memory_share_opts;
log_header(design, "Executing MEMORY pass.\n");
log_push();
@ -74,6 +78,14 @@ struct MemoryPass : public Pass {
flag_memx = true;
continue;
}
if (args[argidx] == "-nowiden") {
memory_share_opts += " -nowiden";
continue;
}
if (args[argidx] == "-nosat") {
memory_share_opts += " -nosat";
continue;
}
if (argidx+1 < args.size() && args[argidx] == "-bram") {
memory_bram_opts += " -rules " + args[++argidx];
continue;
@ -83,9 +95,13 @@ struct MemoryPass : public Pass {
extra_args(args, argidx, design);
Pass::call(design, "opt_mem");
Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
Pass::call(design, "opt_mem_priority");
Pass::call(design, "opt_mem_feedback");
if (!flag_nordff)
Pass::call(design, "memory_dff");
Pass::call(design, "opt_clean");
Pass::call(design, "memory_share");
Pass::call(design, "memory_share" + memory_share_opts);
Pass::call(design, "opt_mem_widen");
if (flag_memx)
Pass::call(design, "memory_memx");
Pass::call(design, "opt_clean");

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/mem.h"
#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -29,9 +30,6 @@ struct rules_t
int group, index, dupidx;
int wrmode, enable, transp, clocks, clkpol;
SigBit sig_clock;
SigSpec sig_addr, sig_data, sig_en;
bool effective_clkpol;
bool make_transp;
bool make_outreg;
int mapped_port;
@ -92,7 +90,6 @@ struct rules_t
pi.mapped_port = -1;
pi.make_transp = false;
pi.make_outreg = false;
pi.effective_clkpol = false;
portinfos.push_back(pi);
}
return portinfos;
@ -401,19 +398,13 @@ struct rules_t
}
};
bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
bool replace_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
{
// We will modify ports — make a copy of the structure.
Mem mem(orig_mem);
Module *module = mem.module;
auto portinfos = bram.make_portinfos();
int dup_count = 1;
pair<SigBit, bool> make_transp_clk;
bool enable_make_transp = false;
int make_transp_enbits = 0;
dict<int, pair<SigBit, bool>> clock_domains;
dict<int, bool> clock_polarities;
dict<int, bool> read_transp;
@ -440,35 +431,12 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
// bram.dump_config();
bool cell_init = !mem.inits.empty();
vector<Const> initdata;
if (cell_init) {
Const initparam = mem.get_init_data();
initdata.reserve(mem.size);
for (int i=0; i < mem.size; i++)
initdata.push_back(initparam.extract(mem.width*i, mem.width, State::Sx));
}
std::vector<int> shuffle_map;
if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && !mem.wr_ports.empty())
{
int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
// extract unshuffled data/enable bits
std::vector<SigSpec> old_wr_en;
std::vector<SigSpec> old_wr_data;
std::vector<SigSpec> old_rd_data;
for (auto &port : mem.wr_ports) {
old_wr_en.push_back(port.en);
old_wr_data.push_back(port.data);
}
for (auto &port : mem.rd_ports)
old_rd_data.push_back(port.data);
// analyze enable structure
std::vector<SigSpec> en_order;
@ -483,52 +451,13 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
bits_wr_en[sig].push_back(i);
}
// re-create memory ports
std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
std::vector<std::vector<State>> new_initdata;
std::vector<int> shuffle_map;
if (cell_init)
new_initdata.resize(mem.size);
for (auto &it : en_order)
{
auto &bits = bits_wr_en.at(it);
int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size;
int fillbits = buckets*bucket_size - GetSize(bits);
SigBit fillbit;
for (auto bit : bits_wr_en.at(it))
shuffle_map.push_back(bit);
for (int i = 0; i < GetSize(bits); i++) {
for (int j = 0; j < GetSize(mem.wr_ports); j++) {
new_wr_en[j].append(old_wr_en[j][bits[i]]);
new_wr_data[j].append(old_wr_data[j][bits[i]]);
fillbit = old_wr_en[j][bits[i]];
}
for (int j = 0; j < GetSize(mem.rd_ports); j++)
new_rd_data[j].append(old_rd_data[j][bits[i]]);
if (cell_init) {
for (int j = 0; j < mem.size; j++)
new_initdata[j].push_back(initdata[j][bits[i]]);
}
shuffle_map.push_back(bits[i]);
}
for (int i = 0; i < fillbits; i++) {
for (int j = 0; j < GetSize(mem.wr_ports); j++) {
new_wr_en[j].append(fillbit);
new_wr_data[j].append(State::S0);
}
for (int j = 0; j < GetSize(mem.rd_ports); j++)
new_rd_data[j].append(State::Sx);
if (cell_init) {
for (int j = 0; j < mem.size; j++)
new_initdata[j].push_back(State::Sx);
}
while (GetSize(shuffle_map) % bucket_size)
shuffle_map.push_back(-1);
}
}
log(" Results of bit order shuffling:");
@ -537,26 +466,15 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
log("\n");
// update mem_*, wr_*, and rd_* variables
mem.width = GetSize(new_wr_en.front());
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
auto &port = mem.wr_ports[i];
port.en = new_wr_en[i];
port.data = new_wr_data[i];
}
for (int i = 0; i < GetSize(mem.rd_ports); i++) {
auto &port = mem.rd_ports[i];
port.data = new_rd_data[i];
}
if (cell_init) {
for (int i = 0; i < mem.size; i++)
initdata[i] = Const(new_initdata[i]);
}
} else {
for (int i = 0; i < mem.width; i++)
shuffle_map.push_back(i);
}
// Align width up to dbits.
while (GetSize(shuffle_map) % bram.dbits)
shuffle_map.push_back(-1);
// assign write ports
pair<SigBit, bool> wr_clkdom;
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < GetSize(mem.wr_ports); cell_port_i++)
@ -574,8 +492,6 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
for (; bram_port_i < GetSize(portinfos); bram_port_i++)
{
auto &pi = portinfos[bram_port_i];
make_transp_enbits = pi.enable;
make_transp_clk = clkdom;
if (pi.wrmode != 1)
skip_bram_wport:
@ -601,16 +517,25 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
}
}
SigSpec sig_en;
SigBit last_en_bit = State::S1;
for (int i = 0; i < mem.width; i++) {
if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
last_en_bit = port.en[i];
sig_en.append(last_en_bit);
}
if (last_en_bit != port.en[i]) {
log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
// We need to check enable compatibility of this port, as well as all
// ports that have priority over this one (because they will be involved
// in emulate_priority logic).
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
auto &oport = mem.wr_ports[i];
if (i != cell_port_i && !oport.priority_mask[cell_port_i])
continue;
SigBit last_en_bit = State::S1;
for (int j = 0; j < GetSize(shuffle_map); j++) {
if (shuffle_map[j] == -1)
continue;
SigBit en_bit = oport.en[shuffle_map[j]];
if (pi.enable && j % (bram.dbits / pi.enable) == 0)
last_en_bit = en_bit;
if (last_en_bit != en_bit) {
log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
}
}
@ -620,14 +545,8 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &
if (port.clk_enable) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
pi.sig_clock = clkdom.first;
pi.effective_clkpol = clkdom.second;
}
pi.sig_en = sig_en;
pi.sig_addr = port.addr;
pi.sig_data = port.data;
bram_port_i++;
goto mapped_wr_port;
}
@ -650,10 +569,6 @@ grow_read_ports:;
for (auto &pi : portinfos) {
if (pi.wrmode == 0) {
pi.mapped_port = -1;
pi.sig_clock = SigBit();
pi.sig_addr = SigSpec();
pi.sig_data = SigSpec();
pi.sig_en = SigSpec();
pi.make_outreg = false;
pi.make_transp = false;
}
@ -685,10 +600,16 @@ grow_read_ports:;
for (int cell_port_i = 0; cell_port_i < GetSize(mem.rd_ports); cell_port_i++)
{
auto &port = mem.rd_ports[cell_port_i];
bool transp = port.transparent;
bool transp = false;
bool non_transp = false;
if (mem.wr_ports.empty())
transp = false;
if (port.clk_enable) {
for (int i = 0; i < GetSize(mem.wr_ports); i++)
if (port.transparency_mask[i])
transp = true;
else if (!port.collision_x_mask[i])
non_transp = true;
}
pair<SigBit, bool> clkdom(port.clk, port.clk_polarity);
if (!port.clk_enable)
@ -723,21 +644,13 @@ grow_read_ports:;
log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
if (port.en != State::S1 && pi.enable == 0) {
log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
if (non_transp && read_transp.count(pi.transp) && read_transp.at(pi.transp)) {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
skip_bram_rport_clkcheck:
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && GetSize(mem.wr_ports) <= 1) {
if (transp && (non_transp || (read_transp.count(pi.transp) && !read_transp.at(pi.transp)))) {
if (match.make_transp) {
pi.make_transp = true;
if (pi.clocks != 0) {
if (GetSize(mem.wr_ports) == 1 && wr_clkdom != clkdom) {
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
enable_make_transp = true;
}
} else {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@ -750,22 +663,19 @@ grow_read_ports:;
}
}
skip_bram_rport_clkcheck:
log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
pi.mapped_port = cell_port_i;
if (port.clk_enable) {
if (pi.clocks) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
if (!pi.make_transp)
read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
pi.sig_en = port.en;
pi.effective_clkpol = clkdom.second;
if (non_transp)
read_transp[pi.transp] = false;
if (transp && !pi.make_transp)
read_transp[pi.transp] = true;
}
pi.sig_addr = port.addr;
pi.sig_data = port.data;
if (grow_read_ports_cursor < cell_port_i) {
grow_read_ports_cursor = cell_port_i;
try_growing_more_read_ports = true;
@ -785,17 +695,19 @@ grow_read_ports:;
// update properties and re-check conditions
int dcells = GetSize(shuffle_map) / bram.dbits;
int acells = (mem.size + (1 << bram.abits) - 1) / (1 << bram.abits);
if (mode <= 1)
{
match_properties["dups"] = dup_count;
match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"];
int cells = ((mem.width + bram.dbits - 1) / bram.dbits) * ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits));
int cells = dcells * acells;
match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits));
match_properties["dcells"] = ((mem.width + bram.dbits - 1) / bram.dbits);
match_properties["acells"] = ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits));
match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"];
match_properties["dcells"] = dcells;
match_properties["acells"] = acells;
match_properties["cells"] = cells * dup_count;
log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
match_properties["dups"], match_properties["waste"], match_properties["efficiency"]);
@ -862,6 +774,90 @@ grow_read_ports:;
return true;
}
// At this point we are commited to replacing the RAM, and can mutate mem.
// Apply make_outreg and make_transp where necessary.
for (auto &pi : portinfos) {
if (pi.mapped_port == -1 || pi.wrmode)
continue;
auto &port = mem.rd_ports[pi.mapped_port];
if (pi.make_outreg) {
mem.extract_rdff(pi.mapped_port, initvals);
} else if (port.clk_enable) {
if (!pi.enable && port.en != State::S1)
mem.emulate_rden(pi.mapped_port, initvals);
else
mem.emulate_reset(pi.mapped_port, true, true, true, initvals);
}
if (pi.make_transp) {
for (int i = 0; i < GetSize(mem.wr_ports); i++)
if (port.transparency_mask[i])
mem.emulate_transparency(i, pi.mapped_port, initvals);
}
}
// We don't really support priorities, emulate them.
for (int i = 0; i < GetSize(mem.wr_ports); i++)
for (int j = 0; j < i; j++)
mem.emulate_priority(j, i, initvals);
// Swizzle the init data. Do this before changing mem.width, so that get_init_data works.
bool cell_init = !mem.inits.empty();
vector<Const> initdata;
if (cell_init) {
Const initparam = mem.get_init_data();
initdata.reserve(mem.size);
for (int i = 0; i < mem.size; i++) {
std::vector<State> val;
for (auto idx : shuffle_map) {
if (idx == -1)
val.push_back(State::Sx);
else
val.push_back(initparam[mem.width * i + idx]);
}
initdata.push_back(Const(val));
}
}
// Now the big swizzle.
mem.width = GetSize(shuffle_map);
// Swizzle write ports.
for (auto &port : mem.wr_ports) {
SigSpec new_en, new_data;
SigBit en_bit = State::S1;
for (auto idx : shuffle_map) {
if (idx == -1) {
new_data.append(State::Sx);
} else {
new_data.append(port.data[idx]);
en_bit = port.en[idx];
}
new_en.append(en_bit);
}
port.en = new_en;
port.data = new_data;
}
// Swizzle read ports.
for (auto &port : mem.rd_ports) {
SigSpec new_data = module->addWire(NEW_ID, mem.width);
Const new_init_value = Const(State::Sx, mem.width);
Const new_arst_value = Const(State::Sx, mem.width);
Const new_srst_value = Const(State::Sx, mem.width);
for (int i = 0; i < mem.width; i++)
if (shuffle_map[i] != -1) {
module->connect(port.data[shuffle_map[i]], new_data[i]);
new_init_value[i] = port.init_value[shuffle_map[i]];
new_arst_value[i] = port.arst_value[shuffle_map[i]];
new_srst_value[i] = port.srst_value[shuffle_map[i]];
}
port.data = new_data;
port.init_value = new_init_value;
port.arst_value = new_arst_value;
port.srst_value = new_srst_value;
}
// prepare variant parameters
dict<IdString, Const> variant_params;
@ -872,21 +868,9 @@ grow_read_ports:;
dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
for (int grid_d = 0; grid_d*bram.dbits < mem.width; grid_d++)
for (int grid_d = 0; grid_d < dcells; grid_d++)
{
SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q;
vector<SigSpec> mktr_wren;
if (enable_make_transp) {
mktr_wraddr = module->addWire(NEW_ID, bram.abits);
mktr_wrdata = module->addWire(NEW_ID, bram.dbits);
mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits);
module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second);
for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++)
mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits));
}
for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++)
for (int grid_a = 0; grid_a < acells; grid_a++)
for (int dupidx = 0; dupidx < dup_count; dupidx++)
{
Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", mem.memid.c_str(), grid_d, grid_a, dupidx)), bram.name);
@ -896,18 +880,16 @@ grow_read_ports:;
c->setParam(vp.first, vp.second);
if (cell_init) {
int init_offset = grid_a*(1 << bram.abits);
int init_offset = grid_a*(1 << bram.abits) - mem.start_offset;
int init_shift = grid_d*bram.dbits;
int init_size = (1 << bram.abits);
Const initparam(State::Sx, init_size*bram.dbits);
for (int i = 0; i < init_size; i++) {
State padding = State::Sx;
for (int i = 0; i < init_size; i++)
for (int j = 0; j < bram.dbits; j++)
if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
if (init_offset+i < GetSize(initdata) && init_offset+i >= 0)
initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
else
initparam[i*bram.dbits+j] = padding;
}
initparam[i*bram.dbits+j] = State::Sx;
c->setParam(ID::INIT, initparam);
}
@ -919,101 +901,84 @@ grow_read_ports:;
string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1);
const char *pf = prefix.c_str();
if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) {
c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock);
if (pi.clkpol > 1 && pi.sig_clock.wire)
c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
if (pi.transp > 1 && pi.sig_clock.wire)
c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
}
if (pi.clocks && clock_domains.count(pi.clocks))
c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), clock_domains.at(pi.clocks).first);
if (pi.clkpol > 1 && clock_polarities.count(pi.clkpol))
c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
if (pi.transp > 1 && read_transp.count(pi.transp))
c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
SigSpec addr_ok;
if (GetSize(pi.sig_addr) > bram.abits) {
SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits);
SigSpec sig_addr;
if (pi.mapped_port >= 0) {
if (pi.wrmode == 1)
sig_addr = mem.wr_ports[pi.mapped_port].addr;
else
sig_addr = mem.rd_ports[pi.mapped_port].addr;
}
if (GetSize(sig_addr) > bram.abits) {
SigSpec extra_addr = sig_addr.extract(bram.abits, GetSize(sig_addr) - bram.abits);
SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr));
addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel);
}
if (pi.enable)
{
SigSpec sig_en = pi.sig_en;
if (pi.wrmode == 1) {
sig_en.extend_u0((grid_d+1) * pi.enable);
sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
}
if (!addr_ok.empty())
sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
c->setPort(stringf("\\%sEN", pf), sig_en);
if (pi.wrmode == 1 && enable_make_transp)
module->connect(mktr_wren[grid_a], sig_en);
}
SigSpec sig_addr = pi.sig_addr;
sig_addr.extend_u0(bram.abits);
c->setPort(stringf("\\%sADDR", pf), sig_addr);
if (pi.wrmode == 1 && enable_make_transp && grid_a == 0)
module->connect(mktr_wraddr, sig_addr);
SigSpec sig_data = pi.sig_data;
sig_data.extend_u0((grid_d+1) * bram.dbits);
sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits);
if (pi.wrmode == 1) {
c->setPort(stringf("\\%sDATA", pf), sig_data);
if (enable_make_transp && grid_a == 0)
module->connect(mktr_wrdata, sig_data);
} else {
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
if (pi.make_outreg && pi.make_transp) {
log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
} else if (pi.make_outreg) {
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
if (!pi.sig_en.empty())
bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
bram_dout = bram_dout_q;
} else if (pi.make_transp) {
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr));
SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits);
module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second);
for (int i = 0; i < make_transp_enbits; i++) {
int en_width = bram.dbits / make_transp_enbits;
SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width);
SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width);
bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i]));
}
if (pi.mapped_port == -1)
{
if (pi.enable)
c->setPort(stringf("\\%sEN", pf), Const(State::S0, pi.enable));
continue;
}
for (int i = bram.dbits-1; i >= 0; i--)
if (sig_data[i].wire == nullptr) {
sig_data.remove(i);
bram_dout.remove(i);
}
auto &port = mem.wr_ports[pi.mapped_port];
SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), sig_data);
if (pi.enable)
{
SigSpec sig_en;
int stride = bram.dbits / pi.enable;
for (int i = 0; i < pi.enable; i++)
sig_en.append(port.en[stride * i + grid_d * bram.dbits]);
if (!addr_ok.empty())
sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
c->setPort(stringf("\\%sEN", pf), sig_en);
}
} else {
if (pi.mapped_port == -1)
{
if (pi.enable)
c->setPort(stringf("\\%sEN", pf), State::S0);
continue;
}
auto &port = mem.rd_ports[pi.mapped_port];
SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits);
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
SigSpec addr_ok_q = addr_ok;
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
if (port.clk_enable && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
if (!pi.sig_en.empty())
addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
module->addDffe(NEW_ID, port.clk, port.en, addr_ok, addr_ok_q, port.clk_polarity);
}
dout_cache[sig_data].first.append(addr_ok_q);
dout_cache[sig_data].second.append(bram_dout);
if (pi.enable) {
SigSpec sig_en = port.en;
if (!addr_ok.empty())
sig_en = module->And(NEW_ID, sig_en, addr_ok);
c->setPort(stringf("\\%sEN", pf), sig_en);
}
}
}
}
@ -1037,9 +1002,10 @@ grow_read_ports:;
return true;
}
void handle_memory(Mem &mem, const rules_t &rules)
void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
{
log("Processing %s.%s:\n", log_id(mem.module), log_id(mem.memid));
mem.narrow();
bool cell_init = !mem.inits.empty();
@ -1189,7 +1155,7 @@ void handle_memory(Mem &mem, const rules_t &rules)
if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name)))
log_error("Found 'or_next_if_better' in last match rule.\n");
if (!replace_memory(mem, rules, bram, match, match_properties, 1)) {
if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 1)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@ -1216,12 +1182,12 @@ void handle_memory(Mem &mem, const rules_t &rules)
best_rule_cache.clear();
auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
if (!replace_memory(mem, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
if (!replace_memory(mem, rules, initvals, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
return;
}
if (!replace_memory(mem, rules, bram, match, match_properties, 0)) {
if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 0)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@ -1353,9 +1319,12 @@ struct MemoryBramPass : public Pass {
}
extra_args(args, argidx, design);
for (auto mod : design->selected_modules())
for (auto &mem : Mem::get_selected_memories(mod))
handle_memory(mem, rules);
for (auto mod : design->selected_modules()) {
SigMap sigmap(mod);
FfInitVals initvals(&sigmap, mod);
for (auto &mem : Mem::get_selected_memories(mod))
handle_memory(mem, rules, &initvals);
}
}
} MemoryBramPass;

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -17,415 +17,627 @@
*
*/
#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
#include "kernel/ffinit.h"
#include "kernel/qcsat.h"
#include "kernel/mem.h"
#include "kernel/ff.h"
#include "kernel/ffmerge.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct MuxData {
int base_idx;
int size;
bool is_b;
SigSpec sig_s;
std::vector<SigSpec> sig_other;
};
struct PortData {
bool relevant;
std::vector<bool> uncollidable_mask;
std::vector<bool> transparency_mask;
std::vector<bool> collision_x_mask;
bool final_transparency;
bool final_collision_x;
};
// A helper with some caching for transparency-related SAT queries.
// Bound to a single memory read port in the process of being converted
// from async to sync..
struct MemQueryCache
{
QuickConeSat &qcsat;
// The memory.
Mem &mem;
// The port, still async at this point.
MemRd &port;
// The virtual FF that will end up merged into this port.
FfData &ff;
// An ezSAT variable that is true when we actually care about the data
// read from memory (ie. the FF has enable on and is not in reset).
int port_ren;
// Some caches.
dict<std::pair<int, SigBit>, bool> cache_can_collide_rdwr;
dict<std::tuple<int, int, SigBit, SigBit>, bool> cache_can_collide_together;
dict<std::tuple<int, SigBit, SigBit, bool>, bool> cache_is_w2rbyp;
dict<std::tuple<SigBit, bool>, bool> cache_impossible_with_ren;
MemQueryCache(QuickConeSat &qcsat, Mem &mem, MemRd &port, FfData &ff) : qcsat(qcsat), mem(mem), port(port), ff(ff) {
// port_ren is an upper bound on when we care about the value fetched
// from memory this cycle.
int ren = ezSAT::CONST_TRUE;
if (ff.has_ce) {
ren = qcsat.importSigBit(ff.sig_ce);
if (!ff.pol_ce)
ren = qcsat.ez->NOT(ren);
}
if (ff.has_srst) {
int nrst = qcsat.importSigBit(ff.sig_srst);
if (ff.pol_srst)
nrst = qcsat.ez->NOT(nrst);
ren = qcsat.ez->AND(ren, nrst);
}
port_ren = ren;
}
// Returns ezSAT variable that is true iff the two addresses are the same.
int addr_eq(SigSpec raddr, SigSpec waddr) {
int abits = std::max(GetSize(raddr), GetSize(waddr));
raddr.extend_u0(abits);
waddr.extend_u0(abits);
return qcsat.ez->vec_eq(qcsat.importSig(raddr), qcsat.importSig(waddr));
}
// Returns true if a given write port bit can be active at the same time
// as this read port and at the same address.
bool can_collide_rdwr(int widx, SigBit wen) {
std::pair<int, SigBit> key(widx, wen);
auto it = cache_can_collide_rdwr.find(key);
if (it != cache_can_collide_rdwr.end())
return it->second;
auto &wport = mem.wr_ports[widx];
int aeq = addr_eq(port.addr, wport.addr);
int wen_sat = qcsat.importSigBit(wen);
qcsat.prepare();
bool res = qcsat.ez->solve(aeq, wen_sat, port_ren);
cache_can_collide_rdwr[key] = res;
return res;
}
// Returns true if both given write port bits can be active at the same
// time as this read port and at the same address (three-way collision).
bool can_collide_together(int widx1, int widx2, int bitidx) {
auto &wport1 = mem.wr_ports[widx1];
auto &wport2 = mem.wr_ports[widx2];
SigBit wen1 = wport1.en[bitidx];
SigBit wen2 = wport2.en[bitidx];
std::tuple<int, int, SigBit, SigBit> key(widx1, widx2, wen1, wen2);
auto it = cache_can_collide_together.find(key);
if (it != cache_can_collide_together.end())
return it->second;
int aeq1 = addr_eq(port.addr, wport1.addr);
int aeq2 = addr_eq(port.addr, wport2.addr);
int wen1_sat = qcsat.importSigBit(wen1);
int wen2_sat = qcsat.importSigBit(wen2);
qcsat.prepare();
bool res = qcsat.ez->solve(wen1_sat, wen2_sat, aeq1, aeq2, port_ren);
cache_can_collide_together[key] = res;
return res;
}
// Returns true if the given mux selection signal is a valid data-bypass
// signal in soft transparency logic for a given write port bit.
bool is_w2rbyp(int widx, SigBit wen, SigBit sel, bool neg_sel) {
std::tuple<int, SigBit, SigBit, bool> key(widx, wen, sel, neg_sel);
auto it = cache_is_w2rbyp.find(key);
if (it != cache_is_w2rbyp.end())
return it->second;
auto &wport = mem.wr_ports[widx];
int aeq = addr_eq(port.addr, wport.addr);
int wen_sat = qcsat.importSigBit(wen);
int sel_expected = qcsat.ez->AND(aeq, wen_sat);
int sel_sat = qcsat.importSigBit(sel);
if (neg_sel)
sel_sat = qcsat.ez->NOT(sel_sat);
qcsat.prepare();
bool res = !qcsat.ez->solve(port_ren, qcsat.ez->XOR(sel_expected, sel_sat));
cache_is_w2rbyp[key] = res;
return res;
}
// Returns true if the given mux selection signal can never be true
// when this port is active.
bool impossible_with_ren(SigBit sel, bool neg_sel) {
std::tuple<SigBit, bool> key(sel, neg_sel);
auto it = cache_impossible_with_ren.find(key);
if (it != cache_impossible_with_ren.end())
return it->second;
int sel_sat = qcsat.importSigBit(sel);
if (neg_sel)
sel_sat = qcsat.ez->NOT(sel_sat);
qcsat.prepare();
bool res = !qcsat.ez->solve(port_ren, sel_sat);
cache_impossible_with_ren[key] = res;
return res;
}
// Helper for data_eq: walks up a multiplexer when the value of its
// sel signal is constant under the assumption that this read port
// is active and a given other mux sel signal is true.
bool walk_up_mux_cond(SigBit sel, bool neg_sel, SigBit &bit) {
auto &drivers = qcsat.modwalker.signal_drivers[qcsat.modwalker.sigmap(bit)];
if (GetSize(drivers) != 1)
return false;
auto driver = *drivers.begin();
if (!driver.cell->type.in(ID($mux), ID($pmux)))
return false;
log_assert(driver.port == ID::Y);
SigSpec sig_s = driver.cell->getPort(ID::S);
int sel_sat = qcsat.importSigBit(sel);
if (neg_sel)
sel_sat = qcsat.ez->NOT(sel_sat);
bool all_0 = true;
int width = driver.cell->parameters.at(ID::WIDTH).as_int();
for (int i = 0; i < GetSize(sig_s); i++) {
int sbit = qcsat.importSigBit(sig_s[i]);
qcsat.prepare();
if (!qcsat.ez->solve(port_ren, sel_sat, qcsat.ez->NOT(sbit))) {
bit = driver.cell->getPort(ID::B)[i * width + driver.offset];
return true;
}
if (qcsat.ez->solve(port_ren, sel_sat, sbit))
all_0 = false;
}
if (all_0) {
bit = driver.cell->getPort(ID::A)[driver.offset];
return true;
}
return false;
}
// Returns true if a given data signal is equivalent to another, under
// the assumption that this read port is active and a given mux sel signal
// is true. Used to match transparency logic data with write port data.
// The walk_up_mux_cond part is necessary because write ports in yosys
// tend to be connected to things like (wen ? wdata : 'x).
bool data_eq(SigBit sel, bool neg_sel, SigBit dbit, SigBit odbit) {
if (qcsat.modwalker.sigmap(dbit) == qcsat.modwalker.sigmap(odbit))
return true;
while (walk_up_mux_cond(sel, neg_sel, dbit));
while (walk_up_mux_cond(sel, neg_sel, odbit));
return qcsat.modwalker.sigmap(dbit) == qcsat.modwalker.sigmap(odbit);
}
};
struct MemoryDffWorker
{
Module *module;
SigMap sigmap;
vector<Cell*> dff_cells;
dict<SigBit, SigBit> invbits;
dict<SigBit, int> sigbit_users_count;
dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
pool<Cell*> forward_merged_dffs, candidate_dffs;
ModWalker modwalker;
FfInitVals initvals;
FfMergeHelper merger;
MemoryDffWorker(Module *module) : module(module), sigmap(module)
MemoryDffWorker(Module *module) : module(module), modwalker(module->design)
{
initvals.set(&sigmap, module);
modwalker.setup(module);
initvals.set(&modwalker.sigmap, module);
merger.set(&initvals, module);
}
bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity)
// Starting from the output of an async read port, as long as the data
// signal's only user is a mux data signal, passes through the mux
// and remembers information about it. Conceptually works on every
// bit separately, but coalesces the result when possible.
SigSpec walk_muxes(SigSpec data, std::vector<MuxData> &res) {
bool did_something;
do {
did_something = false;
int prev_idx = -1;
Cell *prev_cell = nullptr;
bool prev_is_b = false;
for (int i = 0; i < GetSize(data); i++) {
SigBit bit = modwalker.sigmap(data[i]);
auto &consumers = modwalker.signal_consumers[bit];
if (GetSize(consumers) != 1 || modwalker.signal_outputs.count(bit))
continue;
auto consumer = *consumers.begin();
bool is_b;
if (consumer.cell->type == ID($mux)) {
if (consumer.port == ID::A) {
is_b = false;
} else if (consumer.port == ID::B) {
is_b = true;
} else {
continue;
}
} else if (consumer.cell->type == ID($pmux)) {
if (consumer.port == ID::A) {
is_b = false;
} else {
continue;
}
} else {
continue;
}
SigSpec y = consumer.cell->getPort(ID::Y);
int mux_width = GetSize(y);
SigBit ybit = y.extract(consumer.offset);
if (prev_cell != consumer.cell || prev_idx+1 != i || prev_is_b != is_b) {
MuxData md;
md.base_idx = i;
md.size = 0;
md.is_b = is_b;
md.sig_s = consumer.cell->getPort(ID::S);
md.sig_other.resize(GetSize(md.sig_s));
prev_cell = consumer.cell;
prev_is_b = is_b;
res.push_back(md);
}
auto &md = res.back();
md.size++;
for (int j = 0; j < GetSize(md.sig_s); j++) {
SigBit obit = consumer.cell->getPort(is_b ? ID::A : ID::B).extract(j * mux_width + consumer.offset);
md.sig_other[j].append(obit);
}
prev_idx = i;
data[i] = ybit;
did_something = true;
}
} while (did_something);
return data;
}
// Merges FF and possibly soft transparency logic into an asynchronous
// read port, making it into a synchronous one.
//
// There are three moving parts involved here:
//
// - the async port, which we start from, whose data port is input to...
// - an arbitrary chain of $mux and $pmux cells implementing soft transparency
// logic (ie. bypassing write port's data iff the write port is active and
// writing to the same address as this read port), which in turn feeds...
// - a final FF
//
// The async port and the mux chain are not allowed to have any users that
// are not part of the above.
//
// The algorithm is:
//
// 1. Walk through the muxes.
// 2. Recognize the final FF.
// 3. Knowing the FF's clock and read enable, make a list of write ports
// that we'll run transparency analysis on.
// 4. For every mux bit, recognize it as one of:
// - a transparency bypass mux for some port
// - a bypass mux that feeds 'x instead (this will result in collision
// don't care behavior being recognized)
// - a mux that never selects the other value when read port is active,
// and can thus be skipped (this is necessary because this could
// be a transparency bypass mux for never-colliding port that other
// passes failed to optimize)
// - a mux whose other input is 'x, and can thus be skipped
// 5. When recognizing transparency bypasses, take care to preserve priority
// behavior — when two bypasses are sequential muxes on the chain, they
// effectively have priority over one another, and the transform can
// only be performed when either a) their corresponding write ports
// also have priority, or b) there can never be a three-way collision
// between the two write ports and the read port.
// 6. Check consistency of per-bit transparency masks, merge them into
// per-port transparency masks
// 7. If everything went fine in the previous steps, actually perform
// the merge.
void handle_rd_port(Mem &mem, QuickConeSat &qcsat, int idx)
{
sigmap.apply(sig);
auto &port = mem.rd_ports[idx];
log("Checking read port `%s'[%d] in module `%s': ", mem.memid.c_str(), idx, module->name.c_str());
dict<SigBit, SigBit> cache;
std::vector<MuxData> muxdata;
SigSpec data = walk_muxes(port.data, muxdata);
FfData ff;
pool<std::pair<Cell *, int>> bits;
if (!merger.find_output_ff(data, ff, bits)) {
log("no output FF found.\n");
return;
}
if (!ff.has_clk) {
log("output latches are not supported.\n");
return;
}
if (ff.has_aload) {
log("output FF has async load, not supported.\n");
return;
}
if (ff.has_sr) {
// Latches and FFs with SR are not supported.
log("output FF has both set and reset, not supported.\n");
return;
}
for (auto &bit : sig)
{
if (cache.count(bit)) {
bit = cache[bit];
// Construct cache.
MemQueryCache cache(qcsat, mem, port, ff);
// Prepare information structure about all ports, recognize port bits
// that can never collide at all and don't need to be checked.
std::vector<PortData> portdata;
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
PortData pd;
auto &wport = mem.wr_ports[i];
pd.relevant = true;
if (!wport.clk_enable)
pd.relevant = false;
if (wport.clk != ff.sig_clk)
pd.relevant = false;
if (wport.clk_polarity != ff.pol_clk)
pd.relevant = false;
// In theory, we *could* support mismatched width
// ports here. However, it's not worth it — wide
// ports are recognized *after* memory_dff in
// a normal flow.
if (wport.wide_log2 != port.wide_log2)
pd.relevant = false;
pd.uncollidable_mask.resize(GetSize(port.data));
pd.transparency_mask.resize(GetSize(port.data));
pd.collision_x_mask.resize(GetSize(port.data));
if (pd.relevant) {
// If we got this far, this port is potentially
// transparent and/or has undefined collision
// behavior. Now, for every bit, check if it can
// ever collide.
for (int j = 0; j < ff.width; j++) {
if (!cache.can_collide_rdwr(i, wport.en[j])) {
pd.uncollidable_mask[j] = true;
pd.collision_x_mask[j] = true;
}
}
}
portdata.push_back(pd);
}
// Now inspect the mux chain.
for (auto &md : muxdata) {
// We only mark transparent bits after processing a complete
// mux, so that the transparency priority validation check
// below sees transparency information as of previous mux.
std::vector<std::pair<PortData&, int>> trans_queue;
for (int sel_idx = 0; sel_idx < GetSize(md.sig_s); sel_idx++) {
SigBit sbit = md.sig_s[sel_idx];
SigSpec &odata = md.sig_other[sel_idx];
for (int bitidx = md.base_idx; bitidx < md.base_idx+md.size; bitidx++) {
SigBit odbit = odata[bitidx-md.base_idx];
bool recognized = false;
for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) {
auto &pd = portdata[pi];
auto &wport = mem.wr_ports[pi];
if (!pd.relevant)
continue;
if (pd.uncollidable_mask[bitidx])
continue;
bool match = cache.is_w2rbyp(pi, wport.en[bitidx], sbit, md.is_b);
if (!match)
continue;
// If we got here, we recognized this mux sel
// as valid bypass sel for a given port bit.
if (odbit == State::Sx) {
// 'x, mark collision don't care.
pd.collision_x_mask[bitidx] = true;
pd.transparency_mask[bitidx] = false;
} else if (cache.data_eq(sbit, md.is_b, wport.data[bitidx], odbit)) {
// Correct data value, mark transparency,
// but only after verifying that priority
// is fine.
for (int k = 0; k < GetSize(mem.wr_ports); k++) {
if (portdata[k].transparency_mask[bitidx]) {
if (wport.priority_mask[k])
continue;
if (!cache.can_collide_together(pi, k, bitidx))
continue;
log("FF found, but transparency logic priority doesn't match write priority.\n");
return;
}
}
recognized = true;
trans_queue.push_back({pd, bitidx});
break;
} else {
log("FF found, but with a mux data input that doesn't seem to correspond to transparency logic.\n");
return;
}
}
if (!recognized) {
// If we haven't positively identified this as
// a bypass: it's still skippable if the
// data is 'x, or if the sel cannot actually be
// active.
if (odbit == State::Sx)
continue;
if (cache.impossible_with_ren(sbit, md.is_b))
continue;
log("FF found, but with a mux select that doesn't seem to correspond to transparency logic.\n");
return;
}
}
}
// Done with this mux, now actually apply the transparencies.
for (auto it : trans_queue) {
it.first.transparency_mask[it.second] = true;
it.first.collision_x_mask[it.second] = false;
}
}
// Final merging and validation of per-bit masks.
for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) {
auto &pd = portdata[pi];
if (!pd.relevant)
continue;
}
if (bit.wire == NULL)
continue;
if (initvals(bit) != State::Sx)
return false;
for (auto cell : dff_cells)
{
SigSpec this_clk = cell->getPort(ID::CLK);
bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
if (invbits.count(this_clk)) {
this_clk = invbits.at(this_clk);
this_clk_polarity = !this_clk_polarity;
}
if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
if (this_clk != clk)
continue;
if (this_clk_polarity != clk_polarity)
continue;
}
RTLIL::SigSpec q_norm = cell->getPort(ID::Q);
sigmap.apply(q_norm);
RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D));
if (d.size() != 1)
bool trans = false;
bool non_trans = false;
for (int i = 0; i < ff.width; i++) {
if (pd.collision_x_mask[i])
continue;
if (cell->type == ID($sdffce)) {
SigSpec rval = cell->parameters[ID::SRST_VALUE];
SigSpec rbit = q_norm.extract(bit, &rval);
if (cell->parameters[ID::SRST_POLARITY].as_bool())
d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
else
d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
}
if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) {
if (cell->parameters[ID::EN_POLARITY].as_bool())
d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN));
else
d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN));
}
if (cell->type.in(ID($sdff), ID($sdffe))) {
SigSpec rval = cell->parameters[ID::SRST_VALUE];
SigSpec rbit = q_norm.extract(bit, &rval);
if (cell->parameters[ID::SRST_POLARITY].as_bool())
d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
else
d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
}
cache[bit] = d;
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
candidate_dffs.insert(cell);
goto replaced_this_bit;
if (pd.transparency_mask[i])
trans = true;
else
non_trans = true;
}
return false;
replaced_this_bit:;
}
return true;
}
bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity)
{
sigmap.apply(sig);
for (auto &bit : sig)
{
if (bit.wire == NULL)
continue;
for (auto cell : dff_cells)
{
if (forward_merged_dffs.count(cell))
continue;
if (!cell->type.in(ID($dff), ID($dffe)))
continue;
SigSpec this_clk = cell->getPort(ID::CLK);
bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
SigSpec this_en = State::S1;
bool this_en_polarity = true;
if (cell->type == ID($dffe)) {
this_en = cell->getPort(ID::EN);
this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool();
}
if (invbits.count(this_clk)) {
this_clk = invbits.at(this_clk);
this_clk_polarity = !this_clk_polarity;
}
if (invbits.count(this_en)) {
this_en = invbits.at(this_en);
this_en_polarity = !this_en_polarity;
}
if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
if (this_clk != clk)
continue;
if (this_clk_polarity != clk_polarity)
continue;
if (this_en != en)
continue;
if (this_en_polarity != en_polarity)
continue;
}
RTLIL::SigSpec q_norm = cell->getPort(ID::D);
sigmap.apply(q_norm);
RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q));
if (d.size() != 1)
continue;
if (initvals(d) != State::Sx)
return false;
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
en = this_en;
en_polarity = this_en_polarity;
candidate_dffs.insert(cell);
goto replaced_this_bit;
}
return false;
replaced_this_bit:;
}
return true;
}
void handle_wr_cell(RTLIL::Cell *cell)
{
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
bool clk_polarity = 0;
candidate_dffs.clear();
RTLIL::SigSpec sig_addr = cell->getPort(ID::ADDR);
if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
log("no (compatible) $dff for address input found.\n");
return;
}
RTLIL::SigSpec sig_data = cell->getPort(ID::DATA);
if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
log("no (compatible) $dff for data input found.\n");
return;
}
RTLIL::SigSpec sig_en = cell->getPort(ID::EN);
if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
log("no (compatible) $dff for enable input found.\n");
return;
}
if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
{
for (auto cell : candidate_dffs)
forward_merged_dffs.insert(cell);
cell->setPort(ID::CLK, clk);
cell->setPort(ID::ADDR, sig_addr);
cell->setPort(ID::DATA, sig_data);
cell->setPort(ID::EN, sig_en);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
log("merged $dff to cell.\n");
return;
}
log("no (compatible) $dff found.\n");
}
void disconnect_dff(RTLIL::SigSpec sig)
{
sigmap.apply(sig);
sig.sort_and_unify();
std::stringstream sstr;
sstr << "$memory_dff_disconnected$" << (autoidx++);
RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
for (auto cell : module->cells())
if (cell->type.in(ID($dff), ID($dffe))) {
RTLIL::SigSpec new_q = cell->getPort(ID::Q);
new_q.replace(sig, new_sig);
cell->setPort(ID::Q, new_q);
}
}
void handle_rd_cell(RTLIL::Cell *cell)
{
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
bool clk_polarity = 0;
bool en_polarity = 0;
RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
RTLIL::SigSpec en_data;
RTLIL::SigSpec sig_data = cell->getPort(ID::DATA);
for (auto bit : sigmap(sig_data))
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{
RTLIL::SigSpec en;
std::vector<RTLIL::SigSpec> check_q;
do {
bool enable_invert = mux_cells_a.count(sig_data) != 0;
Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
check_q.push_back(sigmap(mux->getPort(enable_invert ? ID::B : ID::A)));
sig_data = sigmap(mux->getPort(ID::Y));
en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort(ID::S)) : mux->getPort(ID::S));
} while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data));
for (auto bit : sig_data)
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; }))
{
if (en_data != State::S1 || !en_polarity) {
if (!en_polarity)
en_data = module->LogicNot(NEW_ID, en_data);
en.append(en_data);
}
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
cell->setPort(ID::EN, en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
cell->setPort(ID::DATA, sig_data);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0);
log("merged data $dff with rd enable to cell.\n");
if (trans && non_trans) {
log("FF found, but soft transparency logic is inconsistent for port %d.\n", pi);
return;
}
pd.final_transparency = trans;
pd.final_collision_x = !trans && !non_trans;
}
// OK, it worked.
log("merging output FF to cell.\n");
merger.remove_output_ff(bits);
if (ff.has_ce && !ff.pol_ce)
ff.sig_ce = module->LogicNot(NEW_ID, ff.sig_ce);
if (ff.has_arst && !ff.pol_arst)
ff.sig_arst = module->LogicNot(NEW_ID, ff.sig_arst);
if (ff.has_srst && !ff.pol_srst)
ff.sig_srst = module->LogicNot(NEW_ID, ff.sig_srst);
port.clk = ff.sig_clk;
port.clk_enable = true;
port.clk_polarity = ff.pol_clk;
if (ff.has_ce)
port.en = ff.sig_ce;
else
{
if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
{
if (!en_polarity)
en_data = module->LogicNot(NEW_ID, en_data);
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
cell->setPort(ID::EN, en_data);
cell->setPort(ID::DATA, sig_data);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0);
log("merged data $dff to cell.\n");
port.en = State::S1;
if (ff.has_arst) {
port.arst = ff.sig_arst;
port.arst_value = ff.val_arst;
} else {
port.arst = State::S0;
}
if (ff.has_srst) {
port.srst = ff.sig_srst;
port.srst_value = ff.val_srst;
port.ce_over_srst = ff.ce_over_srst;
} else {
port.srst = State::S0;
}
port.init_value = ff.val_init;
port.data = ff.sig_q;
for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) {
auto &pd = portdata[pi];
if (!pd.relevant)
continue;
if (pd.final_collision_x) {
log(" Write port %d: don't care on collision.\n", pi);
port.collision_x_mask[pi] = true;
} else if (pd.final_transparency) {
log(" Write port %d: transparent.\n", pi);
port.transparency_mask[pi] = true;
} else {
log(" Write port %d: non-transparent.\n", pi);
}
}
mem.emit();
}
void handle_rd_port_addr(Mem &mem, int idx)
{
auto &port = mem.rd_ports[idx];
log("Checking read port address `%s'[%d] in module `%s': ", mem.memid.c_str(), idx, module->name.c_str());
FfData ff;
pool<std::pair<Cell *, int>> bits;
if (!merger.find_input_ff(port.addr, ff, bits)) {
log("no address FF found.\n");
return;
}
if (!ff.has_clk) {
log("address latches are not supported.\n");
return;
}
if (ff.has_aload) {
log("address FF has async load, not supported.\n");
return;
}
if (ff.has_sr || ff.has_arst) {
log("address FF has async set and/or reset, not supported.\n");
return;
}
// Trick part: this transform is invalid if the initial
// value of the FF is fully-defined. However, we
// cannot simply reject FFs with any defined init bit,
// as this is often the result of merging a const bit.
if (ff.val_init.is_fully_def()) {
log("address FF has fully-defined init value, not supported.\n");
return;
}
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
auto &wport = mem.wr_ports[i];
if (!wport.clk_enable || wport.clk != ff.sig_clk || wport.clk_polarity != ff.pol_clk) {
log("address FF clock is not compatible with write clock.\n");
return;
}
}
skip_ff_after_read_merging:;
RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
RTLIL::SigSpec sig_addr = cell->getPort(ID::ADDR);
if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
{
cell->setPort(ID::CLK, clk_addr);
cell->setPort(ID::EN, State::S1);
cell->setPort(ID::ADDR, sig_addr);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
cell->parameters[ID::TRANSPARENT] = RTLIL::Const(1);
log("merged address $dff to cell.\n");
return;
}
log("no (compatible) $dff found.\n");
// Now we're commited to merge it.
merger.mark_input_ff(bits);
// If the address FF has enable and/or sync reset, unmap it.
ff.unmap_ce_srst();
port.clk = ff.sig_clk;
port.en = State::S1;
port.addr = ff.sig_d;
port.clk_enable = true;
port.clk_polarity = ff.pol_clk;
for (int i = 0; i < GetSize(mem.wr_ports); i++)
port.transparency_mask[i] = true;
mem.emit();
log("merged address FF to cell.\n");
}
void run(bool flag_wr_only)
void run()
{
for (auto wire : module->wires()) {
if (wire->port_output)
for (auto bit : sigmap(wire))
sigbit_users_count[bit]++;
}
for (auto cell : module->cells()) {
if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)))
dff_cells.push_back(cell);
if (cell->type == ID($mux)) {
mux_cells_a[sigmap(cell->getPort(ID::A))] = cell;
mux_cells_b[sigmap(cell->getPort(ID::B))] = cell;
std::vector<Mem> memories = Mem::get_selected_memories(module);
for (auto &mem : memories) {
QuickConeSat qcsat(modwalker);
for (int i = 0; i < GetSize(mem.rd_ports); i++) {
if (!mem.rd_ports[i].clk_enable)
handle_rd_port(mem, qcsat, i);
}
}
for (auto &mem : memories) {
for (int i = 0; i < GetSize(mem.rd_ports); i++) {
if (!mem.rd_ports[i].clk_enable)
handle_rd_port_addr(mem, i);
}
if (cell->type.in(ID($not), ID($_NOT_)) || (cell->type == ID($logic_not) && GetSize(cell->getPort(ID::A)) == 1)) {
SigSpec sig_a = cell->getPort(ID::A);
SigSpec sig_y = cell->getPort(ID::Y);
if (cell->type == ID($not))
sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
if (cell->type == ID($logic_not))
sig_y.extend_u0(1);
for (int i = 0; i < GetSize(sig_y); i++)
invbits[sig_y[i]] = sig_a[i];
}
for (auto &conn : cell->connections())
if (!cell->known() || cell->input(conn.first))
for (auto bit : sigmap(conn.second))
sigbit_users_count[bit]++;
}
for (auto cell : module->selected_cells())
if (cell->type == ID($memwr) && !cell->parameters[ID::CLK_ENABLE].as_bool())
handle_wr_cell(cell);
if (!flag_wr_only)
for (auto cell : module->selected_cells())
if (cell->type == ID($memrd) && !cell->parameters[ID::CLK_ENABLE].as_bool())
handle_rd_cell(cell);
}
};
struct MemoryDffPass : public Pass {
MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memory read ports") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" memory_dff [options] [selection]\n");
log("\n");
log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
log("This pass detects DFFs at memory read ports and merges them into the memory port.\n");
log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
log("interface and yields a synchronous memory port.\n");
log("\n");
log(" -nordfff\n");
log(" do not merge registers on read ports\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool flag_wr_only = false;
log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
flag_wr_only = true;
continue;
}
break;
}
extra_args(args, argidx, design);
for (auto mod : design->selected_modules()) {
MemoryDffWorker worker(mod);
worker.run(flag_wr_only);
worker.run();
}
}
} MemoryDffPass;

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -34,10 +34,12 @@ struct MemoryMapWorker
RTLIL::Design *design;
RTLIL::Module *module;
SigMap sigmap;
FfInitVals initvals;
std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache;
MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) {}
MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), sigmap(module), initvals(&sigmap, module) {}
std::string map_case(std::string value) const
{
@ -151,11 +153,9 @@ struct MemoryMapWorker
continue;
}
if (!port.clk_enable) {
if (port.addr.is_fully_const()) {
// FIXME: Actually we should check for port.en.is_fully_const() also and
// create a $adff cell with this ports port.en input as reset pin when port.en
// is not a simple static 1.
static_cells_map[port.addr.as_int() - mem.start_offset] = port.data;
if (port.addr.is_fully_const() && port.en.is_fully_ones()) {
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
static_cells_map[port.addr.as_int() + sub] = port.data.extract(sub * mem.width, mem.width);
static_ports.insert(i);
continue;
}
@ -176,22 +176,24 @@ struct MemoryMapWorker
log("Mapping memory %s in module %s:\n", mem.memid.c_str(), module->name.c_str());
std::vector<RTLIL::SigSpec> data_reg_in;
std::vector<RTLIL::SigSpec> data_reg_out;
int abits = ceil_log2(mem.size);
std::vector<RTLIL::SigSpec> data_reg_in(1 << abits);
std::vector<RTLIL::SigSpec> data_reg_out(1 << abits);
int count_static = 0;
for (int i = 0; i < mem.size; i++)
{
if (static_cells_map.count(i) > 0)
int addr = i + mem.start_offset;
int idx = addr & ((1 << abits) - 1);
if (static_cells_map.count(addr) > 0)
{
data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem.width));
data_reg_out.push_back(static_cells_map[i]);
data_reg_out[idx] = static_cells_map[addr];
count_static++;
}
else
{
RTLIL::Cell *c = module->addCell(genid(mem.memid, "", i), ID($dff));
RTLIL::Cell *c = module->addCell(genid(mem.memid, "", addr), ID($dff));
c->parameters[ID::WIDTH] = mem.width;
if (GetSize(refclock) != 0) {
c->parameters[ID::CLK_POLARITY] = RTLIL::Const(refclock_pol);
@ -201,13 +203,13 @@ struct MemoryMapWorker
c->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::S0));
}
RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", i, "$d"), mem.width);
data_reg_in.push_back(RTLIL::SigSpec(w_in));
c->setPort(ID::D, data_reg_in.back());
RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", addr, "$d"), mem.width);
data_reg_in[idx] = w_in;
c->setPort(ID::D, w_in);
std::string w_out_name = stringf("%s[%d]", mem.memid.c_str(), i);
std::string w_out_name = stringf("%s[%d]", mem.memid.c_str(), addr);
if (module->wires_.count(w_out_name) > 0)
w_out_name = genid(mem.memid, "", i, "$q");
w_out_name = genid(mem.memid, "", addr, "$q");
RTLIL::Wire *w_out = module->addWire(w_out_name, mem.width);
SigSpec w_init = init_data.extract(i*mem.width, mem.width);
@ -215,8 +217,8 @@ struct MemoryMapWorker
if (!w_init.is_fully_undef())
w_out->attributes[ID::init] = w_init.as_const();
data_reg_out.push_back(RTLIL::SigSpec(w_out));
c->setPort(ID::Q, data_reg_out.back());
data_reg_out[idx] = w_out;
c->setPort(ID::Q, w_out);
}
}
@ -224,35 +226,31 @@ struct MemoryMapWorker
int count_dff = 0, count_mux = 0, count_wrmux = 0;
int abits = ceil_log2(mem.size);
for (int i = 0; i < GetSize(mem.rd_ports); i++)
{
auto &port = mem.rd_ports[i];
if (mem.extract_rdff(i))
if (mem.extract_rdff(i, &initvals))
count_dff++;
RTLIL::SigSpec rd_addr = port.addr;
rd_addr.extend_u0(abits, false);
if (mem.start_offset)
rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem.start_offset, abits));
std::vector<RTLIL::SigSpec> rd_signals;
rd_signals.push_back(port.data);
for (int j = 0; j < abits; j++)
for (int j = 0; j < abits - port.wide_log2; j++)
{
std::vector<RTLIL::SigSpec> next_rd_signals;
for (size_t k = 0; k < rd_signals.size(); k++)
{
RTLIL::Cell *c = module->addCell(genid(mem.memid, "$rdmux", i, "", j, "", k), ID($mux));
c->parameters[ID::WIDTH] = mem.width;
c->parameters[ID::WIDTH] = GetSize(port.data);
c->setPort(ID::Y, rd_signals[k]);
c->setPort(ID::S, rd_addr.extract(abits-j-1, 1));
count_mux++;
c->setPort(ID::A, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$a"), mem.width));
c->setPort(ID::B, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$b"), mem.width));
c->setPort(ID::A, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$a"), GetSize(port.data)));
c->setPort(ID::B, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$b"), GetSize(port.data)));
next_rd_signals.push_back(c->getPort(ID::A));
next_rd_signals.push_back(c->getPort(ID::B));
@ -261,37 +259,38 @@ struct MemoryMapWorker
next_rd_signals.swap(rd_signals);
}
for (int j = 0; j < mem.size; j++)
module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j]));
for (int j = 0; j < (1 << abits); j++)
if (data_reg_out[j] != SigSpec())
module->connect(RTLIL::SigSig(rd_signals[j >> port.wide_log2].extract((j & ((1 << port.wide_log2) - 1)) * mem.width, mem.width), data_reg_out[j]));
}
log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux);
for (int i = 0; i < mem.size; i++)
{
if (static_cells_map.count(i) > 0)
int addr = i + mem.start_offset;
int idx = addr & ((1 << abits) - 1);
if (static_cells_map.count(addr) > 0)
continue;
RTLIL::SigSpec sig = data_reg_out[i];
RTLIL::SigSpec sig = data_reg_out[idx];
for (int j = 0; j < GetSize(mem.wr_ports); j++)
{
auto &port = mem.wr_ports[j];
RTLIL::SigSpec wr_addr = port.addr;
RTLIL::SigSpec wr_addr = port.addr.extract_end(port.wide_log2);
RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(addr >> port.wide_log2, GetSize(wr_addr)));
if (mem.start_offset)
wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem.start_offset, GetSize(wr_addr)));
RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, GetSize(wr_addr)));
int sub = addr & ((1 << port.wide_log2) - 1);
int wr_offset = 0;
while (wr_offset < port.en.size())
while (wr_offset < mem.width)
{
int wr_width = 1;
RTLIL::SigSpec wr_bit = port.en.extract(wr_offset, 1);
RTLIL::SigSpec wr_bit = port.en.extract(wr_offset + sub * mem.width, 1);
while (wr_offset + wr_width < port.en.size()) {
RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width, 1);
while (wr_offset + wr_width < mem.width) {
RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width + sub * mem.width, 1);
if (next_wr_bit != wr_bit)
break;
wr_width++;
@ -301,7 +300,7 @@ struct MemoryMapWorker
if (wr_bit != State::S1)
{
RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", i, "", j, "", wr_offset), ID($and));
RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", addr, "", j, "", wr_offset), ID($and));
c->parameters[ID::A_SIGNED] = RTLIL::Const(0);
c->parameters[ID::B_SIGNED] = RTLIL::Const(0);
c->parameters[ID::A_WIDTH] = RTLIL::Const(1);
@ -310,17 +309,17 @@ struct MemoryMapWorker
c->setPort(ID::A, w);
c->setPort(ID::B, wr_bit);
w = module->addWire(genid(mem.memid, "$wren", i, "", j, "", wr_offset, "$y"));
w = module->addWire(genid(mem.memid, "$wren", addr, "", j, "", wr_offset, "$y"));
c->setPort(ID::Y, RTLIL::SigSpec(w));
}
RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset), ID($mux));
RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset), ID($mux));
c->parameters[ID::WIDTH] = wr_width;
c->setPort(ID::A, sig.extract(wr_offset, wr_width));
c->setPort(ID::B, port.data.extract(wr_offset, wr_width));
c->setPort(ID::B, port.data.extract(wr_offset + sub * mem.width, wr_width));
c->setPort(ID::S, RTLIL::SigSpec(w));
w = module->addWire(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width);
w = module->addWire(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset, "$y"), wr_width);
c->setPort(ID::Y, w);
sig.replace(wr_offset, w);
@ -329,7 +328,7 @@ struct MemoryMapWorker
}
}
module->connect(RTLIL::SigSig(data_reg_in[i], sig));
module->connect(RTLIL::SigSig(data_reg_in[idx], sig));
}
log(" write interface: %d write mux blocks.\n", count_wrmux);

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -17,11 +17,8 @@
*
*/
#include "kernel/register.h"
#include "kernel/log.h"
#include <sstream>
#include <set>
#include <stdlib.h>
#include "kernel/yosys.h"
#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -38,53 +35,45 @@ struct MemoryMemxPass : public Pass {
log("behavior for out-of-bounds memory reads and writes.\n");
log("\n");
}
SigSpec make_addr_check(Mem &mem, SigSpec addr) {
int start_addr = mem.start_offset;
int end_addr = mem.start_offset + mem.size;
addr.extend_u0(32);
SigSpec res = mem.module->Nex(NEW_ID, mem.module->ReduceXor(NEW_ID, addr), mem.module->ReduceXor(NEW_ID, {addr, State::S1}));
if (start_addr != 0)
res = mem.module->LogicAnd(NEW_ID, res, mem.module->Ge(NEW_ID, addr, start_addr));
res = mem.module->LogicAnd(NEW_ID, res, mem.module->Lt(NEW_ID, addr, end_addr));
return res;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
extra_args(args, 1, design);
for (auto module : design->selected_modules())
for (auto &mem : Mem::get_selected_memories(module))
{
vector<Cell*> mem_port_cells;
for (auto cell : module->selected_cells())
if (cell->type.in(ID($memrd), ID($memwr)))
mem_port_cells.push_back(cell);
for (auto cell : mem_port_cells)
for (auto &port : mem.rd_ports)
{
IdString memid = cell->getParam(ID::MEMID).decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
if (port.clk_enable)
log_error("Memory %s.%s has a synchronous read port. Synchronous read ports are not supported by memory_memx!\n",
log_id(module), log_id(mem.memid));
int lowest_addr = mem->start_offset;
int highest_addr = mem->start_offset + mem->size - 1;
SigSpec addr = cell->getPort(ID::ADDR);
addr.extend_u0(32);
SigSpec addr_ok = module->Nex(NEW_ID, module->ReduceXor(NEW_ID, addr), module->ReduceXor(NEW_ID, {addr, State::S1}));
if (lowest_addr != 0)
addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Ge(NEW_ID, addr, lowest_addr));
addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Le(NEW_ID, addr, highest_addr));
if (cell->type == ID($memrd))
{
if (cell->getParam(ID::CLK_ENABLE).as_bool())
log_error("Cell %s.%s (%s) has an enabled clock. Clocked $memrd cells are not supported by memory_memx!\n",
log_id(module), log_id(cell), log_id(cell->type));
SigSpec rdata = cell->getPort(ID::DATA);
Wire *raw_rdata = module->addWire(NEW_ID, GetSize(rdata));
module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(rdata)), raw_rdata, addr_ok, rdata);
cell->setPort(ID::DATA, raw_rdata);
}
if (cell->type == ID($memwr))
{
SigSpec en = cell->getPort(ID::EN);
en = module->And(NEW_ID, en, addr_ok.repeat(GetSize(en)));
cell->setPort(ID::EN, en);
}
SigSpec addr_ok = make_addr_check(mem, port.addr);
Wire *raw_rdata = module->addWire(NEW_ID, GetSize(port.data));
module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(port.data)), raw_rdata, addr_ok, port.data);
port.data = raw_rdata;
}
for (auto &port : mem.wr_ports) {
SigSpec addr_ok = make_addr_check(mem, port.addr);
port.en = module->And(NEW_ID, port.en, addr_ok.repeat(GetSize(port.en)));
}
mem.emit();
}
}
} MemoryMemxPass;

View file

@ -0,0 +1,67 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2020 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/sigtools.h"
#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct MemoryNarrowPass : public Pass {
MemoryNarrowPass() : Pass("memory_narrow", "split up wide memory ports") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" memory_narrow [options] [selection]\n");
log("\n");
log("This pass splits up wide memory ports into several narrow ports.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing MEMORY_NARROW pass (splitting up wide memory ports).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
for (auto &mem : Mem::get_selected_memories(module))
{
bool wide = false;
for (auto &port : mem.rd_ports)
if (port.wide_log2)
wide = true;
for (auto &port : mem.wr_ports)
if (port.wide_log2)
wide = true;
if (wide) {
mem.narrow();
mem.emit();
}
}
}
}
} MemoryNarrowPass;
PRIVATE_NAMESPACE_END

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -33,7 +33,7 @@ struct MemoryNordffPass : public Pass {
log(" memory_nordff [options] [selection]\n");
log("\n");
log("This pass extracts FFs from memory read ports. This results in a netlist\n");
log("similar to what one would get from calling memory_dff with -nordff.\n");
log("similar to what one would get from not calling memory_dff.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
@ -51,15 +51,22 @@ struct MemoryNordffPass : public Pass {
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
for (auto &mem : Mem::get_selected_memories(module))
{
bool changed = false;
for (int i = 0; i < GetSize(mem.rd_ports); i++)
if (mem.extract_rdff(i))
changed = true;
SigMap sigmap(module);
FfInitVals initvals(&sigmap, module);
for (auto &mem : Mem::get_selected_memories(module))
{
bool changed = false;
for (int i = 0; i < GetSize(mem.rd_ports); i++) {
if (mem.rd_ports[i].clk_enable) {
mem.extract_rdff(i, &initvals);
changed = true;
}
}
if (changed)
mem.emit();
if (changed)
mem.emit();
}
}
}
} MemoryNordffPass;

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -2,6 +2,9 @@
OBJS += passes/opt/opt.o
OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_mem.o
OBJS += passes/opt/opt_mem_feedback.o
OBJS += passes/opt/opt_mem_priority.o
OBJS += passes/opt/opt_mem_widen.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_dff.o

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -21,6 +21,7 @@
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
#include "kernel/ffinit.h"
#include <stdlib.h>
#include <stdio.h>
#include <set>
@ -101,6 +102,7 @@ void rmunused_module_cells(Module *module, bool verbose)
pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
dict<SigBit, vector<string>> driver_driver_logs;
FfInitVals ffinit(&sigmap, module);
SigMap raw_sigmap;
for (auto &it : module->connections_) {
@ -115,7 +117,7 @@ void rmunused_module_cells(Module *module, bool verbose)
}
for (Cell *cell : module->cells()) {
if (cell->type.in(ID($memwr), ID($meminit))) {
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
mem2cells[mem_id].insert(cell);
}
@ -165,7 +167,7 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto bit : sigmap(it.second))
bits.insert(bit);
if (cell->type == ID($memrd)) {
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
if (mem_unused.count(mem_id)) {
mem_unused.erase(mem_id);
@ -193,6 +195,8 @@ void rmunused_module_cells(Module *module, bool verbose)
if (verbose)
log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
module->design->scratchpad_set_bool("opt.did_something", true);
if (RTLIL::builtin_ff_cell_types().count(cell->type))
ffinit.remove_init(cell->getPort(ID::Q));
module->remove(cell);
count_rm_cells++;
}
@ -335,6 +339,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
used_signals_nodrivers.add(it2.second);
}
}
dict<RTLIL::SigBit, RTLIL::State> init_bits;
for (auto &it : module->wires_) {
RTLIL::Wire *wire = it.second;
if (wire->port_id > 0) {
@ -350,6 +355,29 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
assign_map.apply(sig);
used_signals.add(sig);
}
auto it2 = wire->attributes.find(ID::init);
if (it2 != wire->attributes.end()) {
RTLIL::Const &val = it2->second;
SigSpec sig = assign_map(wire);
for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
if (val.bits[i] != State::Sx)
init_bits[sig[i]] = val.bits[i];
wire->attributes.erase(it2);
}
}
for (auto wire : module->wires()) {
bool found = false;
Const val(State::Sx, wire->width);
for (int i = 0; i < wire->width; i++) {
auto it = init_bits.find(RTLIL::SigBit(wire, i));
if (it != init_bits.end()) {
val.bits[i] = it->second;
found = true;
}
}
if (found)
wire->attributes[ID::init] = val;
}
pool<RTLIL::Wire*> del_wires_queue;

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
@ -21,7 +21,8 @@
#include "kernel/log.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/satgen.h"
#include "kernel/qcsat.h"
#include "kernel/modtools.h"
#include "kernel/sigtools.h"
#include "kernel/ffinit.h"
#include "kernel/ff.h"
@ -51,26 +52,20 @@ struct OptDffWorker
FfInitVals initvals;
dict<SigBit, int> bitusers;
dict<SigBit, cell_int_t> bit2mux;
dict<SigBit, RTLIL::Cell*> bit2driver;
typedef std::map<RTLIL::SigBit, bool> pattern_t;
typedef std::set<pattern_t> patterns_t;
typedef std::pair<RTLIL::SigBit, bool> ctrl_t;
typedef std::set<ctrl_t> ctrls_t;
ezSatPtr ez;
SatGen satgen;
pool<Cell*> sat_cells;
// Used as a queue.
std::vector<Cell *> dff_cells;
OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) {
// Gathering three kinds of information here for every sigmapped SigBit:
OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod) {
// Gathering two kinds of information here for every sigmapped SigBit:
//
// - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user)
// - bit2mux: the mux cell and bit index that drives it, if any
// - bit2driver: the cell driving it, if any
for (auto wire : module->wires())
{
@ -88,10 +83,6 @@ struct OptDffWorker
for (auto conn : cell->connections()) {
bool is_output = cell->output(conn.first);
if (is_output) {
for (auto bit : sigmap(conn.second))
bit2driver[bit] = cell;
}
if (!is_output || !cell->known()) {
for (auto bit : sigmap(conn.second))
bitusers[bit]++;
@ -104,20 +95,6 @@ struct OptDffWorker
}
std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
if (!sat_cells.insert(c).second)
return;
if (!satgen.importCell(c))
return;
for (auto &conn : c->connections()) {
if (!c->input(conn.first))
continue;
for (auto bit : sigmap(conn.second))
if (bit2driver.count(bit))
sat_import_cell(bit2driver.at(bit));
}
};
State combine_const(State a, State b) {
if (a == State::Sx && !opt.keepdc)
return b;
@ -295,7 +272,7 @@ struct OptDffWorker
bool changed = false;
if (!ff.width) {
module->remove(cell);
ff.remove();
did_something = true;
continue;
}
@ -318,9 +295,9 @@ struct OptDffWorker
if (!ff.pol_clr) {
module->connect(ff.sig_q[i], ff.sig_clr[i]);
} else if (ff.is_fine) {
module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
module->addNotGate(NEW_ID, ff.sig_clr[i], ff.sig_q[i]);
} else {
module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
module->addNot(NEW_ID, ff.sig_clr[i], ff.sig_q[i]);
}
log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n",
i, log_id(cell), log_id(cell->type), log_id(module));
@ -336,6 +313,7 @@ struct OptDffWorker
continue;
}
ff = ff.slice(keep_bits);
ff.cell = cell;
changed = true;
}
@ -402,6 +380,68 @@ struct OptDffWorker
}
}
if (ff.has_aload) {
if (ff.sig_aload == (ff.pol_aload ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_aload == State::Sx)) {
// Always-inactive enable — remove.
log("Removing never-active async load on %s (%s) from module %s.\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_aload = false;
changed = true;
} else if (ff.sig_aload == (ff.pol_aload ? State::S1 : State::S0)) {
// Always-active enable. Make a comb circuit, nuke the FF/latch.
log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.remove();
if (ff.has_sr) {
SigSpec tmp;
if (ff.is_fine) {
if (ff.pol_set)
tmp = module->MuxGate(NEW_ID, ff.sig_ad, State::S1, ff.sig_set);
else
tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_ad, ff.sig_set);
if (ff.pol_clr)
module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q);
else
module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q);
} else {
if (ff.pol_set)
tmp = module->Or(NEW_ID, ff.sig_ad, ff.sig_set);
else
tmp = module->Or(NEW_ID, ff.sig_ad, module->Not(NEW_ID, ff.sig_set));
if (ff.pol_clr)
module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q);
else
module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q);
}
} else if (ff.has_arst) {
if (ff.is_fine) {
if (ff.pol_arst)
module->addMuxGate(NEW_ID, ff.sig_ad, ff.val_arst[0], ff.sig_arst, ff.sig_q);
else
module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_ad, ff.sig_arst, ff.sig_q);
} else {
if (ff.pol_arst)
module->addMux(NEW_ID, ff.sig_ad, ff.val_arst, ff.sig_arst, ff.sig_q);
else
module->addMux(NEW_ID, ff.val_arst, ff.sig_ad, ff.sig_arst, ff.sig_q);
}
} else {
module->connect(ff.sig_q, ff.sig_ad);
}
did_something = true;
continue;
} else if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) {
log("Changing const-value async load to async reset on %s (%s) from module %s.\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_arst = true;
ff.has_aload = false;
ff.sig_arst = ff.sig_aload;
ff.pol_arst = ff.pol_aload;
ff.val_arst = ff.sig_ad.as_const();
changed = true;
}
}
if (ff.has_arst) {
if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) {
// Always-inactive reset — remove.
@ -413,8 +453,7 @@ struct OptDffWorker
// Always-active async reset — change to const driver.
log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n",
log_id(cell), log_id(cell->type), log_id(module));
initvals.remove_init(ff.sig_q);
module->remove(cell);
ff.remove();
module->connect(ff.sig_q, ff.val_arst);
did_something = true;
continue;
@ -434,111 +473,63 @@ struct OptDffWorker
log_id(cell), log_id(cell->type), log_id(module));
ff.has_srst = false;
if (!ff.ce_over_srst)
ff.has_en = false;
ff.sig_d = ff.val_d = ff.val_srst;
ff.d_is_const = true;
ff.has_ce = false;
ff.sig_d = ff.val_srst;
changed = true;
}
}
if (ff.has_en) {
if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) {
if (ff.has_ce) {
if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) {
// Always-inactive enable — remove.
if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) {
if (ff.has_srst && !ff.ce_over_srst) {
log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
log_id(cell), log_id(cell->type), log_id(module));
// FF with sync reset — connect the sync reset to D instead.
ff.pol_en = ff.pol_srst;
ff.sig_en = ff.sig_srst;
ff.pol_ce = ff.pol_srst;
ff.sig_ce = ff.sig_srst;
ff.has_srst = false;
ff.sig_d = ff.val_d = ff.val_srst;
ff.d_is_const = true;
ff.sig_d = ff.val_srst;
changed = true;
} else {
log("Handling never-active EN on %s (%s) from module %s (removing D path).\n",
log_id(cell), log_id(cell->type), log_id(module));
// The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
ff.has_d = ff.has_en = ff.has_clk = false;
// The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver).
ff.has_ce = ff.has_clk = ff.has_srst = false;
changed = true;
}
} else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) {
// Always-active enable.
if (ff.has_clk) {
// For FF, just remove the useless enable.
log("Removing always-active EN on %s (%s) from module %s.\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_en = false;
changed = true;
} else {
// For latches, make a comb circuit, nuke the latch.
log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n",
log_id(cell), log_id(cell->type), log_id(module));
initvals.remove_init(ff.sig_q);
module->remove(cell);
if (ff.has_sr) {
SigSpec tmp;
if (ff.is_fine) {
if (ff.pol_set)
tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set);
else
tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set);
if (ff.pol_clr)
module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q);
else
module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q);
} else {
if (ff.pol_set)
tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set);
else
tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set));
if (ff.pol_clr)
module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q);
else
module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q);
}
} else if (ff.has_arst) {
if (ff.is_fine) {
if (ff.pol_arst)
module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
else
module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q);
} else {
if (ff.pol_arst)
module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q);
else
module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q);
}
} else {
module->connect(ff.sig_q, ff.sig_d);
}
did_something = true;
continue;
}
} else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) {
// Always-active enable. Just remove it.
// For FF, just remove the useless enable.
log("Removing always-active EN on %s (%s) from module %s.\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_ce = false;
changed = true;
}
}
if (ff.has_clk) {
if (ff.sig_clk.is_fully_const()) {
// Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
// Const clock — the D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver).
log("Handling const CLK on %s (%s) from module %s (removing D path).\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false;
ff.has_ce = ff.has_clk = ff.has_srst = false;
changed = true;
}
}
if (ff.has_d && ff.sig_d == ff.sig_q) {
if ((ff.has_clk || ff.has_gclk) && ff.sig_d == ff.sig_q) {
// Q wrapped back to D, can be removed.
if (ff.has_clk && ff.has_srst) {
// FF with sync reset — connect the sync reset to D instead.
log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
log_id(cell), log_id(cell->type), log_id(module));
if (ff.has_en && ff.ce_over_srst) {
if (!ff.pol_en) {
if (ff.has_ce && ff.ce_over_srst) {
if (!ff.pol_ce) {
if (ff.is_fine)
ff.sig_en = module->NotGate(NEW_ID, ff.sig_en);
ff.sig_ce = module->NotGate(NEW_ID, ff.sig_ce);
else
ff.sig_en = module->Not(NEW_ID, ff.sig_en);
ff.sig_ce = module->Not(NEW_ID, ff.sig_ce);
}
if (!ff.pol_srst) {
if (ff.is_fine)
@ -547,96 +538,37 @@ struct OptDffWorker
ff.sig_srst = module->Not(NEW_ID, ff.sig_srst);
}
if (ff.is_fine)
ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst);
ff.sig_ce = module->AndGate(NEW_ID, ff.sig_ce, ff.sig_srst);
else
ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst);
ff.pol_en = true;
ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst);
ff.pol_ce = true;
} else {
ff.pol_en = ff.pol_srst;
ff.sig_en = ff.sig_srst;
ff.pol_ce = ff.pol_srst;
ff.sig_ce = ff.sig_srst;
}
ff.has_en = true;
ff.has_ce = true;
ff.has_srst = false;
ff.sig_d = ff.val_d = ff.val_srst;
ff.d_is_const = true;
ff.sig_d = ff.val_srst;
changed = true;
} else {
// The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
log("Handling D = Q on %s (%s) from module %s (removing D path).\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_d = ff.has_en = ff.has_clk = false;
ff.has_clk = ff.has_ce = false;
changed = true;
}
}
// Now check if any bit can be replaced by a constant.
pool<int> removed_sigbits;
for (int i = 0; i < ff.width; i++) {
State val = ff.val_init[i];
if (ff.has_arst)
val = combine_const(val, ff.val_arst[i]);
if (ff.has_srst)
val = combine_const(val, ff.val_srst[i]);
if (ff.has_sr) {
if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
val = combine_const(val, State::S0);
if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
val = combine_const(val, State::S1);
}
if (val == State::Sm)
continue;
if (ff.has_d) {
if (!ff.sig_d[i].wire) {
val = combine_const(val, ff.sig_d[i].data);
if (val == State::Sm)
continue;
} else {
if (!opt.sat)
continue;
// For each register bit, try to prove that it cannot change from the initial value. If so, remove it
if (!bit2driver.count(ff.sig_d[i]))
continue;
if (val != State::S0 && val != State::S1)
continue;
sat_import_cell(bit2driver.at(ff.sig_d[i]));
int init_sat_pi = satgen.importSigSpec(val).front();
int q_sat_pi = satgen.importSigBit(ff.sig_q[i]);
int d_sat_pi = satgen.importSigBit(ff.sig_d[i]);
// Try to find out whether the register bit can change under some circumstances
bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
// If the register bit cannot change, we can replace it with a constant
if (counter_example_found)
continue;
}
}
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
i, log_id(cell), log_id(cell->type), log_id(module));
initvals.remove_init(ff.sig_q[i]);
module->connect(ff.sig_q[i], val);
removed_sigbits.insert(i);
}
if (!removed_sigbits.empty()) {
std::vector<int> keep_bits;
for (int i = 0; i < ff.width; i++)
if (!removed_sigbits.count(i))
keep_bits.push_back(i);
if (keep_bits.empty()) {
module->remove(cell);
did_something = true;
continue;
}
ff = ff.slice(keep_bits);
if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) {
log("Handling AD = Q on %s (%s) from module %s (removing async load path).\n",
log_id(cell), log_id(cell->type), log_id(module));
ff.has_aload = false;
changed = true;
}
// The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets.
if (ff.has_clk) {
if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) {
if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) {
// Try to merge sync resets.
std::map<ctrls_t, std::vector<int>> groups;
std::vector<int> remaining_indices;
@ -697,9 +629,9 @@ struct OptDffWorker
new_ff.has_srst = true;
new_ff.sig_srst = srst.first;
new_ff.pol_srst = srst.second;
if (new_ff.has_en)
if (new_ff.has_ce)
new_ff.ce_over_srst = true;
Cell *new_cell = new_ff.emit(module, NEW_ID);
Cell *new_cell = new_ff.emit();
if (new_cell)
dff_cells.push_back(new_cell);
log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n",
@ -712,10 +644,11 @@ struct OptDffWorker
continue;
} else if (GetSize(remaining_indices) != ff.width) {
ff = ff.slice(remaining_indices);
ff.cell = cell;
changed = true;
}
}
if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) {
if ((!ff.has_srst || !ff.has_ce || !ff.ce_over_srst) && !opt.nodffe) {
// Try to merge enables.
std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups;
std::vector<int> remaining_indices;
@ -745,8 +678,8 @@ struct OptDffWorker
if (!opt.simple_dffe)
patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t());
if (!patterns.empty() || !enables.empty()) {
if (ff.has_en)
enables.insert(ctrl_t(ff.sig_en, ff.pol_en));
if (ff.has_ce)
enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce));
simplify_patterns(patterns);
groups[std::make_pair(patterns, enables)].push_back(i);
} else
@ -757,11 +690,11 @@ struct OptDffWorker
FfData new_ff = ff.slice(it.second);
ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine);
new_ff.has_en = true;
new_ff.sig_en = en.first;
new_ff.pol_en = en.second;
new_ff.has_ce = true;
new_ff.sig_ce = en.first;
new_ff.pol_ce = en.second;
new_ff.ce_over_srst = false;
Cell *new_cell = new_ff.emit(module, NEW_ID);
Cell *new_cell = new_ff.emit();
if (new_cell)
dff_cells.push_back(new_cell);
log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n",
@ -774,6 +707,7 @@ struct OptDffWorker
continue;
} else if (GetSize(remaining_indices) != ff.width) {
ff = ff.slice(remaining_indices);
ff.cell = cell;
changed = true;
}
}
@ -781,9 +715,116 @@ struct OptDffWorker
if (changed) {
// Rebuild the FF.
IdString name = cell->name;
module->remove(cell);
ff.emit(module, name);
ff.emit();
did_something = true;
}
}
return did_something;
}
bool run_constbits() {
ModWalker modwalker(module->design, module);
QuickConeSat qcsat(modwalker);
// Run as a separate sub-pass, so that we don't mutate (non-FF) cells under ModWalker.
bool did_something = false;
for (auto cell : module->selected_cells()) {
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
continue;
FfData ff(&initvals, cell);
// Now check if any bit can be replaced by a constant.
pool<int> removed_sigbits;
for (int i = 0; i < ff.width; i++) {
State val = ff.val_init[i];
if (ff.has_arst)
val = combine_const(val, ff.val_arst[i]);
if (ff.has_srst)
val = combine_const(val, ff.val_srst[i]);
if (ff.has_sr) {
if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
val = combine_const(val, State::S0);
if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
val = combine_const(val, State::S1);
}
if (val == State::Sm)
continue;
if (ff.has_clk || ff.has_gclk) {
if (!ff.sig_d[i].wire) {
val = combine_const(val, ff.sig_d[i].data);
if (val == State::Sm)
continue;
} else {
if (!opt.sat)
continue;
// For each register bit, try to prove that it cannot change from the initial value. If so, remove it
if (!modwalker.has_drivers(ff.sig_d.extract(i)))
continue;
if (val != State::S0 && val != State::S1)
continue;
int init_sat_pi = qcsat.importSigBit(val);
int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]);
qcsat.prepare();
// Try to find out whether the register bit can change under some circumstances
bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
// If the register bit cannot change, we can replace it with a constant
if (counter_example_found)
continue;
}
}
if (ff.has_aload) {
if (!ff.sig_ad[i].wire) {
val = combine_const(val, ff.sig_ad[i].data);
if (val == State::Sm)
continue;
} else {
if (!opt.sat)
continue;
// For each register bit, try to prove that it cannot change from the initial value. If so, remove it
if (!modwalker.has_drivers(ff.sig_ad.extract(i)))
continue;
if (val != State::S0 && val != State::S1)
continue;
int init_sat_pi = qcsat.importSigBit(val);
int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]);
qcsat.prepare();
// Try to find out whether the register bit can change under some circumstances
bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
// If the register bit cannot change, we can replace it with a constant
if (counter_example_found)
continue;
}
}
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
i, log_id(cell), log_id(cell->type), log_id(module));
initvals.remove_init(ff.sig_q[i]);
module->connect(ff.sig_q[i], val);
removed_sigbits.insert(i);
}
if (!removed_sigbits.empty()) {
std::vector<int> keep_bits;
for (int i = 0; i < ff.width; i++)
if (!removed_sigbits.count(i))
keep_bits.push_back(i);
if (keep_bits.empty()) {
module->remove(cell);
did_something = true;
continue;
}
ff = ff.slice(keep_bits);
ff.cell = cell;
ff.emit();
did_something = true;
}
}
@ -865,6 +906,8 @@ struct OptDffPass : public Pass {
OptDffWorker worker(opt, mod);
if (worker.run())
did_something = true;
if (worker.run_constbits())
did_something = true;
}
if (did_something)

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -393,34 +393,8 @@ int get_highest_hot_index(RTLIL::SigSpec signal)
return -1;
}
// if the signal has only one bit set, return the index of that bit.
// otherwise return -1
int get_onehot_bit_index(RTLIL::SigSpec signal)
{
int bit_index = -1;
for (int i = 0; i < GetSize(signal); i++)
{
if (signal[i] == RTLIL::State::S0)
continue;
if (signal[i] != RTLIL::State::S1)
return -1;
if (bit_index != -1)
return -1;
bit_index = i;
}
return bit_index;
}
void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv)
{
if (!design->selected(module))
return;
CellTypes ct_combinational;
ct_combinational.setup_internals();
ct_combinational.setup_stdcells();
@ -467,7 +441,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (!noclkinv)
{
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr)))
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2)))
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) {
@ -478,10 +452,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($adff), ID($adffe), ID($adlatch)))
handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map);
if (cell->type.in(ID($aldff), ID($aldffe)))
handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map);
if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce)))
handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map);
if (cell->type.in(ID($dffe), ID($adffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
@ -510,6 +487,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map);
@ -1526,14 +1510,12 @@ skip_identity:
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
if (sig_b.is_fully_const() && sig_b.size() <= 32)
if (sig_b.is_fully_const())
std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true;
if (sig_a.is_fully_def() && sig_a.size() <= 32)
if (sig_a.is_fully_def())
{
int a_val = sig_a.as_int();
if (a_val == 0)
if (sig_a.is_fully_zero())
{
cover("opt.opt_expr.mul_shift.zero");
@ -1547,37 +1529,34 @@ skip_identity:
goto next_cell;
}
for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++)
if (a_val == (1 << i))
{
if (swapped_ab)
cover("opt.opt_expr.mul_shift.swapped");
else
cover("opt.opt_expr.mul_shift.unswapped");
int exp;
if (sig_a.is_onehot(&exp) && !(a_signed && exp == GetSize(sig_a) - 1))
{
if (swapped_ab)
cover("opt.opt_expr.mul_shift.swapped");
else
cover("opt.opt_expr.mul_shift.unswapped");
log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
a_val, cell->name.c_str(), module->name.c_str(), i);
log_debug("Replacing multiply-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
log_signal(sig_a), cell->name.c_str(), module->name.c_str(), exp);
if (!swapped_ab) {
cell->setPort(ID::A, cell->getPort(ID::B));
cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH);
cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED);
}
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
new_b.pop_back();
cell->type = ID($shl);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->parameters[ID::B_SIGNED] = false;
cell->setPort(ID::B, new_b);
cell->check();
did_something = true;
goto next_cell;
if (!swapped_ab) {
cell->setPort(ID::A, cell->getPort(ID::B));
cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH);
cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED);
}
Const new_b = exp;
cell->type = ID($shl);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->parameters[ID::B_SIGNED] = false;
cell->setPort(ID::B, new_b);
cell->check();
did_something = true;
goto next_cell;
}
}
sig_a = assign_map(cell->getPort(ID::A));
@ -1622,7 +1601,7 @@ skip_identity:
}
}
if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
{
bool a_signed = cell->parameters[ID::A_SIGNED].as_bool();
bool b_signed = cell->parameters[ID::B_SIGNED].as_bool();
@ -1630,11 +1609,9 @@ skip_identity:
SigSpec sig_b = assign_map(cell->getPort(ID::B));
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
if (sig_b.is_fully_def() && sig_b.size() <= 32)
if (sig_b.is_fully_def())
{
int b_val = sig_b.as_int();
if (b_val == 0)
if (sig_b.is_fully_zero())
{
cover("opt.opt_expr.divmod_zero");
@ -1648,86 +1625,79 @@ skip_identity:
goto next_cell;
}
for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
if (b_val == (1 << i))
int exp;
if (!keepdc && sig_b.is_onehot(&exp) && !(b_signed && exp == GetSize(sig_b) - 1))
{
if (cell->type.in(ID($div), ID($divfloor)))
{
if (cell->type.in(ID($div), ID($divfloor)))
{
cover("opt.opt_expr.div_shift");
cover("opt.opt_expr.div_shift");
bool is_truncating = cell->type == ID($div);
log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
is_truncating ? "truncating" : "flooring",
b_val, cell->name.c_str(), module->name.c_str(), i);
bool is_truncating = cell->type == ID($div);
log_debug("Replacing %s-divide-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
is_truncating ? "truncating" : "flooring",
log_signal(sig_b), cell->name.c_str(), module->name.c_str(), exp);
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
Const new_b = exp;
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
new_b.pop_back();
cell->type = ID($sshr);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->parameters[ID::B_SIGNED] = false;
cell->setPort(ID::B, new_b);
cell->type = ID($sshr);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->parameters[ID::B_SIGNED] = false;
cell->setPort(ID::B, new_b);
// Truncating division is the same as flooring division, except when
// the result is negative and there is a remainder - then trunc = floor + 1
if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) {
Wire *flooring = module->addWire(NEW_ID, sig_y.size());
cell->setPort(ID::Y, flooring);
// Truncating division is the same as flooring division, except when
// the result is negative and there is a remainder - then trunc = floor + 1
if (is_truncating && a_signed) {
Wire *flooring = module->addWire(NEW_ID, sig_y.size());
cell->setPort(ID::Y, flooring);
Wire *result_neg = module->addWire(NEW_ID);
module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg);
Wire *rem_nonzero = module->addWire(NEW_ID);
module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero);
Wire *should_add = module->addWire(NEW_ID);
module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add);
module->addAdd(NEW_ID, flooring, should_add, sig_y);
}
cell->check();
SigSpec a_sign = sig_a[sig_a.size()-1];
SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp));
SigSpec should_add = module->And(NEW_ID, a_sign, rem_nonzero);
module->addAdd(NEW_ID, flooring, should_add, sig_y);
}
else if (cell->type.in(ID($mod), ID($modfloor)))
cell->check();
}
else if (cell->type.in(ID($mod), ID($modfloor)))
{
cover("opt.opt_expr.mod_mask");
bool is_truncating = cell->type == ID($mod);
log_debug("Replacing %s-modulo-by-%s cell `%s' in module `%s' with bitmask.\n",
is_truncating ? "truncating" : "flooring",
log_signal(sig_b), cell->name.c_str(), module->name.c_str());
// truncating modulo has the same masked bits as flooring modulo, but
// the sign bits are those of A (except when R=0)
if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0)
{
cover("opt.opt_expr.mod_mask");
module->remove(cell);
SigSpec truncating = sig_a.extract(0, exp);
bool is_truncating = cell->type == ID($mod);
log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
is_truncating ? "truncating" : "flooring",
b_val, cell->name.c_str(), module->name.c_str());
SigSpec a_sign = sig_a[sig_a.size()-1];
SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp));
SigSpec extend_bit = module->And(NEW_ID, a_sign, rem_nonzero);
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
truncating.append(extend_bit);
module->addPos(NEW_ID, truncating, sig_y, true);
}
else
{
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, exp);
if (b_signed)
if (b_signed || exp == 0)
new_b.push_back(State::S0);
cell->type = ID($and);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->setPort(ID::B, new_b);
// truncating modulo has the same masked bits as flooring modulo, but
// the sign bits are those of A (except when R=0)
if (is_truncating && a_signed) {
Wire *flooring = module->addWire(NEW_ID, sig_y.size());
cell->setPort(ID::Y, flooring);
SigSpec truncating = SigSpec(flooring).extract(0, i);
Wire *rem_nonzero = module->addWire(NEW_ID);
module->addReduceOr(NEW_ID, truncating, rem_nonzero);
SigSpec a_sign = sig_a[sig_a.size()-1];
Wire *extend_bit = module->addWire(NEW_ID);
module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit);
truncating.append(extend_bit);
module->addPos(NEW_ID, truncating, sig_y, true);
}
cell->check();
}
did_something = true;
goto next_cell;
}
did_something = true;
goto next_cell;
}
}
}
@ -1957,8 +1927,8 @@ skip_alu_split:
replace = true;
}
int const_bit_hot = get_onehot_bit_index(const_sig);
if (const_bit_hot >= 0 && const_bit_hot < var_width)
int const_bit_hot;
if (const_sig.is_onehot(&const_bit_hot) && const_bit_hot < var_width)
{
RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
for (int i = const_bit_hot; i < var_width; i++) {
@ -2044,6 +2014,23 @@ skip_alu_split:
}
}
void replace_const_connections(RTLIL::Module *module) {
SigMap assign_map(module);
for (auto cell : module->selected_cells())
{
std::vector<std::pair<RTLIL::IdString, SigSpec>> changes;
for (auto &conn : cell->connections()) {
SigSpec mapped = assign_map(conn.second);
if (conn.second != mapped && mapped.is_fully_const())
changes.push_back({conn.first, mapped});
}
if (!changes.empty())
did_something = true;
for (auto &it : changes)
cell->setPort(it.first, it.second);
}
}
struct OptExprPass : public Pass {
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
void help() override
@ -2154,6 +2141,11 @@ struct OptExprPass : public Pass {
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
did_something = false;
replace_const_connections(module);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
log_suppressed();
}

View file

@ -24,16 +24,22 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct dlogic_t {
IdString cell_type;
// LUT input idx -> hard cell's port name
dict<int, IdString> lut_input_port;
};
struct OptLutWorker
{
dict<IdString, dict<int, IdString>> &dlogic;
const std::vector<dlogic_t> &dlogic;
RTLIL::Module *module;
ModIndex index;
SigMap sigmap;
pool<RTLIL::Cell*> luts;
dict<RTLIL::Cell*, int> luts_arity;
dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
dict<RTLIL::Cell*, pool<std::pair<int, RTLIL::Cell*>>> luts_dlogics;
dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
int eliminated_count = 0, combined_count = 0;
@ -64,7 +70,7 @@ struct OptLutWorker
void show_stats_by_arity()
{
dict<int, int> arity_counts;
dict<IdString, int> dlogic_counts;
std::vector<int> dlogic_counts(dlogic.size());
int max_arity = 0;
for (auto lut_arity : luts_arity)
@ -77,7 +83,7 @@ struct OptLutWorker
{
for (auto &lut_dlogic : lut_dlogics.second)
{
dlogic_counts[lut_dlogic->type]++;
dlogic_counts[lut_dlogic.first]++;
}
}
@ -87,13 +93,13 @@ struct OptLutWorker
if (arity_counts[arity])
log(" %d-LUT %16d\n", arity, arity_counts[arity]);
}
for (auto &dlogic_count : dlogic_counts)
for (int i = 0; i < GetSize(dlogic); i++)
{
log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
log(" with %-12s (#%d) %4d\n", dlogic[i].cell_type.c_str(), i, dlogic_counts[i]);
}
}
OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
OptLutWorker(const std::vector<dlogic_t> &dlogic, RTLIL::Module *module, int limit) :
dlogic(dlogic), module(module), index(module), sigmap(module)
{
log("Discovering LUTs.\n");
@ -116,20 +122,19 @@ struct OptLutWorker
// First, find all dedicated logic we're connected to. This results in an overapproximation
// of such connections.
pool<RTLIL::Cell*> lut_all_dlogics;
pool<std::pair<int, RTLIL::Cell*>> lut_all_dlogics;
for (int i = 0; i < lut_width; i++)
{
SigBit bit = lut_input[i];
for (auto &port : index.query_ports(bit))
{
if (dlogic.count(port.cell->type))
for (int j = 0; j < GetSize(dlogic); j++)
{
auto &dlogic_map = dlogic[port.cell->type];
if (dlogic_map.count(i))
if (dlogic[j].cell_type == port.cell->type)
{
if (port.port == dlogic_map[i])
if (port.port == dlogic[j].lut_input_port.at(i, IdString()))
{
lut_all_dlogics.insert(port.cell);
lut_all_dlogics.insert({j, port.cell});
}
}
}
@ -143,25 +148,25 @@ struct OptLutWorker
// * The connection is illegal.
// In either of these cases, we don't need to concern ourselves with preserving the connection
// between this LUT and this dedicated logic cell.
pool<RTLIL::Cell*> lut_legal_dlogics;
pool<std::pair<int, RTLIL::Cell*>> lut_legal_dlogics;
pool<int> lut_dlogic_inputs;
for (auto lut_dlogic : lut_all_dlogics)
{
auto &dlogic_map = dlogic[lut_dlogic->type];
auto &dlogic_map = dlogic[lut_dlogic.first].lut_input_port;
bool legal = true;
for (auto &dlogic_conn : dlogic_map)
{
if (lut_width <= dlogic_conn.first)
{
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first);
legal = false;
break;
}
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)))
{
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
legal = false;
break;
}
@ -169,7 +174,7 @@ struct OptLutWorker
if (legal)
{
log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
lut_legal_dlogics.insert(lut_dlogic);
for (auto &dlogic_conn : dlogic_map)
lut_dlogic_inputs.insert(dlogic_conn.first);
@ -544,7 +549,7 @@ struct OptLutPass : public Pass {
{
log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
dict<IdString, dict<int, IdString>> dlogic;
std::vector<dlogic_t> dlogic;
int limit = -1;
size_t argidx;
@ -556,7 +561,8 @@ struct OptLutPass : public Pass {
split(tokens, args[++argidx], ':');
if (tokens.size() < 2)
log_cmd_error("The -dlogic option requires at least one connection.\n");
IdString type = "\\" + tokens[0];
dlogic_t entry;
entry.cell_type = "\\" + tokens[0];
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
std::vector<std::string> conn_tokens;
split(conn_tokens, *it, '=');
@ -564,8 +570,9 @@ struct OptLutPass : public Pass {
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
IdString logic_port = "\\" + conn_tokens[0];
int lut_input = atoi(conn_tokens[1].c_str());
dlogic[type][lut_input] = logic_port;
entry.lut_input_port[lut_input] = logic_port;
}
dlogic.push_back(entry);
continue;
}
if (args[argidx] == "-limit" && argidx + 1 < args.size())

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -193,6 +193,12 @@ struct OptLutInsPass : public Pass {
swz += extra;
}
}
if (techname == "gowin") {
// Pad the LUT to 1 input, adding consts from the front.
if (new_inputs.empty()) {
new_inputs.insert(new_inputs.begin(), State::S0);
}
}
Const new_lut(0, 1 << GetSize(new_inputs));
for (int i = 0; i < GetSize(new_lut); i++) {
int lidx = 0;
@ -209,9 +215,9 @@ struct OptLutInsPass : public Pass {
}
new_lut[i] = lut[lidx];
}
// For ecp5, do not replace with a const driver — the nextpnr
// For ecp5, and gowin do not replace with a const driver — the nextpnr
// packer requires a complete set of LUTs for wide LUT muxes.
if (new_inputs.empty() && techname != "ecp5") {
if (new_inputs.empty() && techname != "ecp5" && techname != "gowin") {
// const driver.
remove_cells.push_back(cell);
module->connect(output, new_lut[0]);

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -51,8 +51,32 @@ struct OptMemPass : public Pass {
int total_count = 0;
for (auto module : design->selected_modules()) {
SigMap sigmap(module);
FfInitVals initvals(&sigmap, module);
for (auto &mem : Mem::get_selected_memories(module)) {
bool changed = false;
for (auto &port : mem.wr_ports) {
if (port.en.is_fully_zero()) {
port.removed = true;
changed = true;
total_count++;
}
}
if (changed) {
mem.emit();
}
if (mem.wr_ports.empty() && mem.inits.empty()) {
// The whole memory array will contain
// only State::Sx, but the embedded read
// registers could have reset or init values.
// They will probably be optimized away by
// opt_dff later.
for (int i = 0; i < GetSize(mem.rd_ports); i++) {
mem.extract_rdff(i, &initvals);
auto &port = mem.rd_ports[i];
module->connect(port.data, Const(State::Sx, GetSize(port.data)));
}
mem.remove();
total_count++;
}

View file

@ -0,0 +1,350 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
// Describes found feedback path.
struct FeedbackPath {
// Which write port it is.
int wrport_idx;
// Which data bit of that write port it is.
int data_bit_idx;
// Values of all mux select signals that need to be set to select this path.
dict<RTLIL::SigBit, bool> condition;
// The exact feedback bit used (used to match read port).
SigBit feedback_bit;
FeedbackPath(int wrport_idx, int data_bit_idx, dict<RTLIL::SigBit, bool> condition, SigBit feedback_bit) : wrport_idx(wrport_idx), data_bit_idx(data_bit_idx), condition(condition), feedback_bit(feedback_bit) {}
};
struct OptMemFeedbackWorker
{
RTLIL::Design *design;
RTLIL::Module *module;
SigMap sigmap, sigmap_xmux;
FfInitVals initvals;
dict<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux;
dict<RTLIL::SigBit, int> sig_users_count;
dict<pair<pool<dict<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache;
// -----------------------------------------------------------------
// Converting feedbacks to async read ports to proper enable signals
// -----------------------------------------------------------------
void find_data_feedback(const pool<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig,
const dict<RTLIL::SigBit, bool> &state,
int wrport_idx, int data_bit_idx,
std::vector<FeedbackPath> &paths)
{
if (async_rd_bits.count(sig)) {
paths.push_back(FeedbackPath(wrport_idx, data_bit_idx, state, sig));
return;
}
if (sig_users_count[sig] != 1) {
// Only descend into muxes if we're the only user.
return;
}
if (sig_to_mux.count(sig) == 0)
return;
RTLIL::Cell *cell = sig_to_mux.at(sig).first;
int bit_idx = sig_to_mux.at(sig).second;
std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort(ID::A));
std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort(ID::B));
std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort(ID::S));
std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y));
log_assert(sig_y.at(bit_idx) == sig);
for (int i = 0; i < GetSize(sig_s); i++)
if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) {
find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, wrport_idx, data_bit_idx, paths);
return;
}
for (int i = 0; i < GetSize(sig_s); i++)
{
if (state.count(sig_s[i]) && state.at(sig_s[i]) == false)
continue;
dict<RTLIL::SigBit, bool> new_state = state;
new_state[sig_s[i]] = true;
find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, wrport_idx, data_bit_idx, paths);
}
dict<RTLIL::SigBit, bool> new_state = state;
for (auto bit : sig_s)
new_state[bit] = false;
find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, wrport_idx, data_bit_idx, paths);
}
RTLIL::SigBit conditions_to_logic(pool<dict<RTLIL::SigBit, bool>> &conditions, SigBit olden)
{
auto key = make_pair(conditions, olden);
if (conditions_logic_cache.count(key))
return conditions_logic_cache.at(key);
RTLIL::SigSpec terms;
for (auto &cond : conditions) {
RTLIL::SigSpec sig1, sig2;
for (auto &it : cond) {
sig1.append(it.first);
sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0);
}
terms.append(module->Ne(NEW_ID, sig1, sig2));
}
if (olden != State::S1)
terms.append(olden);
if (GetSize(terms) == 0)
terms = State::S1;
if (GetSize(terms) > 1)
terms = module->ReduceAnd(NEW_ID, terms);
return conditions_logic_cache[key] = terms;
}
void translate_rd_feedback_to_en(Mem &mem)
{
// Look for async read ports that may be suitable for feedback paths.
dict<RTLIL::SigSpec, std::vector<pool<RTLIL::SigBit>>> async_rd_bits;
for (auto &port : mem.rd_ports)
{
if (port.clk_enable)
continue;
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
SigSpec addr = sigmap_xmux(port.sub_addr(sub));
async_rd_bits[addr].resize(mem.width);
for (int i = 0; i < mem.width; i++)
async_rd_bits[addr][i].insert(sigmap(port.data[i + sub * mem.width]));
}
}
if (async_rd_bits.empty())
return;
// Look for actual feedback paths.
std::vector<FeedbackPath> paths;
for (int i = 0; i < GetSize(mem.wr_ports); i++)
{
auto &port = mem.wr_ports[i];
log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i);
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
{
SigSpec addr = sigmap_xmux(port.sub_addr(sub));
if (!async_rd_bits.count(addr))
continue;
for (int j = 0; j < mem.width; j++)
{
int bit_idx = sub * mem.width + j;
if (port.en[bit_idx] == State::S0)
continue;
dict<RTLIL::SigBit, bool> state;
find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[bit_idx]), state, i, bit_idx, paths);
}
}
}
if (paths.empty())
return;
// Now determine which read ports are actually used only for
// feedback paths, and can be removed.
dict<SigBit, int> feedback_users_count;
for (auto &path : paths)
feedback_users_count[path.feedback_bit]++;
pool<SigBit> feedback_ok;
for (auto &port : mem.rd_ports)
{
if (port.clk_enable)
continue;
bool ok = true;
for (auto bit : sigmap(port.data))
if (sig_users_count[bit] != feedback_users_count[bit])
ok = false;
if (ok)
{
// This port is going bye-bye.
for (auto bit : sigmap(port.data))
feedback_ok.insert(bit);
port.removed = true;
}
}
if (feedback_ok.empty())
return;
// Prepare a feedback condition list grouped by port bits.
dict<std::pair<int, int>, pool<dict<SigBit, bool>>> portbit_conds;
for (auto &path : paths)
if (feedback_ok.count(path.feedback_bit))
portbit_conds[std::make_pair(path.wrport_idx, path.data_bit_idx)].insert(path.condition);
if (portbit_conds.empty())
return;
// Okay, let's do it.
log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid));
// If a write port has a feedback path that we're about to bypass,
// but also has priority over some other write port, the feedback
// path is not necessarily a NOP — it may overwrite the other port.
// Emulate this effect by converting the priority to soft logic
// (this will affect the other port's enable signal).
for (auto &it : portbit_conds)
{
int wrport_idx = it.first.first;
auto &port = mem.wr_ports[wrport_idx];
for (int i = 0; i < wrport_idx; i++)
if (port.priority_mask[i])
mem.emulate_priority(i, wrport_idx, &initvals);
}
for (auto &it : portbit_conds)
{
int wrport_idx = it.first.first;
int bit = it.first.second;
auto &port = mem.wr_ports[wrport_idx];
port.en[bit] = conditions_to_logic(it.second, port.en[bit]);
log(" Port %d bit %d: added enable logic for %d different cases.\n", wrport_idx, bit, GetSize(it.second));
}
mem.emit();
for (auto bit : feedback_ok)
module->connect(bit, State::Sx);
design->scratchpad_set_bool("opt.did_something", true);
}
// -------------
// Setup and run
// -------------
OptMemFeedbackWorker(RTLIL::Design *design) : design(design) {}
void operator()(RTLIL::Module* module)
{
std::vector<Mem> memories = Mem::get_selected_memories(module);
this->module = module;
sigmap.set(module);
initvals.set(&sigmap, module);
sig_to_mux.clear();
conditions_logic_cache.clear();
sigmap_xmux = sigmap;
for (auto wire : module->wires()) {
if (wire->port_output)
for (auto bit : sigmap(wire))
sig_users_count[bit]++;
}
for (auto cell : module->cells())
{
if (cell->type == ID($mux))
{
RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort(ID::B));
if (sig_a.is_fully_undef())
sigmap_xmux.add(cell->getPort(ID::Y), sig_b);
else if (sig_b.is_fully_undef())
sigmap_xmux.add(cell->getPort(ID::Y), sig_a);
}
if (cell->type.in(ID($mux), ID($pmux)))
{
std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y));
for (int i = 0; i < int(sig_y.size()); i++)
sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i);
}
for (auto &conn : cell->connections())
if (!cell->known() || cell->input(conn.first))
for (auto bit : sigmap(conn.second))
sig_users_count[bit]++;
}
for (auto &mem : memories)
translate_rd_feedback_to_en(mem);
}
};
struct OptMemFeedbackPass : public Pass {
OptMemFeedbackPass() : Pass("opt_mem_feedback", "convert memory read-to-write port feedback paths to write enables") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_mem_feedback [selection]\n");
log("\n");
log("This pass detects cases where an asynchronous read port is only connected via\n");
log("a mux tree to a write port with the same address. When such a connection is\n");
log("found, it is replaced with a new condition on an enable signal, allowing\n");
log("for removal of the read port.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing OPT_MEM_FEEDBACK pass (finding memory read-to-write feedback paths).\n");
extra_args(args, 1, design);
OptMemFeedbackWorker worker(design);
for (auto module : design->selected_modules())
worker(module);
}
} OptMemFeedbackPass;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,109 @@
/*
* 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/modtools.h"
#include "kernel/qcsat.h"
#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct OptMemPriorityPass : public Pass {
OptMemPriorityPass() : Pass("opt_mem_priority", "remove priority relations between write ports that can never collide") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_mem_priority [selection]\n");
log("\n");
log("This pass detects cases where one memory write port has priority over another\n");
log("even though they can never collide with each other -- ie. there can never be\n");
log("a situation where a given memory bit is written by both ports at the same\n");
log("time, for example because of always-different addresses, or mutually exclusive\n");
log("enable signals. In such cases, the priority relation is removed.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing OPT_MEM_PRIORITY pass (removing unnecessary memory write priority relations).\n");
extra_args(args, 1, design);
ModWalker modwalker(design);
int total_count = 0;
for (auto module : design->selected_modules()) {
modwalker.setup(module);
for (auto &mem : Mem::get_selected_memories(module)) {
bool mem_changed = false;
QuickConeSat qcsat(modwalker);
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
auto &wport1 = mem.wr_ports[i];
for (int j = 0; j < GetSize(mem.wr_ports); j++) {
auto &wport2 = mem.wr_ports[j];
if (!wport1.priority_mask[j])
continue;
// No mixed width support — we could do it, but
// that would complicate code and wouldn't help
// anything since we run this pass before
// wide ports are created in normal flow.
if (wport1.wide_log2 != wport2.wide_log2)
continue;
// Two ports with priority, let's go.
pool<std::pair<SigBit, SigBit>> checked;
SigSpec addr1 = wport1.addr;
SigSpec addr2 = wport2.addr;
int abits = std::max(GetSize(addr1), GetSize(addr2));
addr1.extend_u0(abits);
addr2.extend_u0(abits);
int addr_eq = qcsat.ez->vec_eq(qcsat.importSig(addr1), qcsat.importSig(addr2));
bool ok = true;
for (int k = 0; k < GetSize(wport1.data); k++) {
SigBit wen1 = wport1.en[k];
SigBit wen2 = wport2.en[k];
if (checked.count({wen1, wen2}))
continue;
int wen1_sat = qcsat.importSigBit(wen1);
int wen2_sat = qcsat.importSigBit(wen2);
qcsat.prepare();
if (qcsat.ez->solve(wen1_sat, wen2_sat, addr_eq)) {
ok = false;
break;
}
checked.insert({wen1, wen2});
}
if (ok) {
total_count++;
mem_changed = true;
wport1.priority_mask[j] = false;
}
}
}
if (mem_changed)
mem.emit();
}
}
if (total_count)
design->scratchpad_set_bool("opt.did_something", true);
log("Performed a total of %d transformations.\n", total_count);
}
} OptMemPriorityPass;
PRIVATE_NAMESPACE_END

107
passes/opt/opt_mem_widen.cc Normal file
View file

@ -0,0 +1,107 @@
/*
* 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/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct OptMemWidenPass : public Pass {
OptMemWidenPass() : Pass("opt_mem_widen", "optimize memories where all ports are wide") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_mem_widen [options] [selection]\n");
log("\n");
log("This pass looks for memories where all ports are wide and adjusts the base\n");
log("memory width up until that stops being the case.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_MEM_WIDEN pass (optimize memories where all ports are wide).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
// if (args[argidx] == "-nomux") {
// mode_nomux = true;
// continue;
// }
break;
}
extra_args(args, argidx, design);
int total_count = 0;
for (auto module : design->selected_modules()) {
for (auto &mem : Mem::get_selected_memories(module)) {
// If the memory has no read ports, opt_clean will remove it
// instead.
if (mem.rd_ports.empty())
continue;
int factor_log2 = mem.rd_ports[0].wide_log2;
for (auto &port : mem.rd_ports)
if (port.wide_log2 < factor_log2)
factor_log2 = port.wide_log2;
for (auto &port : mem.wr_ports)
if (port.wide_log2 < factor_log2)
factor_log2 = port.wide_log2;
if (factor_log2 == 0)
continue;
log("Widening base width of memory %s in module %s by factor %d.\n", log_id(mem.memid), log_id(module->name), 1 << factor_log2);
total_count++;
// The inits are too messy to expand one-by-one, for they may
// collide with one another after expansion. Just hit it with
// a hammer.
bool has_init = !mem.inits.empty();
Const init_data;
if (has_init) {
init_data = mem.get_init_data();
mem.clear_inits();
}
mem.width <<= factor_log2;
mem.size >>= factor_log2;
mem.start_offset >>= factor_log2;
if (has_init) {
MemInit new_init;
new_init.addr = mem.start_offset;
new_init.data = init_data;
new_init.en = Const(State::S1, mem.width);
mem.inits.push_back(new_init);
}
for (auto &port : mem.rd_ports) {
port.wide_log2 -= factor_log2;
port.addr = port.addr.extract_end(factor_log2);
}
for (auto &port : mem.wr_ports) {
port.wide_log2 -= factor_log2;
port.addr = port.addr.extract_end(factor_log2);
}
mem.emit();
}
}
if (total_count)
design->scratchpad_set_bool("opt.did_something", true);
log("Performed a total of %d transformations.\n", total_count);
}
} OptMemWidenPass;
PRIVATE_NAMESPACE_END

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -18,6 +18,7 @@
*/
#include "kernel/register.h"
#include "kernel/ffinit.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
@ -35,7 +36,7 @@ struct OptMergeWorker
RTLIL::Design *design;
RTLIL::Module *module;
SigMap assign_map;
SigMap dff_init_map;
FfInitVals initvals;
bool mode_share_all;
CellTypes ct;
@ -121,8 +122,7 @@ struct OptMergeWorker
if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) {
// For the 'Q' output of state elements,
// use its (* init *) attribute value
for (const auto &b : dff_init_map(it.second))
sig.append(b.wire ? State::Sx : b);
sig = initvals(it.second);
}
else
continue;
@ -176,12 +176,8 @@ struct OptMergeWorker
if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell1->type)) {
// For the 'Q' output of state elements,
// use the (* init *) attribute value
auto &sig1 = conn1[it.first];
for (const auto &b : dff_init_map(it.second))
sig1.append(b.wire ? State::Sx : b);
auto &sig2 = conn2[it.first];
for (const auto &b : dff_init_map(cell2->getPort(it.first)))
sig2.append(b.wire ? State::Sx : b);
conn1[it.first] = initvals(it.second);
conn2[it.first] = initvals(cell2->getPort(it.first));
}
else {
conn1[it.first] = RTLIL::SigSpec();
@ -247,14 +243,7 @@ struct OptMergeWorker
log("Finding identical cells in module `%s'.\n", module->name.c_str());
assign_map.set(module);
dff_init_map.set(module);
for (auto &it : module->wires_)
if (it.second->attributes.count(ID::init) != 0) {
Const initval = it.second->attributes.at(ID::init);
for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
dff_init_map.add(SigBit(it.second, i), initval[i]);
}
initvals.set(&assign_map, module);
bool did_something = true;
while (did_something)
@ -293,19 +282,12 @@ struct OptMergeWorker
RTLIL::SigSpec other_sig = r.first->second->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig));
Const init = initvals(other_sig);
initvals.remove_init(it.second);
initvals.remove_init(other_sig);
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) {
for (auto c : it.second.chunks()) {
auto jt = c.wire->attributes.find(ID::init);
if (jt == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset + c.width; i++)
jt->second[i] = State::Sx;
}
dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second)));
}
initvals.set_init(other_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -372,28 +372,27 @@ struct OptMuxtreeWorker
int port_idx = 0, port_off = 0;
vector<int> bits = sig2bits(sig, false);
for (int i = 0; i < GetSize(bits); i++) {
if (bits[i] < 0)
continue;
if (knowledge.known_inactive.at(bits[i])) {
sig[i] = State::S0;
did_something = true;
} else
if (knowledge.known_active.at(bits[i])) {
sig[i] = State::S1;
did_something = true;
}
if (width) {
if (ctrl_bits.count(bits[i])) {
sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0;
did_something = true;
}
if (++port_off == width)
port_idx++, port_off=0;
} else {
if (ctrl_bits.count(bits[i])) {
if (bits[i] >= 0) {
if (knowledge.known_inactive.at(bits[i])) {
sig[i] = State::S0;
did_something = true;
} else
if (knowledge.known_active.at(bits[i])) {
sig[i] = State::S1;
did_something = true;
}
if (ctrl_bits.count(bits[i])) {
if (width) {
sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0;
} else {
sig[i] = State::S0;
}
did_something = true;
}
}
if (width) {
if (++port_off == width)
port_idx++, port_off=0;
}
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -100,7 +100,7 @@ struct OptReduceWorker
return;
}
void opt_mux(RTLIL::Cell *cell)
void opt_pmux(RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
@ -141,20 +141,20 @@ struct OptReduceWorker
handled_sig.insert(this_b);
}
if (new_sig_s.size() != sig_s.size()) {
if (new_sig_s.size() == 0)
{
module->connect(cell->getPort(ID::Y), cell->getPort(ID::A));
assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A));
module->remove(cell);
did_something = true;
total_count++;
return;
}
if (new_sig_s.size() != sig_s.size() || (new_sig_s.size() == 1 && cell->type == ID($pmux))) {
log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
did_something = true;
total_count++;
}
if (new_sig_s.size() == 0)
{
module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A)));
assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A));
module->remove(cell);
}
else
{
cell->setPort(ID::B, new_sig_b);
cell->setPort(ID::S, new_sig_s);
if (new_sig_s.size() > 1) {
@ -166,81 +166,347 @@ struct OptReduceWorker
}
}
void opt_mux_bits(RTLIL::Cell *cell)
void opt_bmux(RTLIL::Cell *cell)
{
std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector();
std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector();
std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector();
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
int width = cell->getParam(ID::WIDTH).as_int();
RTLIL::SigSpec new_sig_a, new_sig_s;
dict<RTLIL::SigBit, int> handled_bits;
// 0 and up: index of new_sig_s bit
// -1: const 0
// -2: const 1
std::vector<int> swizzle;
for (int i = 0; i < sig_s.size(); i++)
{
SigBit bit = sig_s[i];
if (bit == State::S0) {
swizzle.push_back(-1);
} else if (bit == State::S1) {
swizzle.push_back(-2);
} else {
auto it = handled_bits.find(bit);
if (it == handled_bits.end()) {
int new_idx = GetSize(new_sig_s);
new_sig_s.append(bit);
handled_bits[bit] = new_idx;
swizzle.push_back(new_idx);
} else {
swizzle.push_back(it->second);
}
}
}
for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
int idx = 0;
for (int j = 0; j < GetSize(sig_s); j++) {
if (swizzle[j] == -1) {
// const 0.
} else if (swizzle[j] == -2) {
// const 1.
idx |= 1 << j;
} else {
if (i & 1 << swizzle[j])
idx |= 1 << j;
}
}
new_sig_a.append(sig_a.extract(idx * width, width));
}
if (new_sig_s.size() == 0)
{
module->connect(cell->getPort(ID::Y), new_sig_a);
assign_map.add(cell->getPort(ID::Y), new_sig_a);
module->remove(cell);
did_something = true;
total_count++;
return;
}
if (new_sig_s.size() == 1)
{
cell->type = ID($mux);
cell->setPort(ID::A, new_sig_a.extract(0, width));
cell->setPort(ID::B, new_sig_a.extract(width, width));
cell->setPort(ID::S, new_sig_s);
cell->parameters.erase(ID::S_WIDTH);
did_something = true;
total_count++;
return;
}
if (new_sig_s.size() != sig_s.size()) {
log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
did_something = true;
total_count++;
cell->setPort(ID::A, new_sig_a);
cell->setPort(ID::S, new_sig_s);
cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
}
}
void opt_demux(RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
int width = cell->getParam(ID::WIDTH).as_int();
RTLIL::SigSpec new_sig_y, new_sig_s;
dict<RTLIL::SigBit, int> handled_bits;
// 0 and up: index of new_sig_s bit
// -1: const 0
// -2: const 1
std::vector<int> swizzle;
for (int i = 0; i < sig_s.size(); i++)
{
SigBit bit = sig_s[i];
if (bit == State::S0) {
swizzle.push_back(-1);
} else if (bit == State::S1) {
swizzle.push_back(-2);
} else {
auto it = handled_bits.find(bit);
if (it == handled_bits.end()) {
int new_idx = GetSize(new_sig_s);
new_sig_s.append(bit);
handled_bits[bit] = new_idx;
swizzle.push_back(new_idx);
} else {
swizzle.push_back(it->second);
}
}
}
pool<int> nonzero_idx;
for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
int idx = 0;
for (int j = 0; j < GetSize(sig_s); j++) {
if (swizzle[j] == -1) {
// const 0.
} else if (swizzle[j] == -2) {
// const 1.
idx |= 1 << j;
} else {
if (i & 1 << swizzle[j])
idx |= 1 << j;
}
}
log_assert(!nonzero_idx.count(idx));
nonzero_idx.insert(idx);
new_sig_y.append(sig_y.extract(idx * width, width));
}
if (new_sig_s.size() == sig_s.size() && sig_s.size() > 0)
return;
log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
did_something = true;
total_count++;
for (int i = 0; i < (1 << GetSize(sig_s)); i++) {
if (!nonzero_idx.count(i)) {
SigSpec slice = sig_y.extract(i * width, width);
module->connect(slice, Const(State::S0, width));
assign_map.add(slice, Const(State::S0, width));
}
}
if (new_sig_s.size() == 0)
{
module->connect(new_sig_y, cell->getPort(ID::A));
assign_map.add(new_sig_y, cell->getPort(ID::A));
module->remove(cell);
}
else
{
cell->setPort(ID::S, new_sig_s);
cell->setPort(ID::Y, new_sig_y);
cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
}
}
bool opt_mux_bits(RTLIL::Cell *cell)
{
SigSpec sig_a = assign_map(cell->getPort(ID::A));
SigSpec sig_b;
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
int width = GetSize(sig_y);
if (cell->type != ID($bmux))
sig_b = assign_map(cell->getPort(ID::B));
std::vector<RTLIL::SigBit> new_sig_y;
RTLIL::SigSig old_sig_conn;
std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples;
std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map;
dict<SigSpec, SigBit> consolidated_in_tuples;
std::vector<int> swizzle;
for (int i = 0; i < int(sig_y.size()); i++)
for (int i = 0; i < width; i++)
{
std::vector<RTLIL::SigBit> in_tuple;
SigSpec in_tuple;
bool all_tuple_bits_same = true;
in_tuple.push_back(sig_a.at(i));
for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) {
if (sig_b.at(j) != sig_a.at(i))
in_tuple.append(sig_a[i]);
for (int j = i; j < GetSize(sig_a); j += width) {
in_tuple.append(sig_a[j]);
if (sig_a[j] != in_tuple[0])
all_tuple_bits_same = false;
}
for (int j = i; j < GetSize(sig_b); j += width) {
in_tuple.append(sig_b[j]);
if (sig_b[j] != in_tuple[0])
all_tuple_bits_same = false;
in_tuple.push_back(sig_b.at(j));
}
if (all_tuple_bits_same)
{
old_sig_conn.first.append(sig_y.at(i));
old_sig_conn.second.append(sig_a.at(i));
old_sig_conn.first.append(sig_y[i]);
old_sig_conn.second.append(sig_a[i]);
continue;
}
else if (consolidated_in_tuples_map.count(in_tuple))
auto it = consolidated_in_tuples.find(in_tuple);
if (it == consolidated_in_tuples.end())
{
old_sig_conn.first.append(sig_y.at(i));
old_sig_conn.second.append(consolidated_in_tuples_map.at(in_tuple));
consolidated_in_tuples[in_tuple] = sig_y[i];
swizzle.push_back(i);
}
else
{
consolidated_in_tuples_map[in_tuple] = sig_y.at(i);
consolidated_in_tuples.push_back(in_tuple);
new_sig_y.push_back(sig_y.at(i));
old_sig_conn.first.append(sig_y[i]);
old_sig_conn.second.append(it->second);
}
}
if (new_sig_y.size() != sig_y.size())
if (GetSize(swizzle) != width)
{
log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
cell->setPort(ID::A, RTLIL::SigSpec());
for (auto &in_tuple : consolidated_in_tuples) {
RTLIL::SigSpec new_a = cell->getPort(ID::A);
new_a.append(in_tuple.at(0));
cell->setPort(ID::A, new_a);
if (cell->type != ID($bmux)) {
log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
} else {
log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::Y)));
}
cell->setPort(ID::B, RTLIL::SigSpec());
for (int i = 1; i <= cell->getPort(ID::S).size(); i++)
for (auto &in_tuple : consolidated_in_tuples) {
RTLIL::SigSpec new_b = cell->getPort(ID::B);
new_b.append(in_tuple.at(i));
cell->setPort(ID::B, new_b);
if (swizzle.empty()) {
module->remove(cell);
} else {
SigSpec new_sig_a;
for (int i = 0; i < GetSize(sig_a); i += width)
for (int j: swizzle)
new_sig_a.append(sig_a[i+j]);
cell->setPort(ID::A, new_sig_a);
if (cell->type != ID($bmux)) {
SigSpec new_sig_b;
for (int i = 0; i < GetSize(sig_b); i += width)
for (int j: swizzle)
new_sig_b.append(sig_b[i+j]);
cell->setPort(ID::B, new_sig_b);
}
cell->parameters[ID::WIDTH] = RTLIL::Const(new_sig_y.size());
cell->setPort(ID::Y, new_sig_y);
SigSpec new_sig_y;
for (int j: swizzle)
new_sig_y.append(sig_y[j]);
cell->setPort(ID::Y, new_sig_y);
cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
if (cell->type != ID($bmux)) {
log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
} else {
log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::Y)));
}
}
log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
did_something = true;
total_count++;
}
return swizzle.empty();
}
bool opt_demux_bits(RTLIL::Cell *cell) {
SigSpec sig_a = assign_map(cell->getPort(ID::A));
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
int width = GetSize(sig_a);
RTLIL::SigSig old_sig_conn;
dict<SigBit, int> handled_bits;
std::vector<int> swizzle;
for (int i = 0; i < width; i++)
{
if (sig_a[i] == State::S0)
{
for (int j = i; j < GetSize(sig_y); j += width)
{
old_sig_conn.first.append(sig_y[j]);
old_sig_conn.second.append(State::S0);
}
continue;
}
auto it = handled_bits.find(sig_a[i]);
if (it == handled_bits.end())
{
handled_bits[sig_a[i]] = i;
swizzle.push_back(i);
}
else
{
for (int j = 0; j < GetSize(sig_y); j += width)
{
old_sig_conn.first.append(sig_y[i+j]);
old_sig_conn.second.append(sig_y[it->second+j]);
}
}
}
if (GetSize(swizzle) != width)
{
log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::Y)));
if (swizzle.empty()) {
module->remove(cell);
} else {
SigSpec new_sig_a;
for (int j: swizzle)
new_sig_a.append(sig_a[j]);
cell->setPort(ID::A, new_sig_a);
SigSpec new_sig_y;
for (int i = 0; i < GetSize(sig_y); i += width)
for (int j: swizzle)
new_sig_y.append(sig_y[i+j]);
cell->setPort(ID::Y, new_sig_y);
cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
log_signal(cell->getPort(ID::Y)));
}
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
did_something = true;
total_count++;
}
return swizzle.empty();
}
OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) :
@ -254,9 +520,9 @@ struct OptReduceWorker
SigPool mem_wren_sigs;
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
if (cell->type == ID($mem))
if (cell->type.in(ID($mem), ID($mem_v2)))
mem_wren_sigs.add(assign_map(cell->getPort(ID::WR_EN)));
if (cell->type == ID($memwr))
if (cell->type.in(ID($memwr), ID($memwr_v2)))
mem_wren_sigs.add(assign_map(cell->getPort(ID::EN)));
}
for (auto &cell_it : module->cells_) {
@ -309,20 +575,31 @@ struct OptReduceWorker
// merge identical inputs on $mux and $pmux cells
std::vector<RTLIL::Cell*> cells;
for (auto &it : module->cells_)
if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second))
cells.push_back(it.second);
for (auto cell : cells)
for (auto cell : module->selected_cells())
{
if (!cell->type.in(ID($mux), ID($pmux), ID($bmux), ID($demux)))
continue;
// this optimization is to aggressive for most coarse-grain applications.
// but we always want it for multiplexers driving write enable ports.
if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y))))
opt_mux_bits(cell);
if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) {
if (cell->type == ID($demux)) {
if (opt_demux_bits(cell))
continue;
} else {
if (opt_mux_bits(cell))
continue;
}
}
opt_mux(cell);
if (cell->type.in(ID($mux), ID($pmux)))
opt_pmux(cell);
if (cell->type == ID($bmux))
opt_bmux(cell);
if (cell->type == ID($demux))
opt_demux(cell);
}
}

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/satgen.h"
#include "kernel/qcsat.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
#include "kernel/utils.h"
@ -58,8 +58,6 @@ struct ShareWorker
std::map<RTLIL::Cell*, std::set<RTLIL::Cell*, cell_ptr_cmp>, cell_ptr_cmp> topo_cell_drivers;
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*, cell_ptr_cmp>> topo_bit_drivers;
std::vector<std::pair<RTLIL::SigBit, RTLIL::SigBit>> exclusive_ctrls;
// ------------------------------------------------------------------------------
// Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree
@ -368,7 +366,7 @@ struct ShareWorker
continue;
}
if (cell->type == ID($memrd)) {
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
if (cell->parameters.at(ID::CLK_ENABLE).as_bool())
continue;
if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID::ADDR)).is_fully_const())
@ -401,11 +399,14 @@ struct ShareWorker
if (c1->type != c2->type)
return false;
if (c1->type == ID($memrd))
if (c1->type.in(ID($memrd), ID($memrd_v2)))
{
if (c1->parameters.at(ID::MEMID).decode_string() != c2->parameters.at(ID::MEMID).decode_string())
return false;
if (c1->parameters.at(ID::WIDTH) != c2->parameters.at(ID::WIDTH))
return false;
return true;
}
@ -705,7 +706,7 @@ struct ShareWorker
return supercell;
}
if (c1->type == ID($memrd))
if (c1->type.in(ID($memrd), ID($memrd_v2)))
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
RTLIL::SigSpec addr1 = c1->getPort(ID::ADDR);
@ -1156,7 +1157,6 @@ struct ShareWorker
recursion_state.clear();
topo_cell_drivers.clear();
topo_bit_drivers.clear();
exclusive_ctrls.clear();
terminal_bits.clear();
shareable_cells.clear();
forbidden_controls_cache.clear();
@ -1171,13 +1171,6 @@ struct ShareWorker
log("Found %d cells in module %s that may be considered for resource sharing.\n",
GetSize(shareable_cells), log_id(module));
for (auto cell : module->cells())
if (cell->type == ID($pmux))
for (auto bit : cell->getPort(ID::S))
for (auto other_bit : cell->getPort(ID::S))
if (bit < other_bit)
exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit));
while (!shareable_cells.empty() && config.limit != 0)
{
RTLIL::Cell *cell = *shareable_cells.begin();
@ -1256,8 +1249,11 @@ struct ShareWorker
optimize_activation_patterns(filtered_cell_activation_patterns);
optimize_activation_patterns(filtered_other_cell_activation_patterns);
ezSatPtr ez;
SatGen satgen(ez.get(), &modwalker.sigmap);
QuickConeSat qcsat(modwalker);
if (config.opt_fast) {
qcsat.max_cell_outs = 3;
qcsat.max_cell_count = 100;
}
pool<RTLIL::Cell*> sat_cells;
std::set<RTLIL::SigBit> bits_queue;
@ -1267,77 +1263,45 @@ struct ShareWorker
for (auto &p : filtered_cell_activation_patterns) {
log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second));
cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second)));
all_ctrl_signals.append(p.first);
}
for (auto &p : filtered_other_cell_activation_patterns) {
log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second));
other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
other_cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second)));
all_ctrl_signals.append(p.first);
}
for (auto &bit : cell_activation_signals.to_sigbit_vector())
bits_queue.insert(bit);
qcsat.prepare();
for (auto &bit : other_cell_activation_signals.to_sigbit_vector())
bits_queue.insert(bit);
while (!bits_queue.empty())
{
pool<ModWalker::PortBit> portbits;
modwalker.get_drivers(portbits, bits_queue);
bits_queue.clear();
for (auto &pbit : portbits)
if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) {
if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4)
continue;
// log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type));
bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end());
satgen.importCell(pbit.cell);
sat_cells.insert(pbit.cell);
}
if (config.opt_fast && sat_cells.size() > 100)
break;
}
for (auto it : exclusive_ctrls)
if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) {
log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second));
int sub1 = satgen.importSigBit(it.first);
int sub2 = satgen.importSigBit(it.second);
ez->assume(ez->NOT(ez->AND(sub1, sub2)));
}
if (!ez->solve(ez->expression(ez->OpOr, cell_active))) {
int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active);
if (!qcsat.ez->solve(sub1)) {
log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell));
cells_to_remove.insert(cell);
break;
}
if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) {
int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active);
if (!qcsat.ez->solve(sub2)) {
log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell));
cells_to_remove.insert(other_cell);
shareable_cells.erase(other_cell);
continue;
}
ez->non_incremental();
qcsat.ez->non_incremental();
all_ctrl_signals.sort_and_unify();
std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals);
std::vector<int> sat_model = qcsat.importSig(all_ctrl_signals);
std::vector<bool> sat_model_values;
int sub1 = ez->expression(ez->OpOr, cell_active);
int sub2 = ez->expression(ez->OpOr, other_cell_active);
ez->assume(ez->AND(sub1, sub2));
qcsat.ez->assume(qcsat.ez->AND(sub1, sub2));
log(" Size of SAT problem: %d cells, %d variables, %d clauses\n",
GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses());
GetSize(sat_cells), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses());
if (ez->solve(sat_model, sat_model_values)) {
if (qcsat.ez->solve(sat_model, sat_model_values)) {
log(" According to the SAT solver this pair of cells can not be shared.\n");
log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values));
for (int i = GetSize(sat_model_values)-1; i >= 0; i--)

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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
@ -558,7 +558,7 @@ struct WreducePass : public Pass {
}
}
if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
if (!opt_memx && c->type.in(ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
IdString memid = c->getParam(ID::MEMID).decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
if (mem->start_offset >= 0) {

View file

@ -4,24 +4,31 @@
# --------------------------------------
OBJS += passes/pmgen/test_pmgen.o
GENFILES += passes/pmgen/test_pmgen_pm.h
passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h
$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
# --------------------------------------
OBJS += passes/pmgen/ice40_dsp.o
GENFILES += passes/pmgen/ice40_dsp_pm.h
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h))
# --------------------------------------
OBJS += passes/pmgen/ice40_wrapcarry.o
GENFILES += passes/pmgen/ice40_wrapcarry_pm.h
passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h
$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
# --------------------------------------
OBJS += passes/pmgen/xilinx_dsp.o
GENFILES += passes/pmgen/xilinx_dsp_pm.h
GENFILES += passes/pmgen/xilinx_dsp48a_pm.h
GENFILES += passes/pmgen/xilinx_dsp_CREG_pm.h
GENFILES += passes/pmgen/xilinx_dsp_cascade_pm.h
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h))
@ -31,6 +38,7 @@ $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
# --------------------------------------
OBJS += passes/pmgen/peepopt.o
GENFILES += passes/pmgen/peepopt_pm.h
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
@ -43,5 +51,6 @@ passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
# --------------------------------------
OBJS += passes/pmgen/xilinx_srl.o
GENFILES += passes/pmgen/xilinx_srl_pm.h
passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

View file

@ -28,9 +28,8 @@ code sigA sigB sigH
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
// Do not remove sign bit
++i;
return sig.extract(0, i);
};
sigA = unextend(port(mul, \A));

View file

@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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

Some files were not shown because too many files have changed in this diff Show more