mirror of
https://github.com/YosysHQ/yosys.git
synced 2026-10-08 02:52:53 +02:00
379 lines
9.8 KiB
C++
379 lines
9.8 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "kernel/rtlil.h"
|
|
#include "kernel/sigtools.h"
|
|
#include "passes/opt/clean/opt_clean.h"
|
|
#include "kernel/yosys.h"
|
|
|
|
namespace {
|
|
struct YosysSetupEnvironment : ::testing::Environment {
|
|
void SetUp() override { Yosys::yosys_setup(); }
|
|
};
|
|
const ::testing::Environment *yosys_setup_env =
|
|
::testing::AddGlobalTestEnvironment(new YosysSetupEnvironment);
|
|
}
|
|
|
|
YOSYS_NAMESPACE_BEGIN
|
|
|
|
class OptCleanSignormTest : public ::testing::Test {
|
|
protected:
|
|
RTLIL::Design *design;
|
|
RTLIL::Module *module;
|
|
|
|
void SetUp() override {
|
|
design = new RTLIL::Design;
|
|
module = design->addModule(id("\\top"));
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (design->flagSigNormalized)
|
|
design->sigNormalize(false);
|
|
delete design;
|
|
}
|
|
|
|
RTLIL::IdString id(const std::string &name) { return RTLIL::IdString(name); }
|
|
|
|
RTLIL::Wire *wire(const std::string &name, int width = 1) {
|
|
return module->addWire(id(name), width);
|
|
}
|
|
|
|
RTLIL::Wire *input(const std::string &name, int width = 1) {
|
|
RTLIL::Wire *w = wire(name, width);
|
|
w->port_input = true;
|
|
return w;
|
|
}
|
|
|
|
RTLIL::Wire *output(const std::string &name, int width = 1) {
|
|
RTLIL::Wire *w = wire(name, width);
|
|
w->port_output = true;
|
|
return w;
|
|
}
|
|
|
|
void normalize() {
|
|
module->fixup_ports();
|
|
design->sigNormalize(true);
|
|
}
|
|
|
|
RmStats sweep(bool purge = false) {
|
|
std::vector<RTLIL::Module *> modules{module};
|
|
CleanRunContext clean_ctx(design, modules, Flags{purge, false});
|
|
int workers = ThreadPool::work_pool_size(0, opt_clean_work_units(module), 10000);
|
|
ParallelDispatchThreadPool::Subpool subpool(clean_ctx.thread_pool, workers);
|
|
rmunused_module_signorm(module, subpool, clean_ctx);
|
|
return clean_ctx.stats;
|
|
}
|
|
|
|
bool hasCell(const std::string &name) { return module->cell(id(name)) != nullptr; }
|
|
bool hasWire(const std::string &name) { return module->wire(id(name)) != nullptr; }
|
|
|
|
int countCells(RTLIL::IdString type = RTLIL::IdString()) {
|
|
int count = 0;
|
|
for (RTLIL::Cell *cell : module->cells()) {
|
|
if (!type.empty()) {
|
|
if (cell->type == type)
|
|
count++;
|
|
continue;
|
|
}
|
|
if (!cell->type.in(ID($input_port), ID($connect)))
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
void expectIndexConsistent() {
|
|
ASSERT_TRUE(module->signorm_indexed());
|
|
|
|
for (auto &[bit, portbits] : module->signorm_fanout()) {
|
|
for (auto &pb : portbits) {
|
|
ASSERT_TRUE(pb.cell->hasPort(pb.port));
|
|
const RTLIL::SigSpec &sig = pb.cell->getPort(pb.port);
|
|
ASSERT_GE(pb.offset, 0);
|
|
ASSERT_LT(pb.offset, sig.size());
|
|
EXPECT_EQ(sig[pb.offset], bit);
|
|
}
|
|
}
|
|
|
|
for (RTLIL::Cell *cell : module->cells()) {
|
|
for (auto &[port, sig] : cell->connections()) {
|
|
if (cell->port_dir(port) != RTLIL::PD_INPUT)
|
|
continue;
|
|
int i = 0;
|
|
for (auto bit : sig) {
|
|
if (bit.is_wire())
|
|
EXPECT_TRUE(module->fanout(bit).count(RTLIL::PortBit(cell, port, i)));
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (RTLIL::Wire *wire : module->wires()) {
|
|
if (!wire->known_driver()) {
|
|
EXPECT_FALSE(wire->port_input && !wire->port_output);
|
|
continue;
|
|
}
|
|
RTLIL::Cell *driver = wire->driverCell();
|
|
ASSERT_TRUE(driver->hasPort(wire->driverPort()));
|
|
const RTLIL::SigSpec &dsig = driver->getPort(wire->driverPort());
|
|
ASSERT_TRUE(dsig.is_wire());
|
|
EXPECT_EQ(dsig.as_wire(), wire);
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(OptCleanSignormTest, CountsWhatItRemoves) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
RTLIL::Wire *live = wire("$live");
|
|
RTLIL::Wire *dead = wire("$dead");
|
|
|
|
RTLIL::Cell *keeper = module->addCell(id("$keeper"), ID($_NOT_));
|
|
keeper->setPort(ID::A, a);
|
|
keeper->setPort(ID::Y, live);
|
|
|
|
RTLIL::Cell *waste = module->addCell(id("$waste"), ID($_NOT_));
|
|
waste->setPort(ID::A, a);
|
|
waste->setPort(ID::Y, dead);
|
|
|
|
module->connect(y, live);
|
|
normalize();
|
|
|
|
RmStats stats = sweep();
|
|
|
|
EXPECT_EQ(stats.count_rm_cells, 1);
|
|
EXPECT_EQ(stats.count_rm_wires, 1);
|
|
EXPECT_TRUE(hasCell("$keeper"));
|
|
EXPECT_FALSE(hasCell("$waste"));
|
|
EXPECT_FALSE(hasWire("$dead"));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, SecondSweepIsANoOp) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
RTLIL::Wire *dead = wire("$dead");
|
|
|
|
RTLIL::Cell *keeper = module->addCell(id("$keeper"), ID($_NOT_));
|
|
keeper->setPort(ID::A, a);
|
|
keeper->setPort(ID::Y, y);
|
|
|
|
RTLIL::Cell *waste = module->addCell(id("$waste"), ID($_NOT_));
|
|
waste->setPort(ID::A, a);
|
|
waste->setPort(ID::Y, dead);
|
|
|
|
normalize();
|
|
|
|
RmStats first = sweep();
|
|
ASSERT_GT(first.count_rm_cells, 0);
|
|
|
|
int cells = countCells(), wires = module->wires_size();
|
|
RmStats second = sweep();
|
|
|
|
EXPECT_EQ(second.count_rm_cells, 0);
|
|
EXPECT_EQ(second.count_rm_wires, 0);
|
|
EXPECT_EQ(countCells(), cells);
|
|
EXPECT_EQ(module->wires_size(), wires);
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, InputPortMarkersSurvive) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
|
|
RTLIL::Cell *inv = module->addCell(id("$inv"), ID($_NOT_));
|
|
inv->setPort(ID::A, a);
|
|
inv->setPort(ID::Y, y);
|
|
|
|
normalize();
|
|
|
|
ASSERT_EQ(countCells(ID($input_port)), 1);
|
|
ASSERT_TRUE(a->known_driver());
|
|
EXPECT_EQ(a->driverCell()->type, ID($input_port));
|
|
|
|
sweep();
|
|
|
|
EXPECT_EQ(countCells(ID($input_port)), 1);
|
|
EXPECT_TRUE(a->known_driver());
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, LivenessCrossesConnectCells) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
RTLIL::Wire *left = wire("$left");
|
|
RTLIL::Wire *right = wire("$right");
|
|
|
|
RTLIL::Cell *lhs = module->addCell(id("$lhs"), ID($_NOT_));
|
|
lhs->setPort(ID::A, a);
|
|
lhs->setPort(ID::Y, left);
|
|
|
|
RTLIL::Cell *rhs = module->addCell(id("$rhs"), ID($_NOT_));
|
|
rhs->setPort(ID::A, a);
|
|
rhs->setPort(ID::Y, right);
|
|
|
|
module->connect(left, right);
|
|
module->connect(y, left);
|
|
normalize();
|
|
ASSERT_EQ(countCells(ID($connect)), 1);
|
|
|
|
sweep();
|
|
|
|
EXPECT_TRUE(hasCell("$lhs"));
|
|
EXPECT_TRUE(hasCell("$rhs"));
|
|
EXPECT_EQ(countCells(ID($connect)), 1);
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, DeadConnectCellIsCollected) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *left = wire("$left");
|
|
RTLIL::Wire *right = wire("$right");
|
|
|
|
RTLIL::Cell *lhs = module->addCell(id("$lhs"), ID($_NOT_));
|
|
lhs->setPort(ID::A, a);
|
|
lhs->setPort(ID::Y, left);
|
|
|
|
RTLIL::Cell *rhs = module->addCell(id("$rhs"), ID($_NOT_));
|
|
rhs->setPort(ID::A, a);
|
|
rhs->setPort(ID::Y, right);
|
|
|
|
module->connect(left, right);
|
|
normalize();
|
|
ASSERT_EQ(countCells(ID($connect)), 1);
|
|
|
|
RmStats stats = sweep();
|
|
|
|
EXPECT_EQ(stats.count_rm_cells, 3);
|
|
EXPECT_EQ(countCells(ID($connect)), 0);
|
|
EXPECT_FALSE(hasCell("$lhs"));
|
|
EXPECT_FALSE(hasCell("$rhs"));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, PurgeDropsPublicAliases) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
RTLIL::Wire *alias = wire("\\alias");
|
|
|
|
RTLIL::Cell *inv = module->addCell(id("$inv"), ID($_NOT_));
|
|
inv->setPort(ID::A, a);
|
|
inv->setPort(ID::Y, y);
|
|
|
|
module->connect(alias, y);
|
|
normalize();
|
|
|
|
sweep();
|
|
EXPECT_TRUE(hasWire("\\alias"));
|
|
|
|
sweep(true);
|
|
EXPECT_FALSE(hasWire("\\alias"));
|
|
EXPECT_TRUE(hasWire("\\y"));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, InitMovesToRepresentativeAndPinsIt) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
RTLIL::Wire *driven = wire("$driven");
|
|
RTLIL::Wire *alias = wire("$alias");
|
|
|
|
RTLIL::Cell *inv = module->addCell(id("$inv"), ID($_NOT_));
|
|
inv->setPort(ID::A, a);
|
|
inv->setPort(ID::Y, driven);
|
|
|
|
alias->attributes[ID::init] = RTLIL::Const(State::S1, 1);
|
|
|
|
module->connect(alias, driven);
|
|
module->connect(y, driven);
|
|
normalize();
|
|
|
|
sweep();
|
|
|
|
EXPECT_FALSE(hasWire("$alias"));
|
|
ASSERT_TRUE(hasWire("$driven"));
|
|
RTLIL::Wire *rep = module->wire(id("$driven"));
|
|
ASSERT_EQ(rep->attributes.count(ID::init), 1);
|
|
EXPECT_EQ(rep->attributes.at(ID::init), RTLIL::Const(State::S1, 1));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, InitPinsWireButAllXDoesNot) {
|
|
RTLIL::Wire *pinned = wire("$pinned");
|
|
RTLIL::Wire *undef = wire("$undef");
|
|
|
|
pinned->attributes[ID::init] = RTLIL::Const(State::S0, 1);
|
|
undef->attributes[ID::init] = RTLIL::Const(State::Sx, 1);
|
|
|
|
normalize();
|
|
sweep();
|
|
|
|
EXPECT_TRUE(hasWire("$pinned"));
|
|
EXPECT_FALSE(hasWire("$undef"));
|
|
EXPECT_EQ(module->wire(id("$pinned"))->attributes.count(ID::init), 1);
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, BuffersCollapseUnlessKept) {
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y1 = output("\\y1");
|
|
RTLIL::Wire *y2 = output("\\y2");
|
|
RTLIL::Wire *mid = wire("$mid");
|
|
|
|
RTLIL::Cell *buf = module->addCell(id("$buf_cell"), ID($_BUF_));
|
|
buf->setPort(ID::A, a);
|
|
buf->setPort(ID::Y, mid);
|
|
|
|
RTLIL::Cell *pos = module->addCell(id("$pos_cell"), ID($pos));
|
|
pos->setParam(ID::A_SIGNED, 0);
|
|
pos->setParam(ID::A_WIDTH, 1);
|
|
pos->setParam(ID::Y_WIDTH, 1);
|
|
pos->setPort(ID::A, mid);
|
|
pos->setPort(ID::Y, y1);
|
|
|
|
RTLIL::Cell *kept = module->addCell(id("$kept_cell"), ID($_BUF_));
|
|
kept->setPort(ID::A, a);
|
|
kept->setPort(ID::Y, y2);
|
|
kept->set_bool_attribute(ID::keep);
|
|
|
|
normalize();
|
|
sweep();
|
|
|
|
EXPECT_FALSE(hasCell("$buf_cell"));
|
|
EXPECT_FALSE(hasCell("$pos_cell"));
|
|
EXPECT_TRUE(hasCell("$kept_cell"));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
TEST_F(OptCleanSignormTest, LongChainAtThreadPoolScale) {
|
|
const int length = 25000;
|
|
|
|
RTLIL::Wire *a = input("\\a");
|
|
RTLIL::Wire *y = output("\\y");
|
|
|
|
RTLIL::SigSpec prev = a;
|
|
for (int i = 0; i < length; i++) {
|
|
RTLIL::Wire *next = wire(stringf("$chain%d", i));
|
|
RTLIL::Cell *stage = module->addCell(id(stringf("$stage%d", i)), ID($_NOT_));
|
|
stage->setPort(ID::A, prev);
|
|
stage->setPort(ID::Y, next);
|
|
prev = next;
|
|
}
|
|
|
|
RTLIL::Wire *dead = wire("$dead");
|
|
RTLIL::Cell *waste = module->addCell(id("$waste"), ID($_NOT_));
|
|
waste->setPort(ID::A, a);
|
|
waste->setPort(ID::Y, dead);
|
|
|
|
module->connect(y, prev);
|
|
normalize();
|
|
|
|
RmStats stats = sweep();
|
|
|
|
EXPECT_EQ(stats.count_rm_cells, 1);
|
|
EXPECT_EQ(countCells(), length);
|
|
EXPECT_FALSE(hasCell("$waste"));
|
|
EXPECT_TRUE(hasCell("$stage0"));
|
|
EXPECT_TRUE(hasCell(stringf("$stage%d", length - 1)));
|
|
expectIndexConsistent();
|
|
}
|
|
|
|
YOSYS_NAMESPACE_END
|