mirror of https://github.com/YosysHQ/yosys.git
opt_boundary
This commit is contained in:
parent
cd16928385
commit
7db8f29c04
|
|
@ -18,6 +18,7 @@ OBJS += passes/silimate/splitnetlist.o
|
|||
OBJS += passes/silimate/opt_timing_balance.o
|
||||
OBJS += passes/silimate/cone_partition.o
|
||||
OBJS += passes/silimate/clkmerge.o
|
||||
OBJS += passes/silimate/opt_boundary.o
|
||||
OBJS += passes/silimate/opt_vps.o
|
||||
|
||||
OBJS += passes/silimate/opt_expand.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Akash Levy <akash@silimate.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/celltypes.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct BoundaryConeWorker {
|
||||
Module *child, *parent;
|
||||
Cell *instance;
|
||||
SigMap child_sigmap;
|
||||
int max_cells;
|
||||
|
||||
dict<SigBit, Cell*> bit_driver;
|
||||
dict<SigBit, SigBit> input_map;
|
||||
dict<SigBit, SigBit> copied_bits;
|
||||
dict<Cell*, Cell*> copied_cells;
|
||||
std::vector<Wire*> created_wires;
|
||||
std::vector<Cell*> created_cells;
|
||||
pool<Cell*> active_cells;
|
||||
int copied_cell_count = 0;
|
||||
bool failed = false;
|
||||
|
||||
BoundaryConeWorker(Module *child, Module *parent, Cell *instance, int max_cells)
|
||||
: child(child), parent(parent), instance(instance), child_sigmap(child), max_cells(max_cells)
|
||||
{
|
||||
for (auto wire : child->wires()) {
|
||||
if (!wire->port_input || wire->port_output)
|
||||
continue;
|
||||
if (!instance->connections_.count(wire->name))
|
||||
continue;
|
||||
SigSpec conn = instance->connections_.at(wire->name);
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
input_map[child_sigmap(SigBit(wire, i))] = conn[i];
|
||||
}
|
||||
|
||||
for (auto cell : child->cells()) {
|
||||
if (!yosys_celltypes.cell_evaluable(cell->type) || cell->has_keep_attr())
|
||||
continue;
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!yosys_celltypes.cell_output(cell->type, conn.first))
|
||||
continue;
|
||||
for (auto bit : conn.second)
|
||||
if (bit.is_wire())
|
||||
bit_driver[child_sigmap(bit)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SigSpec materialize(SigSpec sig)
|
||||
{
|
||||
SigSpec result;
|
||||
for (auto bit : sig)
|
||||
result.append(materialize(bit));
|
||||
return result;
|
||||
}
|
||||
|
||||
SigBit materialize(SigBit bit)
|
||||
{
|
||||
bit = child_sigmap(bit);
|
||||
|
||||
if (!bit.is_wire())
|
||||
return bit;
|
||||
|
||||
if (input_map.count(bit))
|
||||
return input_map.at(bit);
|
||||
|
||||
if (copied_bits.count(bit))
|
||||
return copied_bits.at(bit);
|
||||
|
||||
if (!bit_driver.count(bit)) {
|
||||
failed = true;
|
||||
return RTLIL::Sx;
|
||||
}
|
||||
|
||||
Cell *driver = bit_driver.at(bit);
|
||||
if (active_cells.count(driver) || copied_cell_count >= max_cells) {
|
||||
failed = true;
|
||||
return RTLIL::Sx;
|
||||
}
|
||||
|
||||
copy_driver(driver);
|
||||
if (failed || !copied_bits.count(bit)) {
|
||||
failed = true;
|
||||
return RTLIL::Sx;
|
||||
}
|
||||
return copied_bits.at(bit);
|
||||
}
|
||||
|
||||
void copy_driver(Cell *driver)
|
||||
{
|
||||
if (copied_cells.count(driver))
|
||||
return;
|
||||
|
||||
active_cells.insert(driver);
|
||||
dict<IdString, SigSpec> new_connections;
|
||||
|
||||
for (auto &conn : driver->connections()) {
|
||||
if (yosys_celltypes.cell_input(driver->type, conn.first)) {
|
||||
SigSpec mapped = materialize(conn.second);
|
||||
if (failed)
|
||||
break;
|
||||
new_connections[conn.first] = mapped;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (yosys_celltypes.cell_output(driver->type, conn.first)) {
|
||||
Wire *wire = parent->addWire(NEW_ID_SUFFIX("opt_boundary"), GetSize(conn.second));
|
||||
created_wires.push_back(wire);
|
||||
SigSpec mapped = wire;
|
||||
new_connections[conn.first] = mapped;
|
||||
for (int i = 0; i < GetSize(conn.second); i++) {
|
||||
if (conn.second[i].is_wire())
|
||||
copied_bits[child_sigmap(conn.second[i])] = mapped[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!failed && copied_cell_count >= max_cells)
|
||||
failed = true;
|
||||
|
||||
if (!failed) {
|
||||
Cell *copy = parent->addCell(NEW_ID_SUFFIX("opt_boundary"), driver);
|
||||
for (auto &conn : new_connections)
|
||||
copy->setPort(conn.first, conn.second);
|
||||
copied_cells[driver] = copy;
|
||||
created_cells.push_back(copy);
|
||||
copied_cell_count++;
|
||||
}
|
||||
|
||||
active_cells.erase(driver);
|
||||
}
|
||||
|
||||
void rollback()
|
||||
{
|
||||
for (auto it = created_cells.rbegin(); it != created_cells.rend(); ++it)
|
||||
parent->remove(*it);
|
||||
for (auto it = created_wires.rbegin(); it != created_wires.rend(); ++it)
|
||||
parent->remove(pool<Wire*>{*it});
|
||||
created_cells.clear();
|
||||
created_wires.clear();
|
||||
copied_cells.clear();
|
||||
copied_bits.clear();
|
||||
copied_cell_count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static bool protected_module(Module *module)
|
||||
{
|
||||
return module->get_blackbox_attribute() ||
|
||||
module->get_bool_attribute(ID::keep) ||
|
||||
module->get_bool_attribute(ID::keep_hierarchy);
|
||||
}
|
||||
|
||||
struct OptBoundaryPass : Pass {
|
||||
OptBoundaryPass() : Pass("opt_boundary", "perform conservative parent-side cross-boundary cone optimization") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" opt_boundary [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass performs a conservative form of hierarchical boundary optimization.\n");
|
||||
log("For each selected parent module, it looks through instances of non-blackbox,\n");
|
||||
log("non-keep child modules and copies small evaluable combinational cones that\n");
|
||||
log("drive child output ports into the parent. The original child module body is\n");
|
||||
log("left unchanged; optimized instance outputs are disconnected only after an\n");
|
||||
log("equivalent parent-side cone has been created.\n");
|
||||
log("\n");
|
||||
log(" -max_cells <N>\n");
|
||||
log(" maximum number of child cells to copy for one output bit. Default: 8.\n");
|
||||
log("\n");
|
||||
log(" -no_disconnect\n");
|
||||
log(" copy eligible cones into the parent but leave instance output ports\n");
|
||||
log(" connected to their original nets.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing OPT_BOUNDARY pass.\n");
|
||||
|
||||
int max_cells = 8;
|
||||
bool no_disconnect = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-max_cells" && argidx + 1 < args.size()) {
|
||||
max_cells = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-no_disconnect") {
|
||||
no_disconnect = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (max_cells < 1)
|
||||
log_cmd_error("The -max_cells value must be positive.\n");
|
||||
|
||||
bool did_something = false;
|
||||
for (auto parent : design->selected_modules(RTLIL::SELECT_WHOLE_ONLY, RTLIL::SB_UNBOXED_CMDERR)) {
|
||||
if (protected_module(parent))
|
||||
continue;
|
||||
|
||||
for (auto instance : parent->cells().to_vector()) {
|
||||
if (instance->has_keep_attr())
|
||||
continue;
|
||||
|
||||
Module *child = design->module(instance->type);
|
||||
if (child == nullptr || protected_module(child))
|
||||
continue;
|
||||
|
||||
for (auto &conn : instance->connections_) {
|
||||
Wire *port = child->wire(conn.first);
|
||||
if (port == nullptr || !port->port_output || port->port_input)
|
||||
continue;
|
||||
if (port->width != GetSize(conn.second))
|
||||
log_error("Port %s connected on instance %s not found in module %s or width is not matching\n",
|
||||
log_id(conn.first), log_id(instance), log_id(child));
|
||||
|
||||
SigSpec new_conn = conn.second;
|
||||
bool changed_port = false;
|
||||
for (int i = 0; i < port->width; i++) {
|
||||
if (!conn.second[i].is_wire())
|
||||
continue;
|
||||
|
||||
BoundaryConeWorker worker(child, parent, instance, max_cells);
|
||||
SigBit replacement = worker.materialize(SigBit(port, i));
|
||||
if (worker.failed) {
|
||||
worker.rollback();
|
||||
continue;
|
||||
}
|
||||
if (replacement == conn.second[i]) {
|
||||
worker.rollback();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!no_disconnect) {
|
||||
parent->connect(conn.second[i], replacement);
|
||||
Wire *dummy = parent->addWire(NEW_ID_SUFFIX("opt_boundary_output"));
|
||||
new_conn[i] = SigBit(dummy, 0);
|
||||
changed_port = true;
|
||||
}
|
||||
did_something = true;
|
||||
|
||||
if (worker.copied_cell_count > 0)
|
||||
log("Copied %d cells from cone driving %s[%d] of instance '%s' (type '%s') into '%s'\n",
|
||||
worker.copied_cell_count, log_id(port), i, log_id(instance), log_id(instance->type), log_id(parent));
|
||||
else
|
||||
log("Bypassed cone driving %s[%d] of instance '%s' (type '%s') in '%s'\n",
|
||||
log_id(port), i, log_id(instance), log_id(instance->type), log_id(parent));
|
||||
}
|
||||
|
||||
if (changed_port)
|
||||
conn.second = new_conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (did_something)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
}
|
||||
} OptBoundaryPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,851 @@
|
|||
###################################################################
|
||||
# Boundary optimization test cases
|
||||
###################################################################
|
||||
|
||||
log -header "Copy simple child cone into parent"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:$and
|
||||
select -assert-count 0 top/t:m
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
design -load gate
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:$and
|
||||
select -assert-count 0 top/t:m
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Partially bypass multi-bit output when one bit cannot be copied"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input clk, input a, input b, output [1:0] y);
|
||||
reg q;
|
||||
always @(posedge clk)
|
||||
q <= a;
|
||||
assign y[0] = a & b;
|
||||
assign y[1] = q;
|
||||
endmodule
|
||||
|
||||
module top(input clk, input a, input b, output [1:0] y);
|
||||
m u(.clk(clk), .a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 1 top/t:$and
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Ignore cone that depends on inout port"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(inout p, output y);
|
||||
assign y = p;
|
||||
endmodule
|
||||
|
||||
module top(inout p, output y);
|
||||
m u(.p(p), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave memory read cone intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input [1:0] addr, output y);
|
||||
reg [0:0] mem [0:3];
|
||||
assign y = mem[addr];
|
||||
endmodule
|
||||
|
||||
module top(input [1:0] addr, output y);
|
||||
m u(.addr(addr), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Rollback failed copy leaves no parent temporaries"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, input c, input d, output y);
|
||||
assign y = ((a & b) ^ c) | d;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, input c, input d, output y);
|
||||
m u(.a(a), .b(b), .c(c), .d(d), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary -max_cells 2
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
select -assert-count 0 top/t:$xor
|
||||
select -assert-count 0 top/t:$or
|
||||
check -assert
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy only mode leaves instance output connected"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary -no_disconnect
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 1 top/t:$and
|
||||
check -assert
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Bypass constant child output"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(output y);
|
||||
assign y = 1'b1;
|
||||
endmodule
|
||||
|
||||
module top(output y);
|
||||
m u(.y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy through sliced and concatenated port connections"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input [3:0] i, output [1:0] y);
|
||||
assign y = {i[0] ^ i[3], i[1] & i[2]};
|
||||
endmodule
|
||||
|
||||
module top(input [3:0] a, output [7:0] y);
|
||||
assign y[3:0] = a;
|
||||
assign y[7:6] = 2'b10;
|
||||
m u(.i({a[0], a[3], a[1], a[2]}), .y(y[5:4]));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-count 1 top/t:$and
|
||||
select -assert-count 1 top/t:$xor
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy shared internal cone feeding multiple outputs"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, input c, input d, output y, output z);
|
||||
wire t = a & b;
|
||||
assign y = t ^ c;
|
||||
assign z = t | d;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, input c, input d, output y, output z);
|
||||
m u(.a(a), .b(b), .c(c), .d(d), .y(y), .z(z));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-any top/t:$and
|
||||
select -assert-count 1 top/t:$xor
|
||||
select -assert-count 1 top/t:$or
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Keep protected module boundary intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
(* keep *)
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Keep protected parent boundary intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
(* keep *)
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Only selected parent modules are optimized"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top_keep(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
|
||||
module top_opt(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary top_opt
|
||||
opt_clean
|
||||
select -assert-count 1 top_keep/t:m
|
||||
select -assert-count 0 top_keep/t:$and
|
||||
select -assert-count 0 top_opt/t:m
|
||||
select -assert-count 1 top_opt/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy cone from parameterized child module"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m #(parameter WIDTH = 4) (input [WIDTH-1:0] a, input [WIDTH-1:0] b, output [WIDTH-1:0] y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input [7:0] a, input [7:0] b, output [7:0] y);
|
||||
m #(.WIDTH(8)) u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-any top/t:$and
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Stress generated instances with vector cones"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input [3:0] a, input [3:0] b, input [3:0] c, input [3:0] d, output [3:0] y);
|
||||
assign y = ((a & b) ^ c) | d;
|
||||
endmodule
|
||||
|
||||
module top(input [31:0] a, input [31:0] b, input [31:0] c, input [31:0] d, output [31:0] y);
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < 8; i = i + 1) begin : gen
|
||||
m u(
|
||||
.a(a[i*4 +: 4]),
|
||||
.b(b[i*4 +: 4]),
|
||||
.c(c[i*4 +: 4]),
|
||||
.d(d[i*4 +: 4]),
|
||||
.y(y[i*4 +: 4])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary -max_cells 3
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-any top/t:$and
|
||||
select -assert-any top/t:$xor
|
||||
select -assert-any top/t:$or
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Bypass direct child input to output"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, output y);
|
||||
assign y = a;
|
||||
endmodule
|
||||
|
||||
module top(input a, output y);
|
||||
m u(.a(a), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy multi-bit vector cone"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input [3:0] a, input [3:0] b, input [3:0] c, output [3:0] y);
|
||||
assign y = (a & b) ^ c;
|
||||
endmodule
|
||||
|
||||
module top(input [3:0] a, input [3:0] b, input [3:0] c, output [3:0] y);
|
||||
m u(.a(a), .b(b), .c(c), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-any top/t:$and
|
||||
select -assert-any top/t:$xor
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy independent cones from two instances of one module type"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a0, input b0, input a1, input b1, output y0, output y1);
|
||||
m u0(.a(a0), .b(b0), .y(y0));
|
||||
m u1(.a(a1), .b(b1), .y(y1));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 2 top/t:$and
|
||||
select -assert-count 0 top/t:m
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Honor max_cells limit"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, input c, input d, output y);
|
||||
assign y = ((a & b) ^ c) | d;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, input c, input d, output y);
|
||||
m u(.a(a), .b(b), .c(c), .d(d), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary -max_cells 2
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
select -assert-count 0 top/t:$xor
|
||||
select -assert-count 0 top/t:$or
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Copy deeper cone when max_cells allows it"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, input c, input d, output y);
|
||||
assign y = ((a & b) ^ c) | d;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, input c, input d, output y);
|
||||
m u(.a(a), .b(b), .c(c), .d(d), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary -max_cells 3
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:m
|
||||
select -assert-count 1 top/t:$and
|
||||
select -assert-count 1 top/t:$xor
|
||||
select -assert-count 1 top/t:$or
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave blackbox boundary intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
(* blackbox *)
|
||||
module m(input a, input b, output y);
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave kept instance boundary intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
(* keep *) m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave cone with kept internal cell intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -icells <<EOF
|
||||
module m(input a, input b, output y);
|
||||
(* keep *) \$and #(
|
||||
.A_SIGNED(1'b0),
|
||||
.B_SIGNED(1'b0),
|
||||
.A_WIDTH(1),
|
||||
.B_WIDTH(1),
|
||||
.Y_WIDTH(1)
|
||||
) kept_and (
|
||||
.A(a),
|
||||
.B(b),
|
||||
.Y(y)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave cone with missing input connection intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, output y);
|
||||
m u(.a(a), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Leave sequential cone intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input clk, input d, output y);
|
||||
reg q;
|
||||
always @(posedge clk)
|
||||
q <= d;
|
||||
assign y = q & d;
|
||||
endmodule
|
||||
|
||||
module top(input clk, input d, output y);
|
||||
m u(.clk(clk), .d(d), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 0 top/t:$and
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Partially bypass one output while preserving live instance"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module m(input clk, input a, input b, output y, output q);
|
||||
reg q_r;
|
||||
always @(posedge clk)
|
||||
q_r <= a;
|
||||
assign q = q_r;
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input clk, input a, input b, output y, output q);
|
||||
m u(.clk(clk), .a(a), .b(b), .y(y), .q(q));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:m
|
||||
select -assert-count 1 top/t:$and
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Keep protected child boundary intact"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
(* keep_hierarchy *)
|
||||
module m(input a, input b, output y);
|
||||
assign y = a & b;
|
||||
endmodule
|
||||
|
||||
module top(input a, input b, output y);
|
||||
m u(.a(a), .b(b), .y(y));
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
check -assert
|
||||
|
||||
opt_boundary
|
||||
opt_clean
|
||||
select -assert-count 0 top/t:$and
|
||||
select -assert-count 1 top/t:m
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
yosys -import
|
||||
|
||||
set tmp_v "opt_boundary_random_tmp.v"
|
||||
|
||||
proc emit_case {case_id width op0 op1 op2} {
|
||||
global tmp_v
|
||||
|
||||
set fh [open $tmp_v w]
|
||||
puts $fh "module m(input \[$width-1:0\] a, input \[$width-1:0\] b, input \[$width-1:0\] c, input \[$width-1:0\] d, output \[$width-1:0\] y);"
|
||||
puts $fh " wire \[$width-1:0\] t0 = a $op0 b;"
|
||||
puts $fh " wire \[$width-1:0\] t1 = t0 $op1 c;"
|
||||
puts $fh " assign y = t1 $op2 d;"
|
||||
puts $fh "endmodule"
|
||||
puts $fh ""
|
||||
puts $fh "module top(input \[$width-1:0\] a, input \[$width-1:0\] b, input \[$width-1:0\] c, input \[$width-1:0\] d, output \[$width-1:0\] y);"
|
||||
puts $fh " genvar i;"
|
||||
puts $fh " generate"
|
||||
puts $fh " for (i = 0; i < 4; i = i + 1) begin : gen"
|
||||
puts $fh " m u(.a({a\[$width-2:0\], a\[$width-1\]}), .b(b), .c(c), .d(d), .y(y));"
|
||||
puts $fh " end"
|
||||
puts $fh " endgenerate"
|
||||
puts $fh "endmodule"
|
||||
close $fh
|
||||
|
||||
log -header "randomized boundary case $case_id"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog $tmp_v
|
||||
hierarchy -top top
|
||||
yosys proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save start
|
||||
flatten
|
||||
design -save gold
|
||||
design -load start
|
||||
opt_boundary -max_cells 4
|
||||
opt_clean
|
||||
flatten
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gold -as gold A:top
|
||||
design -copy-from gate -as gate A:top
|
||||
yosys rename -hide
|
||||
equiv_make gold gate equiv
|
||||
equiv_simple equiv
|
||||
equiv_status -assert equiv
|
||||
log -pop
|
||||
}
|
||||
|
||||
set ops [list "&" "|" "^"]
|
||||
for {set i 0} {$i < 12} {incr i} {
|
||||
set width [expr {2 + ($i % 5)}]
|
||||
set op0 [lindex $ops [expr {$i % 3}]]
|
||||
set op1 [lindex $ops [expr {($i / 3) % 3}]]
|
||||
set op2 [lindex $ops [expr {($i / 9) % 3}]]
|
||||
emit_case $i $width $op0 $op1 $op2
|
||||
}
|
||||
|
||||
file delete -force $tmp_v
|
||||
Loading…
Reference in New Issue