2019-11-22 09:58:49 -06:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
2021-06-07 17:39:36 -05:00
|
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
2019-11-22 09:58:49 -06:00
|
|
|
*
|
|
|
|
* 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"
|
2020-10-17 15:21:05 -05:00
|
|
|
#include "kernel/mem.h"
|
2022-05-06 16:29:16 -05:00
|
|
|
#include "kernel/ff.h"
|
2019-11-22 09:58:49 -06:00
|
|
|
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
struct OptMemPass : public Pass {
|
|
|
|
OptMemPass() : Pass("opt_mem", "optimize memories") { }
|
2020-06-18 18:34:52 -05:00
|
|
|
void help() override
|
2019-11-22 09:58:49 -06:00
|
|
|
{
|
|
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
|
|
log("\n");
|
|
|
|
log(" opt_mem [options] [selection]\n");
|
|
|
|
log("\n");
|
|
|
|
log("This pass performs various optimizations on memories in the design.\n");
|
|
|
|
log("\n");
|
|
|
|
}
|
2020-06-18 18:34:52 -05:00
|
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
2019-11-22 09:58:49 -06:00
|
|
|
{
|
|
|
|
log_header(design, "Executing OPT_MEM pass (optimize memories).\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()) {
|
2021-05-25 11:49:17 -05:00
|
|
|
SigMap sigmap(module);
|
|
|
|
FfInitVals initvals(&sigmap, module);
|
2020-10-17 15:21:05 -05:00
|
|
|
for (auto &mem : Mem::get_selected_memories(module)) {
|
2022-05-06 16:29:16 -05:00
|
|
|
std::vector<bool> always_0(mem.width, true);
|
|
|
|
std::vector<bool> always_1(mem.width, true);
|
2021-05-22 13:27:51 -05:00
|
|
|
bool changed = false;
|
|
|
|
for (auto &port : mem.wr_ports) {
|
|
|
|
if (port.en.is_fully_zero()) {
|
|
|
|
port.removed = true;
|
|
|
|
changed = true;
|
|
|
|
total_count++;
|
2022-05-06 16:29:16 -05:00
|
|
|
} else {
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
for (int i = 0; i < mem.width; i++) {
|
|
|
|
int bit = sub * mem.width + i;
|
|
|
|
if (port.en[bit] != State::S0) {
|
|
|
|
if (port.data[bit] != State::Sx && port.data[bit] != State::S0) {
|
|
|
|
always_0[i] = false;
|
|
|
|
}
|
|
|
|
if (port.data[bit] != State::Sx && port.data[bit] != State::S1) {
|
|
|
|
always_1[i] = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (port.data[bit] != State::Sx) {
|
|
|
|
port.data[bit] = State::Sx;
|
|
|
|
changed = true;
|
|
|
|
total_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-22 13:27:51 -05:00
|
|
|
}
|
|
|
|
}
|
2022-05-06 16:29:16 -05:00
|
|
|
for (auto &init : mem.inits) {
|
|
|
|
for (int i = 0; i < GetSize(init.data); i++) {
|
|
|
|
State bit = init.data.bits[i];
|
|
|
|
int lane = i % mem.width;
|
|
|
|
if (bit != State::Sx && bit != State::S0) {
|
|
|
|
always_0[lane] = false;
|
|
|
|
}
|
|
|
|
if (bit != State::Sx && bit != State::S1) {
|
|
|
|
always_1[lane] = false;
|
|
|
|
}
|
|
|
|
}
|
2021-05-22 13:27:51 -05:00
|
|
|
}
|
2022-05-06 16:29:16 -05:00
|
|
|
std::vector<int> swizzle;
|
|
|
|
for (int i = 0; i < mem.width; i++) {
|
|
|
|
if (!always_0[i] && !always_1[i]) {
|
|
|
|
swizzle.push_back(i);
|
|
|
|
continue;
|
2021-05-25 11:49:17 -05:00
|
|
|
}
|
2022-05-06 16:29:16 -05:00
|
|
|
State bit;
|
|
|
|
if (!always_0[i]) {
|
|
|
|
log("%s.%s: removing const-1 lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
|
|
|
bit = State::S1;
|
|
|
|
} else if (!always_1[i]) {
|
|
|
|
log("%s.%s: removing const-0 lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
|
|
|
bit = State::S0;
|
|
|
|
} else {
|
|
|
|
log("%s.%s: removing const-x lane %d\n", log_id(module->name), log_id(mem.memid), i);
|
|
|
|
bit = State::Sx;
|
|
|
|
}
|
|
|
|
// Reconnect read port data.
|
|
|
|
for (auto &port: mem.rd_ports) {
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
int bidx = sub * mem.width + i;
|
|
|
|
if (!port.clk_enable) {
|
|
|
|
module->connect(port.data[bidx], bit);
|
|
|
|
} else {
|
|
|
|
// The FF will most likely be redundant, but it's up to opt_dff to deal with this.
|
|
|
|
FfData ff(module, &initvals, NEW_ID);
|
|
|
|
ff.width = 1;
|
|
|
|
ff.has_clk = true;
|
|
|
|
ff.sig_clk = port.clk;
|
|
|
|
ff.pol_clk = port.clk_polarity;
|
|
|
|
if (port.en != State::S1) {
|
|
|
|
ff.has_ce = true;
|
|
|
|
ff.pol_ce = true;
|
|
|
|
ff.sig_ce = port.en;
|
|
|
|
}
|
|
|
|
if (port.arst != State::S0) {
|
|
|
|
ff.has_arst = true;
|
|
|
|
ff.pol_arst = true;
|
|
|
|
ff.sig_arst = port.arst;
|
|
|
|
ff.val_arst = port.arst_value[bidx];
|
|
|
|
}
|
|
|
|
if (port.srst != State::S0) {
|
|
|
|
ff.has_srst = true;
|
|
|
|
ff.pol_srst = true;
|
|
|
|
ff.sig_srst = port.srst;
|
|
|
|
ff.val_srst = port.srst_value[bidx];
|
|
|
|
}
|
|
|
|
ff.sig_d = bit;
|
|
|
|
ff.sig_q = port.data[bidx];
|
|
|
|
ff.val_init = port.init_value[bidx];
|
|
|
|
ff.emit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (GetSize(swizzle) == 0) {
|
2020-10-17 15:21:05 -05:00
|
|
|
mem.remove();
|
|
|
|
total_count++;
|
2022-05-06 16:29:16 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (GetSize(swizzle) != mem.width) {
|
|
|
|
for (auto &port: mem.wr_ports) {
|
|
|
|
SigSpec new_data;
|
|
|
|
SigSpec new_en;
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
for (auto i: swizzle) {
|
|
|
|
new_data.append(port.data[sub * mem.width + i]);
|
|
|
|
new_en.append(port.en[sub * mem.width + i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port.data = new_data;
|
|
|
|
port.en = new_en;
|
|
|
|
}
|
|
|
|
for (auto &port: mem.rd_ports) {
|
|
|
|
SigSpec new_data;
|
|
|
|
Const new_init;
|
|
|
|
Const new_arst;
|
|
|
|
Const new_srst;
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
for (auto i: swizzle) {
|
|
|
|
int bidx = sub * mem.width + i;
|
|
|
|
new_data.append(port.data[bidx]);
|
|
|
|
new_init.bits.push_back(port.init_value.bits[bidx]);
|
|
|
|
new_arst.bits.push_back(port.arst_value.bits[bidx]);
|
|
|
|
new_srst.bits.push_back(port.srst_value.bits[bidx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port.data = new_data;
|
|
|
|
port.init_value = new_init;
|
|
|
|
port.arst_value = new_arst;
|
|
|
|
port.srst_value = new_srst;
|
|
|
|
}
|
|
|
|
for (auto &init: mem.inits) {
|
|
|
|
Const new_data;
|
|
|
|
Const new_en;
|
|
|
|
for (int s = 0; s < GetSize(init.data); s += mem.width) {
|
|
|
|
for (auto i: swizzle) {
|
|
|
|
new_data.bits.push_back(init.data.bits[s + i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto i: swizzle) {
|
|
|
|
new_en.bits.push_back(init.en.bits[i]);
|
|
|
|
}
|
|
|
|
init.data = new_data;
|
|
|
|
init.en = new_en;
|
|
|
|
}
|
|
|
|
mem.width = GetSize(swizzle);
|
|
|
|
changed = true;
|
|
|
|
total_count++;
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
mem.emit();
|
2020-10-17 15:21:05 -05:00
|
|
|
}
|
2019-11-22 09:58:49 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (total_count)
|
|
|
|
design->scratchpad_set_bool("opt.did_something", true);
|
|
|
|
log("Performed a total of %d transformations.\n", total_count);
|
|
|
|
}
|
|
|
|
} OptMemPass;
|
|
|
|
|
|
|
|
PRIVATE_NAMESPACE_END
|