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:
commit
49545c73f7
757 changed files with 49469 additions and 8717 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
210
passes/cmds/clean_zerowidth.cc
Normal file
210
passes/cmds/clean_zerowidth.cc
Normal 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
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
599
passes/cmds/glift.cc
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
312
passes/cmds/sta.cc
Normal 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
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ¶m : 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 ¶m : 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
67
passes/memory/memory_narrow.cc
Normal file
67
passes/memory/memory_narrow.cc
Normal 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
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
350
passes/opt/opt_mem_feedback.cc
Normal file
350
passes/opt/opt_mem_feedback.cc
Normal 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
|
||||
|
109
passes/opt/opt_mem_priority.cc
Normal file
109
passes/opt/opt_mem_priority.cc
Normal 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
107
passes/opt/opt_mem_widen.cc
Normal 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
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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--)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue