mirror of https://github.com/YosysHQ/yosys.git
Added SAT-based write-port sharing to memory_share
This commit is contained in:
parent
35edac0b31
commit
297a0962ea
|
@ -18,7 +18,9 @@
|
|||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/modwalker.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <algorithm>
|
||||
|
@ -37,6 +39,8 @@ struct MemoryShareWorker
|
|||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
SigMap sigmap, sigmap_xmux;
|
||||
ModWalker modwalker;
|
||||
CellTypes cone_ct;
|
||||
|
||||
std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux;
|
||||
std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache;
|
||||
|
@ -470,6 +474,167 @@ struct MemoryShareWorker
|
|||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Consolidate write ports using sat-based resource sharing
|
||||
// --------------------------------------------------------
|
||||
|
||||
void consolidate_wr_using_sat(std::string memid, std::vector<RTLIL::Cell*> &wr_ports)
|
||||
{
|
||||
if (wr_ports.size() <= 1)
|
||||
return;
|
||||
|
||||
ezDefaultSAT ez;
|
||||
SatGen satgen(&ez, &modwalker.sigmap);
|
||||
|
||||
// find list of considered ports and port pairs
|
||||
|
||||
std::set<int> considered_ports;
|
||||
std::set<int> considered_port_pairs;
|
||||
|
||||
for (int i = 0; i < int(wr_ports.size()); i++) {
|
||||
std::vector<RTLIL::SigBit> bits = modwalker.sigmap(wr_ports[i]->connections.at("\\EN"));
|
||||
for (auto bit : bits)
|
||||
if (bit == RTLIL::State::S1)
|
||||
goto port_is_always_active;
|
||||
if (modwalker.has_drivers(bits))
|
||||
considered_ports.insert(i);
|
||||
port_is_always_active:;
|
||||
}
|
||||
|
||||
log("Consolidating write ports of memory %s using sat-based resource sharing:\n", log_id(memid));
|
||||
|
||||
bool cache_clk_enable = false;
|
||||
bool cache_clk_polarity = false;
|
||||
RTLIL::SigSpec cache_clk;
|
||||
|
||||
for (int i = 0; i < int(wr_ports.size()); i++)
|
||||
{
|
||||
RTLIL::Cell *cell = wr_ports.at(i);
|
||||
|
||||
if (cell->parameters.at("\\CLK_ENABLE").as_bool() != cache_clk_enable ||
|
||||
(cache_clk_enable && (sigmap(cell->connections.at("\\CLK")) != cache_clk ||
|
||||
cell->parameters.at("\\CLK_POLARITY").as_bool() != cache_clk_polarity)))
|
||||
{
|
||||
cache_clk_enable = cell->parameters.at("\\CLK_ENABLE").as_bool();
|
||||
cache_clk_polarity = cell->parameters.at("\\CLK_POLARITY").as_bool();
|
||||
cache_clk = sigmap(cell->connections.at("\\CLK"));
|
||||
}
|
||||
else if (i > 0 && considered_ports.count(i-1) && considered_ports.count(i))
|
||||
considered_port_pairs.insert(i);
|
||||
|
||||
if (cache_clk_enable)
|
||||
log(" Port %d (%s) on %s %s: %s\n", i, log_id(cell),
|
||||
cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk),
|
||||
considered_ports.count(i) ? "considered" : "not considered");
|
||||
else
|
||||
log(" Port %d (%s) unclocked: %s\n", i, log_id(cell),
|
||||
considered_ports.count(i) ? "considered" : "not considered");
|
||||
}
|
||||
|
||||
if (considered_port_pairs.size() < 1) {
|
||||
log(" No two subsequent ports in same clock domain considered -> nothing to consolidate.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create SAT representation of common input cone of all considered EN signals
|
||||
|
||||
std::set<RTLIL::Cell*> sat_cells;
|
||||
std::set<RTLIL::SigBit> bits_queue;
|
||||
std::map<int, int> port_to_sat_variable;
|
||||
|
||||
for (int i = 0; i < int(wr_ports.size()); i++)
|
||||
if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1))
|
||||
{
|
||||
RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->connections.at("\\EN"));
|
||||
port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig));
|
||||
|
||||
std::vector<RTLIL::SigBit> bits = sig;
|
||||
bits_queue.insert(bits.begin(), bits.end());
|
||||
}
|
||||
|
||||
while (!bits_queue.empty())
|
||||
{
|
||||
std::set<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)) {
|
||||
std::set<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell];
|
||||
bits_queue.insert(cell_inputs.begin(), cell_inputs.end());
|
||||
sat_cells.insert(pbit.cell);
|
||||
}
|
||||
}
|
||||
|
||||
log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size()));
|
||||
|
||||
for (auto cell : sat_cells)
|
||||
satgen.importCell(cell);
|
||||
|
||||
log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses());
|
||||
|
||||
// merge subsequent ports if possible
|
||||
|
||||
for (int i = 0; i < int(wr_ports.size()); i++)
|
||||
{
|
||||
if (!considered_port_pairs.count(i))
|
||||
continue;
|
||||
|
||||
if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
|
||||
log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
log(" Merging port %d into port %d.\n", i-1, i);
|
||||
port_to_sat_variable.at(i) = ez.OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
|
||||
|
||||
RTLIL::SigSpec last_addr = wr_ports[i-1]->connections.at("\\ADDR");
|
||||
RTLIL::SigSpec last_data = wr_ports[i-1]->connections.at("\\DATA");
|
||||
std::vector<RTLIL::SigBit> last_en = modwalker.sigmap(wr_ports[i-1]->connections.at("\\EN"));
|
||||
|
||||
RTLIL::SigSpec this_addr = wr_ports[i]->connections.at("\\ADDR");
|
||||
RTLIL::SigSpec this_data = wr_ports[i]->connections.at("\\DATA");
|
||||
std::vector<RTLIL::SigBit> this_en = modwalker.sigmap(wr_ports[i]->connections.at("\\EN"));
|
||||
|
||||
RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en);
|
||||
|
||||
wr_ports[i]->connections.at("\\ADDR") = module->Mux(NEW_ID, last_addr, this_addr, this_en_active);
|
||||
wr_ports[i]->connections.at("\\DATA") = module->Mux(NEW_ID, last_data, this_data, this_en_active);
|
||||
|
||||
std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups_en;
|
||||
RTLIL::SigSpec grouped_last_en, grouped_this_en, en;
|
||||
RTLIL::Wire *grouped_en = module->new_wire(0, NEW_ID);
|
||||
|
||||
for (int j = 0; j < int(this_en.size()); j++) {
|
||||
std::pair<RTLIL::SigBit, RTLIL::SigBit> key(last_en[j], this_en[j]);
|
||||
if (!groups_en.count(key)) {
|
||||
grouped_last_en.append_bit(last_en[j]);
|
||||
grouped_this_en.append_bit(this_en[j]);
|
||||
groups_en[key] = grouped_en->width;
|
||||
grouped_en->width++;
|
||||
}
|
||||
en.append(RTLIL::SigSpec(grouped_en, 1, groups_en[key]));
|
||||
}
|
||||
|
||||
module->addMux(NEW_ID, grouped_last_en, grouped_this_en, this_en_active, grouped_en);
|
||||
wr_ports[i]->connections.at("\\EN") = en;
|
||||
|
||||
module->cells.erase(wr_ports[i-1]->name);
|
||||
delete wr_ports[i-1];
|
||||
wr_ports[i-1] = NULL;
|
||||
}
|
||||
|
||||
// Clean up `wr_ports': remove all NULL entries
|
||||
|
||||
std::vector<RTLIL::Cell*> wr_ports_with_nulls;
|
||||
wr_ports_with_nulls.swap(wr_ports);
|
||||
|
||||
for (auto cell : wr_ports_with_nulls)
|
||||
if (cell != NULL)
|
||||
wr_ports.push_back(cell);
|
||||
}
|
||||
|
||||
|
||||
// -------------
|
||||
// Setup and run
|
||||
// -------------
|
||||
|
@ -515,6 +680,21 @@ struct MemoryShareWorker
|
|||
translate_rd_feedback_to_en(it.first, it.second.first, it.second.second);
|
||||
consolidate_wr_by_addr(it.first, it.second.second);
|
||||
}
|
||||
|
||||
cone_ct.setup_internals();
|
||||
cone_ct.cell_types.erase("$mul");
|
||||
cone_ct.cell_types.erase("$mod");
|
||||
cone_ct.cell_types.erase("$div");
|
||||
cone_ct.cell_types.erase("$pow");
|
||||
cone_ct.cell_types.erase("$shl");
|
||||
cone_ct.cell_types.erase("$shr");
|
||||
cone_ct.cell_types.erase("$sshl");
|
||||
cone_ct.cell_types.erase("$sshr");
|
||||
|
||||
modwalker.setup(design, module, &cone_ct);
|
||||
|
||||
for (auto &it : memindex)
|
||||
consolidate_wr_using_sat(it.first, it.second.second);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// expect-wr-ports 1
|
||||
// expect-rd-ports 1
|
||||
|
||||
module test(
|
||||
input clk,
|
||||
input wr_en1, wr_en2, wr_en3,
|
||||
input [3:0] wr_addr1, wr_addr2, wr_addr3,
|
||||
input [15:0] wr_data,
|
||||
input [3:0] rd_addr,
|
||||
output reg [31:0] rd_data
|
||||
);
|
||||
|
||||
reg [31:0] mem [0:15];
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (wr_en1)
|
||||
mem[wr_addr1][15:0] <= wr_data;
|
||||
else if (wr_en2)
|
||||
mem[wr_addr2][23:8] <= wr_data;
|
||||
else if (wr_en3)
|
||||
mem[wr_addr3][31:16] <= wr_data;
|
||||
rd_data <= mem[rd_addr];
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue