mirror of
https://github.com/YosysHQ/yosys.git
synced 2026-09-08 04:41:06 +02:00
tests: cover twines, name publicity, hashing and sorting
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p temp
|
||||
|
||||
${YOSYS} -p "read_rtlil suffix-twines.il; write_rtlil temp/suffix-twines-write.il"
|
||||
tail -n +2 temp/suffix-twines-write.il > temp/suffix-twines-write-nogen.il
|
||||
diff suffix-twines.il temp/suffix-twines-write-nogen.il
|
||||
|
||||
${YOSYS} -p "read_rtlil suffix-twines.il; design -push; design -pop; write_rtlil temp/suffix-twines-push.il"
|
||||
tail -n +2 temp/suffix-twines-push.il > temp/suffix-twines-push-nogen.il
|
||||
diff suffix-twines.il temp/suffix-twines-push-nogen.il
|
||||
|
||||
${YOSYS} -p "read_rtlil suffix-chain.il; design -push; design -pop; write_rtlil temp/suffix-chain-push.il"
|
||||
tail -n +2 temp/suffix-chain-push.il > temp/suffix-chain-push-nogen.il
|
||||
diff suffix-chain.il temp/suffix-chain-push-nogen.il
|
||||
|
||||
${YOSYS} -p "read_rtlil suffix-chain.il; opt_clean; write_rtlil -readable temp/suffix-chain-gc-resolved.il"
|
||||
grep 'wire' temp/suffix-chain-gc-resolved.il | sort > temp/suffix-chain-gc-resolved.names
|
||||
cat > temp/suffix-chain-expected.names <<EOF
|
||||
wire input 1 \\home.repo.chain.in
|
||||
wire output 2 \\home.repo.chain.out
|
||||
EOF
|
||||
diff temp/suffix-chain-expected.names temp/suffix-chain-gc-resolved.names
|
||||
@@ -0,0 +1,13 @@
|
||||
autoidx 1
|
||||
twines
|
||||
leaf 1053 "home"
|
||||
suffix 1054 1053 ".repo"
|
||||
suffix 1055 1054 ".chain"
|
||||
suffix 1056 1055 ".in"
|
||||
suffix 1057 1055 ".out"
|
||||
end
|
||||
module \chain
|
||||
wire input 1 $pub@1056 # \home.repo.chain.in
|
||||
wire output 2 $pub@1057 # \home.repo.chain.out
|
||||
connect $pub@1057 $pub@1056 # \home.repo.chain.out \home.repo.chain.in
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
autoidx 1
|
||||
twines
|
||||
leaf 1053 "everything"
|
||||
suffix 1054 1053 ".a"
|
||||
suffix 1055 1053 ".b"
|
||||
leaf 1056 "tiny"
|
||||
end
|
||||
module $pub@1056 # \tiny
|
||||
wire input 1 $pub@1054 # \everything.a
|
||||
wire output 2 $pub@1055 # \everything.b
|
||||
connect $pub@1055 $pub@1054 # \everything.b \everything.a
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p temp
|
||||
|
||||
# If a different version of Yosys with different constids.inc wrote a file,
|
||||
# the integers have to get remapped when read into this version
|
||||
cat > temp/moved-static.il <<EOF
|
||||
autoidx 1
|
||||
twines
|
||||
leaf 42 "A"
|
||||
suffix 999999 42 ".sub"
|
||||
end
|
||||
module \$pub@999999
|
||||
wire input 1 \\a
|
||||
end
|
||||
EOF
|
||||
|
||||
${YOSYS} -p "read_rtlil temp/moved-static.il; write_rtlil temp/moved-static-out.il"
|
||||
grep -q '# \\A.sub' temp/moved-static-out.il
|
||||
|
||||
cat > temp/stale-dynamic.il <<EOF
|
||||
autoidx 1
|
||||
twines
|
||||
leaf 3 "everything"
|
||||
suffix 4 3 ".sub"
|
||||
leaf 5 "tiny"
|
||||
end
|
||||
module \$pub@5
|
||||
wire input 1 \$pub@4
|
||||
end
|
||||
EOF
|
||||
|
||||
${YOSYS} -p "read_rtlil temp/stale-dynamic.il; write_rtlil temp/stale-dynamic-out.il"
|
||||
grep -q '# \\tiny' temp/stale-dynamic-out.il
|
||||
grep -q '# \\everything.sub' temp/stale-dynamic-out.il
|
||||
|
||||
${YOSYS} -p "read_rtlil temp/moved-static.il; write_rtlil temp/roundtrip.il"
|
||||
grep -q 'leaf .* "A"' temp/roundtrip.il
|
||||
@@ -6,6 +6,7 @@ yosys_gtest(kernel
|
||||
ioTest.cc
|
||||
logTest.cc
|
||||
modindexTest.cc
|
||||
nameMasqTest.cc
|
||||
pooledNameTest.cc
|
||||
rtlilHelpers.h
|
||||
rtlilStringTest.cc
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
int env_int(const char *name, int fallback)
|
||||
{
|
||||
const char *value = getenv(name);
|
||||
if (value == nullptr || *value == 0)
|
||||
return fallback;
|
||||
return atoi(value);
|
||||
}
|
||||
|
||||
size_t rss_bytes()
|
||||
{
|
||||
size_t total_pages = 0, resident_pages = 0;
|
||||
FILE *f = fopen("/proc/self/statm", "r");
|
||||
if (f == nullptr)
|
||||
return 0;
|
||||
if (fscanf(f, "%zu %zu", &total_pages, &resident_pages) != 2)
|
||||
resident_pages = 0;
|
||||
fclose(f);
|
||||
return resident_pages * (size_t)sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
size_t peak_rss_bytes()
|
||||
{
|
||||
struct rusage ru;
|
||||
if (getrusage(RUSAGE_SELF, &ru) != 0)
|
||||
return 0;
|
||||
return (size_t)ru.ru_maxrss * 1024;
|
||||
}
|
||||
|
||||
std::string name_padding;
|
||||
bool attach_src = false;
|
||||
int src_counter = 0;
|
||||
|
||||
void set_src(RTLIL::AttrObject *object)
|
||||
{
|
||||
if (!attach_src)
|
||||
return;
|
||||
src_counter++;
|
||||
object->set_src_attribute(stringf("flatten_perf_source_file.v:%d.1-%d.20", src_counter, src_counter));
|
||||
}
|
||||
|
||||
std::string pad(std::string name)
|
||||
{
|
||||
name += name_padding;
|
||||
return name;
|
||||
}
|
||||
|
||||
double ms(std::chrono::steady_clock::duration d)
|
||||
{
|
||||
return std::chrono::duration<double, std::milli>(d).count();
|
||||
}
|
||||
|
||||
RTLIL::Module *build_leaf(RTLIL::Design *design, int chain_length)
|
||||
{
|
||||
RTLIL::Module *m = design->addModule(pad("\\flatten_perf_leaf_module"));
|
||||
|
||||
RTLIL::Wire *in = m->addWire(pad("\\leaf_module_data_input_port"));
|
||||
in->port_input = true;
|
||||
set_src(in);
|
||||
RTLIL::Wire *out = m->addWire(pad("\\leaf_module_data_output_port"));
|
||||
out->port_output = true;
|
||||
set_src(out);
|
||||
m->fixup_ports();
|
||||
|
||||
RTLIL::SigBit prev = in;
|
||||
for (int i = 0; i < chain_length; i++) {
|
||||
RTLIL::SigBit next = i + 1 == chain_length
|
||||
? RTLIL::SigBit(out)
|
||||
: RTLIL::SigBit(m->addWire(pad(stringf("\\leaf_intermediate_signal_wire_number_%d", i))));
|
||||
if (next.wire != nullptr)
|
||||
set_src(next.wire);
|
||||
set_src(m->addNotGate(pad(stringf("$leaf_inverter_cell_instance_number_%d", i)), prev, next));
|
||||
prev = next;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
RTLIL::Module *build_level(RTLIL::Design *design, RTLIL::Module *child, int level, int branch)
|
||||
{
|
||||
RTLIL::Module *m = design->addModule(pad(stringf("\\flatten_perf_hierarchy_level_%d_module", level)));
|
||||
|
||||
RTLIL::Wire *in = m->addWire(pad(stringf("\\level_%d_module_data_input_port", level)));
|
||||
in->port_input = true;
|
||||
set_src(in);
|
||||
RTLIL::Wire *out = m->addWire(pad(stringf("\\level_%d_module_data_output_port", level)));
|
||||
out->port_output = true;
|
||||
set_src(out);
|
||||
m->fixup_ports();
|
||||
|
||||
RTLIL::Wire *child_in = child->wire(child->ports.at(0));
|
||||
RTLIL::Wire *child_out = child->wire(child->ports.at(1));
|
||||
|
||||
RTLIL::SigBit prev = in;
|
||||
for (int k = 0; k < branch; k++) {
|
||||
RTLIL::SigBit next = k + 1 == branch
|
||||
? RTLIL::SigBit(out)
|
||||
: RTLIL::SigBit(m->addWire(pad(stringf("\\level_%d_interconnect_signal_wire_number_%d", level, k))));
|
||||
RTLIL::Cell *cell = m->addCell(
|
||||
pad(stringf("\\hierarchical_child_instance_at_level_%d_branch_%d", level, k)), child->name);
|
||||
set_src(cell);
|
||||
cell->setPort(child_in->name, prev);
|
||||
cell->setPort(child_out->name, next);
|
||||
prev = next;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
RTLIL::Module *build_design(RTLIL::Design *design, int depth, int branch, int chain_length)
|
||||
{
|
||||
RTLIL::Module *m = build_leaf(design, chain_length);
|
||||
for (int level = depth - 1; level >= 0; level--)
|
||||
m = build_level(design, m, level, branch);
|
||||
m->set_bool_attribute(ID::top);
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(FlattenPerf, deep_hierarchy_stress)
|
||||
{
|
||||
int depth = env_int("YOSYS_FLATTEN_PERF_DEPTH", 8);
|
||||
int branch = env_int("YOSYS_FLATTEN_PERF_BRANCH", 2);
|
||||
int chain_length = env_int("YOSYS_FLATTEN_PERF_CHAIN", 48);
|
||||
int extra_name_chars = env_int("YOSYS_FLATTEN_PERF_NAMEPAD", 32);
|
||||
const char *flatten_args = getenv("YOSYS_FLATTEN_PERF_ARGS");
|
||||
|
||||
ASSERT_GE(depth, 1);
|
||||
ASSERT_GE(branch, 2);
|
||||
ASSERT_GE(chain_length, 1);
|
||||
ASSERT_GE(extra_name_chars, 0);
|
||||
|
||||
name_padding = std::string(extra_name_chars, 'n');
|
||||
attach_src = env_int("YOSYS_FLATTEN_PERF_SRC", 0) != 0;
|
||||
|
||||
RTLIL::Design *design = new RTLIL::Design;
|
||||
|
||||
auto build_start = std::chrono::steady_clock::now();
|
||||
RTLIL::Module *top = build_design(design, depth, branch, chain_length);
|
||||
auto build_end = std::chrono::steady_clock::now();
|
||||
|
||||
size_t rss_before = rss_bytes();
|
||||
|
||||
auto flatten_start = std::chrono::steady_clock::now();
|
||||
Pass::call(design, flatten_args != nullptr ? std::string("flatten ") + flatten_args : std::string("flatten"));
|
||||
auto flatten_end = std::chrono::steady_clock::now();
|
||||
|
||||
size_t rss_after = rss_bytes();
|
||||
|
||||
size_t leaves = 1;
|
||||
for (int level = 0; level < depth; level++)
|
||||
leaves *= (size_t)branch;
|
||||
|
||||
EXPECT_EQ(design->modules().size(), 1u);
|
||||
EXPECT_EQ(design->top_module(), top);
|
||||
EXPECT_GE(top->cells().size(), leaves * (size_t)chain_length);
|
||||
|
||||
printf("[ PERF ] depth=%d branch=%d chain=%d namepad=%d src=%d args=%s leaves=%zu\n", depth, branch, chain_length,
|
||||
extra_name_chars, (int)attach_src, flatten_args != nullptr ? flatten_args : "", leaves);
|
||||
printf("[ PERF ] cells=%zu wires=%zu\n", top->cells().size(), top->wires().size());
|
||||
printf("[ PERF ] build_ms=%.1f flatten_ms=%.1f\n", ms(build_end - build_start), ms(flatten_end - flatten_start));
|
||||
printf("[ PERF ] rss_before_mb=%.1f rss_after_mb=%.1f flatten_rss_mb=%.1f peak_rss_mb=%.1f\n",
|
||||
rss_before / 1048576.0, rss_after / 1048576.0,
|
||||
(rss_after - rss_before) / 1048576.0, peak_rss_bytes() / 1048576.0);
|
||||
fflush(stdout);
|
||||
|
||||
delete design;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -0,0 +1,263 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
struct MasqFixture {
|
||||
Design design;
|
||||
Module *mod;
|
||||
Wire *wire;
|
||||
Cell *cell;
|
||||
|
||||
MasqFixture()
|
||||
{
|
||||
mod = design.addModule(std::string("\\zz_top"));
|
||||
wire = mod->addWire(std::string("\\zz_alu_result"), 4);
|
||||
cell = mod->addCell(std::string("\\zz_adder"), ID($and));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, StringQueries)
|
||||
{
|
||||
MasqFixture f;
|
||||
const RTLIL::WireNameMasq &name = f.wire->name;
|
||||
|
||||
EXPECT_TRUE(name.begins_with("\\zz_alu"));
|
||||
EXPECT_FALSE(name.begins_with("\\zz_alv"));
|
||||
EXPECT_TRUE(name.ends_with("result"));
|
||||
EXPECT_FALSE(name.ends_with("resulz"));
|
||||
EXPECT_TRUE(name.contains("alu"));
|
||||
EXPECT_FALSE(name.contains("mul"));
|
||||
EXPECT_EQ(name.substr(1, 2), "zz");
|
||||
EXPECT_EQ(name.substr(), name.escaped());
|
||||
EXPECT_EQ(name[0], '\\');
|
||||
EXPECT_EQ(name.size(), name.escaped().size());
|
||||
EXPECT_FALSE(name.empty());
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, SuffixQueries)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
IdString prefix = design.twines.add(std::string("\\zz_bus"));
|
||||
IdString suffixed = design.twines.add(Twine{Twine::Suffix{prefix, "_hi"}});
|
||||
Wire *w = mod->addWire(suffixed, 1);
|
||||
|
||||
EXPECT_EQ(w->name.escaped(), "\\zz_bus_hi");
|
||||
EXPECT_TRUE(w->name.begins_with("\\zz_bus"));
|
||||
EXPECT_TRUE(w->name.begins_with("\\zz_bus_h"));
|
||||
EXPECT_TRUE(w->name.begins_with("\\zz_bus_hi"));
|
||||
EXPECT_FALSE(w->name.begins_with("\\zz_bus_hi_"));
|
||||
EXPECT_FALSE(w->name.begins_with("\\zz_buT"));
|
||||
EXPECT_FALSE(w->name.begins_with("\\zz_bus_i"));
|
||||
EXPECT_TRUE(w->name.ends_with("_hi"));
|
||||
EXPECT_EQ(w->name.size(), w->name.escaped().size());
|
||||
EXPECT_EQ(w->name.substr(4), "bus_hi");
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, PoollessMatchesPooled)
|
||||
{
|
||||
Design design;
|
||||
IdString id = design.twines.add(std::string("\\zz_shared"));
|
||||
PooledName pooled(&design.twines, id);
|
||||
PooledName poolless(ID::A);
|
||||
|
||||
EXPECT_EQ(pooled.pool(), &design.twines);
|
||||
EXPECT_EQ(poolless.pool(), nullptr);
|
||||
|
||||
EXPECT_EQ(poolless.escaped(), "\\A");
|
||||
EXPECT_EQ(poolless.size(), poolless.escaped().size());
|
||||
EXPECT_TRUE(poolless.begins_with("\\A"));
|
||||
EXPECT_FALSE(poolless.begins_with("\\AB"));
|
||||
EXPECT_TRUE(poolless.ends_with("A"));
|
||||
|
||||
PooledName same_content(&design.twines, design.twines.add(std::string("\\A")));
|
||||
EXPECT_EQ(same_content.escaped(), poolless.escaped());
|
||||
EXPECT_EQ(same_content.size(), poolless.size());
|
||||
EXPECT_EQ(same_content.begins_with("\\A"), poolless.begins_with("\\A"));
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, PoollessOrdering)
|
||||
{
|
||||
PooledName a(ID::A);
|
||||
PooledName b(ID::B);
|
||||
|
||||
EXPECT_EQ(a.pool(), nullptr);
|
||||
EXPECT_TRUE(a.lt_by_name(b));
|
||||
EXPECT_FALSE(b.lt_by_name(a));
|
||||
EXPECT_FALSE(a.lt_by_name(a));
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, PooledOrdering)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
Wire *first = mod->addWire(std::string("\\zz_aaa"), 1);
|
||||
Wire *second = mod->addWire(std::string("\\zz_bbb"), 1);
|
||||
|
||||
EXPECT_TRUE(first->name.lt_by_name(second->name));
|
||||
EXPECT_FALSE(second->name.lt_by_name(first->name));
|
||||
EXPECT_FALSE(first->name.lt_by_name(first->name));
|
||||
|
||||
EXPECT_NE(first->name < second->name, second->name < first->name);
|
||||
EXPECT_FALSE(first->name < first->name);
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, Membership)
|
||||
{
|
||||
MasqFixture f;
|
||||
|
||||
EXPECT_TRUE(f.cell->type.in(ID($and)));
|
||||
EXPECT_FALSE(f.cell->type.in(ID($or)));
|
||||
EXPECT_TRUE(f.cell->type.in(ID($or), ID($and)));
|
||||
EXPECT_FALSE(f.cell->type.in(ID($or), ID($xor)));
|
||||
EXPECT_TRUE(f.wire->name.in(IdString(f.wire->name)));
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, Equality)
|
||||
{
|
||||
MasqFixture f;
|
||||
IdString id = f.wire->name;
|
||||
|
||||
EXPECT_TRUE(f.wire->name == id);
|
||||
EXPECT_TRUE(id == f.wire->name);
|
||||
EXPECT_FALSE(f.wire->name != id);
|
||||
EXPECT_TRUE(f.wire->name != ID::A);
|
||||
|
||||
EXPECT_TRUE(f.cell->type == ID($and));
|
||||
EXPECT_TRUE(f.cell->type != ID($or));
|
||||
|
||||
EXPECT_TRUE(f.wire->name == std::string("\\zz_alu_result"));
|
||||
EXPECT_TRUE(f.wire->name != std::string("\\zz_other"));
|
||||
|
||||
EXPECT_TRUE(f.wire->name == f.wire->name);
|
||||
EXPECT_TRUE(f.wire->name != f.cell->name);
|
||||
|
||||
PooledName null_name;
|
||||
EXPECT_TRUE(null_name == IdString::Null);
|
||||
EXPECT_TRUE(null_name.empty());
|
||||
EXPECT_FALSE(f.wire->name == IdString::Null);
|
||||
EXPECT_TRUE(f.wire->name != IdString::Null);
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, CrossMasqEquality)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
Cell *recursive = mod->addCell(std::string("\\zz_self"), mod->name);
|
||||
Cell *other = mod->addCell(std::string("\\zz_and"), ID($and));
|
||||
|
||||
EXPECT_TRUE(mod->name == recursive->type);
|
||||
EXPECT_TRUE(recursive->type == mod->name);
|
||||
EXPECT_FALSE(mod->name == other->type);
|
||||
EXPECT_TRUE(mod->name != other->type);
|
||||
EXPECT_TRUE(recursive->name != other->name);
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, Conversions)
|
||||
{
|
||||
MasqFixture f;
|
||||
|
||||
std::string as_string = f.wire->name;
|
||||
IdString as_id = f.wire->name;
|
||||
|
||||
EXPECT_EQ(as_string, "\\zz_alu_result");
|
||||
EXPECT_EQ(as_string, f.wire->name.str());
|
||||
EXPECT_EQ(as_id, f.wire->name.ref());
|
||||
EXPECT_EQ(f.wire->name.pool(), &f.design.twines);
|
||||
EXPECT_EQ(f.cell->type.pool(), &f.design.twines);
|
||||
EXPECT_EQ(f.mod->name.pool(), &f.design.twines);
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, Hashing)
|
||||
{
|
||||
MasqFixture f;
|
||||
|
||||
EXPECT_EQ(run_hash(f.wire->name), run_hash(f.wire->name.ref()));
|
||||
EXPECT_EQ(run_hash(f.cell->type), run_hash(f.cell->type.ref()));
|
||||
EXPECT_EQ(run_hash(PooledName(f.wire->name)), run_hash(f.wire->name.ref()));
|
||||
EXPECT_NE(run_hash(f.wire->name), run_hash(f.cell->name.ref()));
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, WireAssignment)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
Wire *a = mod->addWire(std::string("\\zz_a"), 1);
|
||||
Wire *b = mod->addWire(std::string("\\zz_b"), 1);
|
||||
IdString renamed = design.twines.add(std::string("\\zz_renamed"));
|
||||
|
||||
a->name = renamed;
|
||||
EXPECT_EQ(a->name.str(), "\\zz_renamed");
|
||||
|
||||
b->name = a->name;
|
||||
EXPECT_EQ(b->name.str(), "\\zz_renamed");
|
||||
EXPECT_TRUE(a->name == b->name);
|
||||
|
||||
Wire *c = mod->addWire(std::string("\\zz_c"), 1);
|
||||
c->name = std::move(b->name);
|
||||
EXPECT_EQ(c->name.str(), "\\zz_renamed");
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, TypeAssignment)
|
||||
{
|
||||
MasqFixture f;
|
||||
Cell *other = f.mod->addCell(std::string("\\zz_other"), ID($or));
|
||||
|
||||
f.cell->type = ID($xor);
|
||||
EXPECT_TRUE(f.cell->type == ID($xor));
|
||||
EXPECT_EQ(f.cell->type.str(), "$xor");
|
||||
|
||||
other->type = f.cell->type;
|
||||
EXPECT_TRUE(other->type == ID($xor));
|
||||
|
||||
Cell *third = f.mod->addCell(std::string("\\zz_third"), ID($not));
|
||||
third->type = std::move(other->type);
|
||||
EXPECT_TRUE(third->type == ID($xor));
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, ModuleAssignment)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
Module *sibling = design.addModule(std::string("\\zz_side"));
|
||||
IdString renamed = design.twines.add(std::string("\\zz_renamed"));
|
||||
|
||||
mod->name = renamed;
|
||||
EXPECT_EQ(mod->name.str(), "\\zz_renamed");
|
||||
EXPECT_TRUE(mod->name == renamed);
|
||||
|
||||
sibling->name = mod->name;
|
||||
EXPECT_EQ(sibling->name.str(), "\\zz_renamed");
|
||||
|
||||
EXPECT_EQ(PooledName(mod->name).str(), "\\zz_renamed");
|
||||
}
|
||||
|
||||
TEST(NameMasqTest, MakePair)
|
||||
{
|
||||
MasqFixture f;
|
||||
|
||||
auto left = make_pair(f.wire->name, 7);
|
||||
auto right = make_pair(7, f.wire->name);
|
||||
auto both = make_pair(f.wire->name, f.cell->type);
|
||||
|
||||
EXPECT_TRUE((std::is_same_v<decltype(left.first), IdString>));
|
||||
EXPECT_TRUE((std::is_same_v<decltype(right.second), IdString>));
|
||||
EXPECT_TRUE((std::is_same_v<decltype(both.first), IdString>));
|
||||
EXPECT_TRUE((std::is_same_v<decltype(both.second), IdString>));
|
||||
|
||||
EXPECT_EQ(left.first, f.wire->name.ref());
|
||||
EXPECT_EQ(left.second, 7);
|
||||
EXPECT_EQ(right.first, 7);
|
||||
EXPECT_EQ(right.second, f.wire->name.ref());
|
||||
EXPECT_EQ(both.first, f.wire->name.ref());
|
||||
EXPECT_EQ(both.second, f.cell->type.ref());
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
TEST(PooledNameTest, OwnPool)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString pub = pool.add(std::string("\\zz_alpha"));
|
||||
IdString priv = pool.add(std::string("$zz_beta"));
|
||||
|
||||
PooledName a(&pool, pub);
|
||||
PooledName b(&pool, priv);
|
||||
|
||||
EXPECT_EQ(a.str(), "\\zz_alpha");
|
||||
EXPECT_EQ(a.unescape(), "zz_alpha");
|
||||
EXPECT_TRUE(a.isPublic());
|
||||
EXPECT_EQ(b.str(), "$zz_beta");
|
||||
EXPECT_EQ(b.unescape(), "$zz_beta");
|
||||
EXPECT_FALSE(b.isPublic());
|
||||
}
|
||||
|
||||
TEST(PooledNameTest, DivergedPools)
|
||||
{
|
||||
TwinePool pool_a;
|
||||
TwinePool pool_b;
|
||||
pool_b.add(std::string("\\zz_filler"));
|
||||
|
||||
IdString in_a = pool_a.add(std::string("\\zz_shared"));
|
||||
IdString in_b = pool_b.add(std::string("\\zz_shared"));
|
||||
ASSERT_NE(in_a, in_b);
|
||||
|
||||
PooledName a(&pool_a, in_a);
|
||||
PooledName b(&pool_b, in_b);
|
||||
|
||||
EXPECT_EQ(a.str(), b.str());
|
||||
EXPECT_NE(a.ref(), b.ref());
|
||||
EXPECT_FALSE(a == b);
|
||||
}
|
||||
|
||||
TEST(PooledNameTest, PoollessConstids)
|
||||
{
|
||||
PooledName kind(ID($state));
|
||||
EXPECT_EQ(kind.str(), "$state");
|
||||
EXPECT_EQ(kind.unescape(), "$state");
|
||||
EXPECT_TRUE(kind == ID($state));
|
||||
}
|
||||
|
||||
TEST(PooledNameTest, PoollessLookup)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString ref = pool.add(std::string("\\zz_gamma"));
|
||||
|
||||
dict<PooledName, int> by_name;
|
||||
by_name[PooledName(&pool, ref)] = 7;
|
||||
|
||||
EXPECT_EQ(by_name.at(PooledName(ref)), 7);
|
||||
EXPECT_EQ(by_name.at(PooledName(&pool, ref)), 7);
|
||||
}
|
||||
|
||||
TEST(PooledNameTest, FromMasqs)
|
||||
{
|
||||
Design design;
|
||||
Module *mod = design.addModule(std::string("\\zz_top"));
|
||||
Wire *w = mod->addWire(std::string("\\zz_wire"), 1);
|
||||
Cell *cell = mod->addCell(std::string("\\zz_cell"), ID($and));
|
||||
|
||||
EXPECT_EQ(PooledName(mod->name).str(), "\\zz_top");
|
||||
EXPECT_EQ(PooledName(w->name).str(), "\\zz_wire");
|
||||
EXPECT_EQ(PooledName(cell->name).unescape(), "zz_cell");
|
||||
EXPECT_EQ(PooledName(cell->type).str(), "$and");
|
||||
}
|
||||
|
||||
TEST(PooledNameTest, NullName)
|
||||
{
|
||||
PooledName none;
|
||||
EXPECT_TRUE(none.empty());
|
||||
EXPECT_EQ(none.str(), "");
|
||||
EXPECT_EQ(none.unescape(), "");
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -22,7 +22,7 @@ namespace RTLIL {
|
||||
if (log_files.empty()) log_files.emplace_back(stdout);
|
||||
}
|
||||
virtual void SetUp() override {
|
||||
IdString::ensure_prepopulated();
|
||||
StaticTwines::init();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -335,9 +335,10 @@ namespace RTLIL {
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, ModuleAddWireWidthLimit) {
|
||||
std::unique_ptr<Module> mod = std::make_unique<Module>();
|
||||
EXPECT_DEATH(mod->addWire(ID(test), RTLIL::WIDTH_LIMIT), "");
|
||||
EXPECT_NO_FATAL_FAILURE(mod->addWire(ID(test), RTLIL::WIDTH_LIMIT - 1));
|
||||
std::unique_ptr<Design> design = std::make_unique<Design>();
|
||||
Module *mod = design->addModule("$test_mod");
|
||||
EXPECT_DEATH(mod->addWire("\\test", RTLIL::WIDTH_LIMIT), "");
|
||||
EXPECT_NO_FATAL_FAILURE(mod->addWire("\\test2", RTLIL::WIDTH_LIMIT - 1));
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, ConstEqualStr) {
|
||||
@@ -417,34 +418,23 @@ namespace RTLIL {
|
||||
EXPECT_FALSE(Const().is_onehot(&pos));
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, OwningIdString) {
|
||||
OwningIdString own("\\figblortle");
|
||||
OwningIdString::collect_garbage();
|
||||
EXPECT_EQ(own.str(), "\\figblortle");
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, LookupAutoidxId) {
|
||||
IdString id = NEW_ID;
|
||||
IdString id2 = IdString(id.str());
|
||||
EXPECT_EQ(id, id2);
|
||||
TwinePool twines;
|
||||
IdString id = twines.add(NEW_ID);
|
||||
TwineSearch search(&twines);
|
||||
EXPECT_EQ(id, search.find(twines.str(id)));
|
||||
EXPECT_EQ(twines.find(twines.str(id)), IdString::Null);
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, NewIdBeginsWith) {
|
||||
IdString id = NEW_ID;
|
||||
EXPECT_TRUE(id.begins_with("$auto"));
|
||||
EXPECT_FALSE(id.begins_with("xyz"));
|
||||
EXPECT_TRUE(id.begins_with("$auto$"));
|
||||
EXPECT_FALSE(id.begins_with("abcdefghijklmn"));
|
||||
EXPECT_TRUE(id.begins_with("$auto$rtlilTest"));
|
||||
EXPECT_FALSE(id.begins_with("$auto$rtlilX"));
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, NewIdIndexing) {
|
||||
IdString id = NEW_ID;
|
||||
std::string str = id.str();
|
||||
for (int i = 0; i < GetSize(str) + 1; ++i) {
|
||||
EXPECT_EQ(id[i], str.c_str()[i]);
|
||||
}
|
||||
TwinePool twines;
|
||||
std::string id = twines.str(twines.add(NEW_ID));
|
||||
EXPECT_TRUE(id.starts_with("$auto"));
|
||||
EXPECT_FALSE(id.starts_with("xyz"));
|
||||
EXPECT_TRUE(id.starts_with("$auto$"));
|
||||
EXPECT_FALSE(id.starts_with("abcdefghijklmn"));
|
||||
EXPECT_TRUE(id.starts_with("$auto$rtlilTest"));
|
||||
EXPECT_FALSE(id.starts_with("$auto$rtlilX"));
|
||||
}
|
||||
|
||||
class WireRtlVsHdlIndexConversionTest :
|
||||
@@ -473,8 +463,9 @@ namespace RTLIL {
|
||||
);
|
||||
|
||||
TEST_P(WireRtlVsHdlIndexConversionTest, WireRtlVsHdlIndexConversion) {
|
||||
std::unique_ptr<Module> mod = std::make_unique<Module>();
|
||||
Wire *wire = mod->addWire(ID(test), 10);
|
||||
std::unique_ptr<Design> design = std::make_unique<Design>();
|
||||
Module *mod = design->addModule("$test_mod");
|
||||
Wire *wire = mod->addWire("\\test", 10);
|
||||
|
||||
auto [upto, start_offset, width] = GetParam();
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
// A name reached through a suffix chain must hash the same as the identical
|
||||
// name interned as a single leaf, otherwise TwineSearch misses it.
|
||||
TEST(TwineHashTest, Fragmentation)
|
||||
{
|
||||
TwinePool pool;
|
||||
DeepTwineHash hash{&pool};
|
||||
|
||||
IdString flat = pool.add(std::string("$abcdefghij"));
|
||||
IdString base = pool.add(std::string("$abcde"));
|
||||
IdString split = pool.add(Twine::Suffix{base, "fghij"});
|
||||
|
||||
EXPECT_EQ(hash(flat.untag()), hash(split.untag()));
|
||||
EXPECT_EQ(hash(flat.untag()), hash(std::string_view("$abcdefghij")));
|
||||
}
|
||||
|
||||
// The 8-byte buffering must not leak fragment boundaries at any offset.
|
||||
TEST(TwineHashTest, SplitPoints)
|
||||
{
|
||||
const std::string content = "$0123456789abcdefghijklmnopqr";
|
||||
|
||||
TwinePool pool;
|
||||
DeepTwineHash hash{&pool};
|
||||
const size_t want = hash(std::string_view(content));
|
||||
|
||||
for (size_t cut = 1; cut < content.size(); cut++) {
|
||||
IdString base = pool.add(content.substr(0, cut));
|
||||
IdString split = pool.add(Twine::Suffix{base, content.substr(cut)});
|
||||
EXPECT_EQ(hash(split.untag()), want) << "split after " << cut;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TwineHashTest, DistinctContent)
|
||||
{
|
||||
TwinePool twines;
|
||||
DeepTwineHash hash{&twines};
|
||||
|
||||
std::set<size_t> seen;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
IdString ref = twines.add(stringf("$name%d", i));
|
||||
seen.insert(hash(ref.untag()));
|
||||
}
|
||||
EXPECT_GT(seen.size(), 4000u);
|
||||
}
|
||||
|
||||
// Not an assertion of speed, but a harness: reports how long interning and
|
||||
// rehashing a large pool takes so the cost of the hashing path is visible.
|
||||
TEST(TwineHashTest, BenchmarkIntern)
|
||||
{
|
||||
constexpr int kNames = 40000;
|
||||
|
||||
TwinePool pool;
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
IdString prefix = pool.add(std::string("$bench"));
|
||||
std::vector<IdString> refs;
|
||||
refs.reserve(kNames);
|
||||
for (int i = 0; i < kNames; i++)
|
||||
refs.push_back(pool.add(Twine::Suffix{prefix, stringf("$%d", i)}));
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
TwineSearch search(&pool);
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
for (int i = 0; i < kNames; i++)
|
||||
ASSERT_EQ(search.find(pool.str(refs[i])), refs[i]) << "at " << i;
|
||||
auto t3 = std::chrono::steady_clock::now();
|
||||
|
||||
auto ms = [](auto a, auto b) {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(b - a).count() / 1000.0;
|
||||
};
|
||||
RecordProperty("intern_ms", std::to_string(ms(t0, t1)));
|
||||
RecordProperty("search_build_ms", std::to_string(ms(t1, t2)));
|
||||
RecordProperty("search_find_ms", std::to_string(ms(t2, t3)));
|
||||
std::cerr << "[ BENCH ] intern " << ms(t0, t1) << " ms, TwineSearch build "
|
||||
<< ms(t1, t2) << " ms, " << kNames << " finds " << ms(t2, t3) << " ms\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -0,0 +1,167 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
TEST(TwinePublicityTest, LeafEscape)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString pub = pool.add(std::string("\\foo"));
|
||||
IdString priv = pool.add(std::string("$foo"));
|
||||
|
||||
EXPECT_TRUE(pub.isPublic());
|
||||
EXPECT_FALSE(priv.isPublic());
|
||||
EXPECT_EQ(pool.str(pub), "\\foo");
|
||||
EXPECT_EQ(pool.unescaped_str(pub), "foo");
|
||||
EXPECT_EQ(pool.str(priv), "$foo");
|
||||
EXPECT_EQ(pool.unescaped_str(priv), "$foo");
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, EscapedDollar)
|
||||
{
|
||||
// Verilog escaped identifier `\$foo` (public, content "$foo") must not
|
||||
// collide with the private name `$foo` as a dict key.
|
||||
TwinePool pool;
|
||||
IdString pub = pool.add(std::string("\\$foo"));
|
||||
IdString priv = pool.add(std::string("$foo"));
|
||||
|
||||
EXPECT_EQ(pub.untag(), priv.untag()); // shared content node
|
||||
EXPECT_NE(pub, priv); // distinct handles
|
||||
EXPECT_EQ(pool.str(pub), "\\$foo");
|
||||
EXPECT_EQ(pool.str(priv), "$foo");
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, TagStability)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString a = pool.add(std::string("\\foo"));
|
||||
IdString b = pool.add(std::string("\\foo"));
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, SuffixPublicity)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString pub = pool.add(std::string("\\base"));
|
||||
IdString priv = pool.add(std::string("$base"));
|
||||
|
||||
IdString pub_sfx = pool.add(Twine{Twine::Suffix{pub, "_1"}});
|
||||
IdString priv_sfx = pool.add(Twine{Twine::Suffix{priv, "_1"}});
|
||||
|
||||
EXPECT_TRUE(pub_sfx.isPublic());
|
||||
EXPECT_FALSE(priv_sfx.isPublic());
|
||||
EXPECT_EQ(pool.str(pub_sfx), "\\base_1");
|
||||
EXPECT_EQ(pool.str(priv_sfx), "$base_1");
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, StaticTags)
|
||||
{
|
||||
TwinePool pool;
|
||||
EXPECT_TRUE((ID::A).isPublic());
|
||||
EXPECT_EQ(pool.str(ID::A), "\\A");
|
||||
EXPECT_EQ(pool.unescaped_str(ID::A), "A");
|
||||
EXPECT_FALSE((ID($and)).isPublic());
|
||||
EXPECT_EQ(pool.str(ID($and)), "$and");
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, LookupTag)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString pub = pool.add(std::string("\\net"));
|
||||
IdString priv = pool.add(std::string("$net"));
|
||||
|
||||
TwineSearch search(&pool);
|
||||
EXPECT_EQ(search.find("\\net"), pub);
|
||||
EXPECT_EQ(search.find("$net"), priv);
|
||||
EXPECT_EQ(search.find("\\A"), ID::A);
|
||||
EXPECT_EQ(search.find("\\nonexistent"), IdString::Null);
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, SearchUnifies)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString flat = pool.add(Twine::Leaf{"$abc"});
|
||||
IdString head = pool.add(Twine::Leaf{"$a"});
|
||||
IdString split = pool.add(Twine::Suffix{head, "bc"});
|
||||
|
||||
ASSERT_NE(flat, split);
|
||||
ASSERT_EQ(pool.str(flat), pool.str(split));
|
||||
|
||||
TwineSearch search(&pool);
|
||||
EXPECT_EQ(search.index.count(flat), 1u);
|
||||
EXPECT_EQ(search.index.count(split), 1u);
|
||||
EXPECT_EQ(search.index.count(head), 1u);
|
||||
EXPECT_NE(search.find("$abc"), IdString::Null);
|
||||
EXPECT_EQ(search.find("$a"), head);
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, SearchPublicity)
|
||||
{
|
||||
TwinePool pool;
|
||||
IdString priv = pool.add(Twine::Leaf{"sig"});
|
||||
IdString pub = pool.add(std::string("\\sig"));
|
||||
|
||||
ASSERT_EQ(priv, pub.untag());
|
||||
|
||||
TwineSearch search(&pool);
|
||||
IdString found_pub = search.find("\\sig");
|
||||
IdString found_priv = search.find("sig");
|
||||
|
||||
EXPECT_TRUE(found_pub.isPublic());
|
||||
EXPECT_FALSE(found_priv.isPublic());
|
||||
EXPECT_EQ(found_pub.untag(), found_priv.untag());
|
||||
EXPECT_EQ(found_pub, pub);
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, CopyTag)
|
||||
{
|
||||
TwinePool src, dst;
|
||||
IdString pub = src.add(std::string("\\xfer"));
|
||||
IdString copied = dst.copy_from(src, pub);
|
||||
EXPECT_TRUE(copied.isPublic());
|
||||
EXPECT_EQ(dst.str(copied), "\\xfer");
|
||||
// Static handles pass through tag and all.
|
||||
EXPECT_EQ(dst.copy_from(src, ID::A), ID::A);
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, GcRoots)
|
||||
{
|
||||
TwinePool twines;
|
||||
IdString pub = twines.add(std::string("\\keep"));
|
||||
twines.add(std::string("\\drop"));
|
||||
Yosys::pool<IdString> roots{pub};
|
||||
EXPECT_EQ(twines.gc(roots), 1u);
|
||||
EXPECT_EQ(twines.str(pub), "\\keep");
|
||||
}
|
||||
|
||||
TEST(TwinePublicityTest, WireMasquerade)
|
||||
{
|
||||
RTLIL::Design design;
|
||||
RTLIL::Module *mod = design.addModule("\\top");
|
||||
|
||||
RTLIL::Wire *pub = mod->addWire("\\sig");
|
||||
RTLIL::Wire *priv = mod->addWire("$sig");
|
||||
|
||||
EXPECT_TRUE(pub->name.isPublic());
|
||||
EXPECT_FALSE(priv->name.isPublic());
|
||||
EXPECT_EQ(pub->name.escaped(), "\\sig");
|
||||
EXPECT_EQ(pub->name.unescape(), "sig");
|
||||
EXPECT_EQ(pub->name.str(), "\\sig");
|
||||
EXPECT_EQ(priv->name.escaped(), "$sig");
|
||||
EXPECT_EQ(priv->name.unescape(), "$sig");
|
||||
|
||||
// Distinct dict keys despite shared content.
|
||||
EXPECT_NE(pub, priv);
|
||||
TwineSearch search(&design.twines);
|
||||
EXPECT_EQ(mod->wire(search.find("\\sig")), pub);
|
||||
EXPECT_EQ(mod->wire(search.find("$sig")), priv);
|
||||
|
||||
// uniquify keeps publicity.
|
||||
IdString uniq = mod->uniquify(pub->name);
|
||||
EXPECT_TRUE(uniq.isPublic());
|
||||
EXPECT_EQ(design.twines.str(uniq), "\\sig_1");
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -0,0 +1,126 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<IdString> bench_refs(TwinePool &twines, int count)
|
||||
{
|
||||
IdString prefix = twines.add(std::string("$sortbench"));
|
||||
std::vector<IdString> refs;
|
||||
refs.reserve(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
refs.push_back(twines.add(Twine::Suffix{prefix, stringf("$%08d", (count - i) * 7919 % count)}));
|
||||
return refs;
|
||||
}
|
||||
|
||||
TEST(TwineSortTest, RenderedOrder)
|
||||
{
|
||||
TwinePool twines;
|
||||
std::vector<IdString> refs = {
|
||||
twines.add(std::string("$c")),
|
||||
twines.add(std::string("$a")),
|
||||
twines.add(std::string("$b")),
|
||||
};
|
||||
std::sort(refs.begin(), refs.end(), RTLIL::sort_by_id_str(twines));
|
||||
EXPECT_EQ(twines.str(refs[0]), "$a");
|
||||
EXPECT_EQ(twines.str(refs[1]), "$b");
|
||||
EXPECT_EQ(twines.str(refs[2]), "$c");
|
||||
}
|
||||
|
||||
TEST(TwineSortTest, PublicOrder)
|
||||
{
|
||||
TwinePool twines;
|
||||
IdString pub = twines.add(std::string("\\same"));
|
||||
IdString priv = pub.tag(false);
|
||||
|
||||
ASSERT_EQ(twines.str(pub), "\\same");
|
||||
ASSERT_EQ(twines.str(priv), "same");
|
||||
|
||||
RTLIL::sort_by_id_str less(twines);
|
||||
EXPECT_NE(less(pub, priv), less(priv, pub));
|
||||
EXPECT_TRUE(less(pub, priv));
|
||||
}
|
||||
|
||||
TEST(TwineSortTest, WeakOrdering)
|
||||
{
|
||||
TwinePool twines;
|
||||
std::vector<IdString> refs = bench_refs(twines, 500);
|
||||
RTLIL::sort_by_id_str less(twines);
|
||||
std::sort(refs.begin(), refs.end(), less);
|
||||
for (size_t i = 1; i < refs.size(); i++)
|
||||
ASSERT_FALSE(less(refs[i], refs[i - 1])) << "at " << i;
|
||||
}
|
||||
|
||||
TEST(TwineSortTest, GeneralWalk)
|
||||
{
|
||||
TwinePool twines;
|
||||
std::vector<IdString> refs;
|
||||
|
||||
IdString a = twines.add(std::string("$alpha"));
|
||||
IdString b = twines.add(std::string("\\alpha"));
|
||||
IdString c = twines.add(std::string("$alphabet"));
|
||||
refs.insert(refs.end(), {a, b, c});
|
||||
|
||||
IdString deep = a;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
deep = twines.add(Twine::Suffix{deep, stringf(".lvl%d", i)});
|
||||
refs.push_back(deep);
|
||||
refs.push_back(deep.tag(true));
|
||||
}
|
||||
|
||||
IdString other = twines.add(std::string("$alpha."));
|
||||
for (int i = 0; i < 12; i++) {
|
||||
other = twines.add(Twine::Suffix{other, stringf("lvl%d.", i)});
|
||||
refs.push_back(other);
|
||||
}
|
||||
|
||||
RTLIL::sort_by_id_str less(twines);
|
||||
for (IdString x : refs)
|
||||
for (IdString y : refs) {
|
||||
bool want = twines.str(x) < twines.str(y);
|
||||
EXPECT_EQ(less(x, y), want)
|
||||
<< twines.str(x) << " vs " << twines.str(y);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TwineSortTest, BenchmarkSort)
|
||||
{
|
||||
constexpr int kNames = 50000;
|
||||
|
||||
TwinePool twines;
|
||||
std::vector<IdString> refs = bench_refs(twines, kNames);
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
std::sort(refs.begin(), refs.end(), RTLIL::sort_by_id_str(twines));
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
size_t flattens = 0;
|
||||
std::vector<IdString> naive = bench_refs(twines, kNames);
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
std::sort(naive.begin(), naive.end(), [&](IdString a, IdString b) {
|
||||
flattens += 2;
|
||||
return twines.str(a) < twines.str(b);
|
||||
});
|
||||
auto t3 = std::chrono::steady_clock::now();
|
||||
|
||||
auto ms = [](auto a, auto b) {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(b - a).count() / 1000.0;
|
||||
};
|
||||
std::cerr << "[ BENCH ] " << kNames << " refs: walked " << ms(t0, t1)
|
||||
<< " ms vs per-comparison " << ms(t2, t3) << " ms ("
|
||||
<< flattens << " flattens vs " << kNames << ")\n";
|
||||
RecordProperty("walked_ms", std::to_string(ms(t0, t1)));
|
||||
RecordProperty("naive_ms", std::to_string(ms(t2, t3)));
|
||||
|
||||
EXPECT_LT(ms(t0, t1), ms(t2, t3));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <gtest/gtest.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);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
read_rtlil <<EOT
|
||||
module \zz_top_mod
|
||||
wire width 1 input 1 \zz_in_alpha
|
||||
wire width 1 input 2 \zz_in_beta
|
||||
wire width 1 output 3 \zz_out_gamma
|
||||
cell $and \zz_the_gate
|
||||
parameter \A_SIGNED 0
|
||||
parameter \B_SIGNED 0
|
||||
parameter \A_WIDTH 1
|
||||
parameter \B_WIDTH 1
|
||||
parameter \Y_WIDTH 1
|
||||
connect \A \zz_in_alpha
|
||||
connect \B \zz_in_beta
|
||||
connect \Y \zz_out_gamma
|
||||
end
|
||||
end
|
||||
EOT
|
||||
|
||||
design -save golden
|
||||
|
||||
design -reset
|
||||
read_rtlil <<EOT
|
||||
module \zz_filler
|
||||
wire width 1 \zz_pad_one
|
||||
wire width 1 \zz_pad_two
|
||||
wire width 1 \zz_pad_three
|
||||
wire width 1 \zz_pad_four
|
||||
end
|
||||
EOT
|
||||
delete zz_filler
|
||||
|
||||
read_rtlil <<EOT
|
||||
module \zz_top_mod
|
||||
wire width 1 input 1 \zz_in_alpha
|
||||
wire width 1 input 2 \zz_in_beta
|
||||
wire width 1 output 3 \zz_out_gamma
|
||||
cell $and \zz_the_gate
|
||||
parameter \A_SIGNED 0
|
||||
parameter \B_SIGNED 0
|
||||
parameter \A_WIDTH 1
|
||||
parameter \B_WIDTH 1
|
||||
parameter \Y_WIDTH 1
|
||||
connect \A \zz_in_alpha
|
||||
connect \B \zz_in_beta
|
||||
connect \Y \zz_out_gamma
|
||||
end
|
||||
end
|
||||
EOT
|
||||
|
||||
design_equal golden
|
||||
@@ -0,0 +1,22 @@
|
||||
logger -expect error "Module top missing wire other in second design" 1
|
||||
|
||||
read_rtlil <<EOT
|
||||
module \top
|
||||
wire width 1 input 1 \a
|
||||
wire width 1 output 2 \renamed
|
||||
connect \renamed \a
|
||||
end
|
||||
EOT
|
||||
|
||||
design -save golden
|
||||
|
||||
design -reset
|
||||
read_rtlil <<EOT
|
||||
module \top
|
||||
wire width 1 input 1 \a
|
||||
wire width 1 output 2 \other
|
||||
connect \other \a
|
||||
end
|
||||
EOT
|
||||
|
||||
design_equal golden
|
||||
Reference in New Issue
Block a user