3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-05-22 01:49:45 +00:00

Merge pull request #5832 from YosysHQ/emil/simple-extract

rtlil: rewrite SigSpec::extract for perf and packing
This commit is contained in:
Emil J 2026-04-24 19:03:53 +00:00 committed by GitHub
commit ec0a102302
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 198 additions and 30 deletions

View file

@ -5272,26 +5272,32 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
log_assert(length >= 0);
log_assert(offset + length <= size());
SigSpec extracted;
Chunks cs = chunks();
auto it = cs.begin();
for (; offset; offset -= it->width, ++it) {
if (offset < it->width) {
int chunk_length = min(it->width - offset, length);
extracted.append(it->extract(offset, chunk_length));
length -= chunk_length;
++it;
break;
}
}
for (; length; length -= it->width, ++it) {
if (length >= it->width) {
extracted.append(*it);
std::vector<SigBit> extracted;
SigBit first;
bool is_packing = true;
for (int i = offset; i < offset + length; i++) {
bool was_packing_before = is_packing;
SigBit bit = (*this)[i];
if (i == offset) {
first = bit;
if (!bit.wire)
is_packing = false;
} else {
extracted.append(it->extract(0, length));
break;
if (bit.wire != first.wire)
is_packing = false;
if (bit.wire)
if (bit.offset != first.offset + (i - offset))
is_packing = false;
}
if (was_packing_before && !is_packing)
for (int j = offset; j < i; j++)
extracted.push_back((*this)[j]);
if (!is_packing)
extracted.push_back((*this)[i]);
}
if (is_packing)
return SigChunk(first.wire, first.offset, length);
return extracted;
}

View file

@ -1400,6 +1400,8 @@ struct RTLIL::SigSpecConstIterator
struct RTLIL::SigSpec
{
private:
friend class SigSpecRepTest;
FRIEND_TEST(SigSpecRepTest, Extract);
enum Representation : char {
CHUNK,
BITS,

View file

@ -0,0 +1,71 @@
#ifndef RTLIL_HELPERS_H
#define RTLIL_HELPERS_H
#include <gtest/gtest.h>
#include "kernel/rtlil.h"
YOSYS_NAMESPACE_BEGIN
class SigSpecRepTest : public ::testing::Test {
protected:
Design* d;
Module* m;
void SetUp() override {
d = new Design;
m = d->addModule("$test");
}
void TearDown() override {
delete d;
}
// Create n wires with given width
std::vector<Wire*> createWires(int count, int width = 4) {
std::vector<Wire*> wires;
for (int i = 0; i < count; i++) {
Wire* w = m->addWire(stringf("$w%d", i), width);
wires.push_back(w);
}
return wires;
}
// Append all wires to a SigSpec
SigSpec wiresAsSigSpec(const std::vector<Wire*>& wires) {
SigSpec sig;
for (auto w : wires)
sig.append(w);
return sig;
}
// Create a SigSpec of constants
SigSpec constsAsSigSpec(int count, int width = 4) {
SigSpec sig;
for (int i = 0; i < count; i++)
sig.append(Const(i, width));
return sig;
}
// Convert wires to pool of SigBits
pool<SigBit> wiresToPool(const std::vector<Wire*>& wires) {
pool<SigBit> pool;
for (auto w : wires)
for (auto &bit : SigSpec(w))
pool.insert(bit);
return pool;
}
// Convert wires to set of SigBits
std::set<SigBit> wiresToSet(const std::vector<Wire*>& wires) {
std::set<SigBit> set;
for (auto w : wires)
for (auto &bit : SigSpec(w))
set.insert(bit);
return set;
}
};
YOSYS_NAMESPACE_END
#endif /* RTLIL_HELPERS_H */

View file

@ -0,0 +1,89 @@
#include <gtest/gtest.h>
#include "kernel/rtlil.h"
#include "tests/unit/kernel/rtlilHelpers.h"
YOSYS_NAMESPACE_BEGIN
namespace RTLIL {
TEST_F(SigSpecRepTest, Extract)
{
{
std::vector<Wire*> wires;
SigSpec sig;
for (int i = 0; i < 4; i++)
wires.push_back(m->addWire(stringf("$w%d", i), 4));
for (auto w : wires)
sig.append(w);
SigSpec extractedFirst = sig.extract(0, 4);
SigSpec extractedSecond = sig.extract(4, 4);
SigSpec extractedLast = sig.extract(12, 4);
EXPECT_EQ(extractedFirst.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedSecond.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedLast.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedFirst.as_wire(), wires[0]);
EXPECT_EQ(extractedSecond.as_wire(), wires[1]);
EXPECT_EQ(extractedLast.as_wire(), wires[3]);
}
{
std::vector<SigSpec> consts;
SigSpec sig;
for (int i = 0; i < 4; i++)
consts.push_back(Const(i, 4));
for (auto c : consts)
sig.append(c);
SigSpec extractedFirst = sig.extract(0, 4);
SigSpec extractedSecond = sig.extract(4, 4);
SigSpec extractedLast = sig.extract(12, 4);
EXPECT_EQ(extractedFirst.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedSecond.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedLast.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedFirst, consts[0]);
EXPECT_EQ(extractedSecond, consts[1]);
EXPECT_EQ(extractedLast, consts[3]);
}
{
SigSpec sig;
sig.append(Const(0, 4));
Wire* w = m->addWire("$foo", 4);
sig.append(w);
sig.append(Const(15, 4));
SigSpec extractedFirst = sig.extract(0, 4);
SigSpec extractedSecond = sig.extract(4, 4);
SigSpec extractedLast = sig.extract(8, 4);
EXPECT_EQ(extractedFirst.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedSecond.rep_, SigSpec::Representation::CHUNK);
EXPECT_EQ(extractedLast.rep_, SigSpec::Representation::CHUNK);
SigSpec extractedFirstTwo = sig.extract(0, 8);
EXPECT_EQ(extractedFirstTwo.rep_, SigSpec::Representation::BITS);
SigSpec extractedLastTwo = sig.extract(4, 8);
EXPECT_EQ(extractedLastTwo.rep_, SigSpec::Representation::BITS);
EXPECT_EQ(extractedFirstTwo[0], State::S0);
EXPECT_EQ(extractedFirstTwo[1], State::S0);
EXPECT_EQ(extractedFirstTwo[2], State::S0);
EXPECT_EQ(extractedFirstTwo[3], State::S0);
EXPECT_EQ(extractedFirstTwo[4], SigBit(w, 0));
EXPECT_EQ(extractedFirstTwo[5], SigBit(w, 1));
EXPECT_EQ(extractedFirstTwo[6], SigBit(w, 2));
EXPECT_EQ(extractedFirstTwo[7], SigBit(w, 3));
EXPECT_EQ(extractedLastTwo[0], SigBit(w, 0));
EXPECT_EQ(extractedLastTwo[1], SigBit(w, 1));
EXPECT_EQ(extractedLastTwo[2], SigBit(w, 2));
EXPECT_EQ(extractedLastTwo[3], SigBit(w, 3));
EXPECT_EQ(extractedLastTwo[4], State::S1);
EXPECT_EQ(extractedLastTwo[5], State::S1);
EXPECT_EQ(extractedLastTwo[6], State::S1);
EXPECT_EQ(extractedLastTwo[7], State::S1);
}
}
};
YOSYS_NAMESPACE_END

View file

@ -5,7 +5,7 @@
YOSYS_NAMESPACE_BEGIN
// Test fixture with helper functions
class SigSpecRemove2Test : public ::testing::Test {
class SigSpecRepTest : public ::testing::Test {
protected:
Design* d;
Module* m;
@ -64,7 +64,7 @@ protected:
}
};
TEST_F(SigSpecRemove2Test, WithSigSpecPattern)
TEST_F(SigSpecRepTest, WithSigSpecPattern)
{
auto wires = createWires(4);
SigSpec sig = wiresAsSigSpec(wires);
@ -84,7 +84,7 @@ TEST_F(SigSpecRemove2Test, WithSigSpecPattern)
EXPECT_EQ(sig, expected);
}
TEST_F(SigSpecRemove2Test, WithPoolPattern)
TEST_F(SigSpecRepTest, WithPoolPattern)
{
auto wires = createWires(3);
SigSpec sig = wiresAsSigSpec(wires);
@ -103,7 +103,7 @@ TEST_F(SigSpecRemove2Test, WithPoolPattern)
EXPECT_EQ(sig, expected);
}
TEST_F(SigSpecRemove2Test, WithSetPattern)
TEST_F(SigSpecRepTest, WithSetPattern)
{
auto wires = createWires(3);
SigSpec sig = wiresAsSigSpec(wires);
@ -122,7 +122,7 @@ TEST_F(SigSpecRemove2Test, WithSetPattern)
EXPECT_EQ(sig, expected);
}
TEST_F(SigSpecRemove2Test, ManyElements)
TEST_F(SigSpecRepTest, ManyElements)
{
const int num_wires = 100;
auto wires = createWires(num_wires);
@ -146,7 +146,7 @@ TEST_F(SigSpecRemove2Test, ManyElements)
}
// Test remove2 with very large dataset to check scaling
TEST_F(SigSpecRemove2Test, VeryLargeScalingTest)
TEST_F(SigSpecRepTest, VeryLargeScalingTest)
{
const int num_wires = 50000;
auto wires = createWires(num_wires);
@ -173,7 +173,7 @@ TEST_F(SigSpecRemove2Test, VeryLargeScalingTest)
}
// Test multiple sequential removals (simulates removeSignalFromCaseTree)
TEST_F(SigSpecRemove2Test, MultipleSequentialRemovals)
TEST_F(SigSpecRepTest, MultipleSequentialRemovals)
{
const int num_wires = 512;
auto wires = createWires(num_wires);
@ -204,7 +204,7 @@ TEST_F(SigSpecRemove2Test, MultipleSequentialRemovals)
}
// Test remove2 with very large dataset to check scaling
TEST_F(SigSpecRemove2Test, PoolOverloadLargeDataset)
TEST_F(SigSpecRepTest, PoolOverloadLargeDataset)
{
const int num_wires = 50000;
auto wires = createWires(num_wires, 1);
@ -228,7 +228,7 @@ TEST_F(SigSpecRemove2Test, PoolOverloadLargeDataset)
}
// Test set overload (same perf characteristics as pool)
TEST_F(SigSpecRemove2Test, SetOverloadLargeDataset)
TEST_F(SigSpecRepTest, SetOverloadLargeDataset)
{
const int num_wires = 50000;
auto wires = createWires(num_wires, 1);
@ -252,7 +252,7 @@ TEST_F(SigSpecRemove2Test, SetOverloadLargeDataset)
}
// Worst case: remove almost all elements
TEST_F(SigSpecRemove2Test, RemoveAlmostAllElements)
TEST_F(SigSpecRepTest, RemoveAlmostAllElements)
{
const int num_wires = 10000;
auto wires = createWires(num_wires, 1);
@ -269,7 +269,7 @@ TEST_F(SigSpecRemove2Test, RemoveAlmostAllElements)
EXPECT_EQ(sig[0].wire, wires[num_wires - 1]);
}
TEST_F(SigSpecRemove2Test, EmptyPattern)
TEST_F(SigSpecRepTest, EmptyPattern)
{
auto wires = createWires(1);
SigSpec sig(wires[0]);
@ -285,7 +285,7 @@ TEST_F(SigSpecRemove2Test, EmptyPattern)
}
// Test that NULL wire bits (constants) are not removed
TEST_F(SigSpecRemove2Test, NullWireBitsStay)
TEST_F(SigSpecRepTest, NullWireBitsStay)
{
auto wires = createWires(1);
SigSpec sig;
@ -309,7 +309,7 @@ TEST_F(SigSpecRemove2Test, NullWireBitsStay)
EXPECT_EQ(sig, expected);
}
TEST_F(SigSpecRemove2Test, PartialBitRemoval)
TEST_F(SigSpecRepTest, PartialBitRemoval)
{
Wire* w = m->addWire("$w1", 8);
SigSpec sig(w);