mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-13 04:28:18 +00:00
opt_clean: Better memory handling.
Previously, `$memwr` and `$meminit` cells were always preserved (along with the memory itself). With this change, they are instead part of the main cell mark-and-sweep pass: a memory (and its `$meminit` and `$memwr` cells) is only preserved iff any associated `$memrd` cell needs to be preserved.
This commit is contained in:
parent
fd306b0520
commit
7670a89e1f
|
@ -55,7 +55,7 @@ struct keep_cache_t
|
||||||
if (!module->get_bool_attribute(ID::keep)) {
|
if (!module->get_bool_attribute(ID::keep)) {
|
||||||
bool found_keep = false;
|
bool found_keep = false;
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
if (query(cell, true /* ignore_specify_mem */)) {
|
if (query(cell, true /* ignore_specify */)) {
|
||||||
found_keep = true;
|
found_keep = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -70,12 +70,12 @@ struct keep_cache_t
|
||||||
return cache[module];
|
return cache[module];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool query(Cell *cell, bool ignore_specify_mem = false)
|
bool query(Cell *cell, bool ignore_specify = false)
|
||||||
{
|
{
|
||||||
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!ignore_specify_mem && cell->type.in(ID($memwr), ID($meminit), ID($specify2), ID($specify3), ID($specrule)))
|
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (cell->has_keep_attr())
|
if (cell->has_keep_attr())
|
||||||
|
@ -95,6 +95,8 @@ int count_rm_cells, count_rm_wires;
|
||||||
void rmunused_module_cells(Module *module, bool verbose)
|
void rmunused_module_cells(Module *module, bool verbose)
|
||||||
{
|
{
|
||||||
SigMap sigmap(module);
|
SigMap sigmap(module);
|
||||||
|
dict<IdString, pool<Cell*>> mem2cells;
|
||||||
|
pool<IdString> mem_unused;
|
||||||
pool<Cell*> queue, unused;
|
pool<Cell*> queue, unused;
|
||||||
pool<SigBit> used_raw_bits;
|
pool<SigBit> used_raw_bits;
|
||||||
dict<SigBit, pool<Cell*>> wire2driver;
|
dict<SigBit, pool<Cell*>> wire2driver;
|
||||||
|
@ -108,6 +110,17 @@ void rmunused_module_cells(Module *module, bool verbose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto &it : module->memories) {
|
||||||
|
mem_unused.insert(it.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Cell *cell : module->cells()) {
|
||||||
|
if (cell->type.in(ID($memwr), ID($meminit))) {
|
||||||
|
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||||
|
mem2cells[mem_id].insert(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &it : module->cells_) {
|
for (auto &it : module->cells_) {
|
||||||
Cell *cell = it.second;
|
Cell *cell = it.second;
|
||||||
for (auto &it2 : cell->connections()) {
|
for (auto &it2 : cell->connections()) {
|
||||||
|
@ -145,17 +158,33 @@ void rmunused_module_cells(Module *module, bool verbose)
|
||||||
while (!queue.empty())
|
while (!queue.empty())
|
||||||
{
|
{
|
||||||
pool<SigBit> bits;
|
pool<SigBit> bits;
|
||||||
for (auto cell : queue)
|
pool<IdString> mems;
|
||||||
for (auto &it : cell->connections())
|
for (auto cell : queue) {
|
||||||
if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
|
for (auto &it : cell->connections())
|
||||||
for (auto bit : sigmap(it.second))
|
if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
|
||||||
bits.insert(bit);
|
for (auto bit : sigmap(it.second))
|
||||||
|
bits.insert(bit);
|
||||||
|
|
||||||
|
if (cell->type == ID($memrd)) {
|
||||||
|
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||||
|
if (mem_unused.count(mem_id)) {
|
||||||
|
mem_unused.erase(mem_id);
|
||||||
|
mems.insert(mem_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
queue.clear();
|
queue.clear();
|
||||||
|
|
||||||
for (auto bit : bits)
|
for (auto bit : bits)
|
||||||
for (auto c : wire2driver[bit])
|
for (auto c : wire2driver[bit])
|
||||||
if (unused.count(c))
|
if (unused.count(c))
|
||||||
queue.insert(c), unused.erase(c);
|
queue.insert(c), unused.erase(c);
|
||||||
|
|
||||||
|
for (auto mem : mems)
|
||||||
|
for (auto c : mem2cells[mem])
|
||||||
|
if (unused.count(c))
|
||||||
|
queue.insert(c), unused.erase(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
|
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
|
||||||
|
@ -168,6 +197,14 @@ void rmunused_module_cells(Module *module, bool verbose)
|
||||||
count_rm_cells++;
|
count_rm_cells++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto it : mem_unused)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
log_debug(" removing unused memory `%s'.\n", it.c_str());
|
||||||
|
delete module->memories.at(it);
|
||||||
|
module->memories.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &it : module->cells_) {
|
for (auto &it : module->cells_) {
|
||||||
Cell *cell = it.second;
|
Cell *cell = it.second;
|
||||||
for (auto &it2 : cell->connections()) {
|
for (auto &it2 : cell->connections()) {
|
||||||
|
|
49
tests/opt/opt_clean_mem.ys
Normal file
49
tests/opt/opt_clean_mem.ys
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(...);
|
||||||
|
|
||||||
|
input [7:0] wa;
|
||||||
|
input [7:0] ra1;
|
||||||
|
input [7:0] ra2;
|
||||||
|
input [7:0] wd;
|
||||||
|
input clk;
|
||||||
|
wire [7:0] rd1;
|
||||||
|
wire [7:0] rd2;
|
||||||
|
|
||||||
|
reg [7:0] mem[0:7];
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
mem[wa] <= wd;
|
||||||
|
assign rd1 = mem[ra1];
|
||||||
|
assign rd2 = mem[ra2];
|
||||||
|
|
||||||
|
initial mem[8'h12] = 8'h34;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
proc
|
||||||
|
memory_dff
|
||||||
|
|
||||||
|
select -assert-count 2 t:$memrd
|
||||||
|
select -assert-count 1 t:$memwr
|
||||||
|
select -assert-count 1 t:$meminit
|
||||||
|
design -save orig
|
||||||
|
|
||||||
|
opt_clean
|
||||||
|
select -assert-none t:$memrd
|
||||||
|
select -assert-none t:$memwr
|
||||||
|
select -assert-none t:$meminit
|
||||||
|
|
||||||
|
design -load orig
|
||||||
|
expose top/rd1
|
||||||
|
opt_clean
|
||||||
|
select -assert-count 1 t:$memrd
|
||||||
|
select -assert-count 1 t:$memwr
|
||||||
|
select -assert-count 1 t:$meminit
|
||||||
|
|
||||||
|
design -load orig
|
||||||
|
expose top/rd1 top/rd2
|
||||||
|
opt_clean
|
||||||
|
select -assert-count 2 t:$memrd
|
||||||
|
select -assert-count 1 t:$memwr
|
||||||
|
select -assert-count 1 t:$meminit
|
Loading…
Reference in a new issue