diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index c0f6e0d81..a99f0803e 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -5273,9 +5273,31 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const log_assert(offset + length <= size()); std::vector extracted; + SigBit first; + bool is_packing = true; for (int i = offset; i < offset + length; i++) { - extracted.push_back((*this)[i]); + bool was_packing_before = is_packing; + SigBit bit = (*this)[i]; + if (i == offset) { + first = bit; + if (!bit.wire) + is_packing = false; + } else { + 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; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index b9d86b91c..c10fd7df9 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1400,6 +1400,9 @@ struct RTLIL::SigSpecConstIterator struct RTLIL::SigSpec { private: + friend class SigSpecRepTest; + FRIEND_TEST(SigSpecRepTest, ExtractWires); + FRIEND_TEST(SigSpecRepTest, ExtractConsts); enum Representation : char { CHUNK, BITS, diff --git a/tests/unit/kernel/rtlilHelpers.h b/tests/unit/kernel/rtlilHelpers.h new file mode 100644 index 000000000..99585f23b --- /dev/null +++ b/tests/unit/kernel/rtlilHelpers.h @@ -0,0 +1,71 @@ +#ifndef RTLIL_HELPERS_H +#define RTLIL_HELPERS_H + +#include + +#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 createWires(int count, int width = 4) { + std::vector 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& 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 wiresToPool(const std::vector& wires) { + pool pool; + for (auto w : wires) + for (auto &bit : SigSpec(w)) + pool.insert(bit); + return pool; + } + + // Convert wires to set of SigBits + std::set wiresToSet(const std::vector& wires) { + std::set set; + for (auto w : wires) + for (auto &bit : SigSpec(w)) + set.insert(bit); + return set; + } +}; + +YOSYS_NAMESPACE_END + +#endif /* RTLIL_HELPERS_H */ diff --git a/tests/unit/kernel/sigspecExtract2Test.cc b/tests/unit/kernel/sigspecExtract2Test.cc new file mode 100644 index 000000000..a38ff1ba6 --- /dev/null +++ b/tests/unit/kernel/sigspecExtract2Test.cc @@ -0,0 +1,57 @@ +#include + +#include "kernel/rtlil.h" +#include "tests/unit/kernel/rtlilHelpers.h" + +YOSYS_NAMESPACE_BEGIN + +namespace RTLIL { + TEST_F(SigSpecRepTest, ExtractWires) + { + auto wires = createWires(4, 4); + SigSpec sig = wiresAsSigSpec(wires); + + 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); + } + + TEST_F(SigSpecRepTest, ExtractConsts) + { + { + SigSpec sig = constsAsSigSpec(4, 4); + + SigSpec extractedFirst = sig.extract(0, 4); + SigSpec extractedSecond = sig.extract(4, 4); + SigSpec extractedLast = sig.extract(12, 4); + std::cout << log_signal(extractedFirst) << "\n"; + EXPECT_EQ(extractedFirst.rep_, SigSpec::Representation::CHUNK); + EXPECT_EQ(extractedSecond.rep_, SigSpec::Representation::CHUNK); + EXPECT_EQ(extractedLast.rep_, SigSpec::Representation::CHUNK); + } + + { + SigSpec sig; + sig.append(Const(0, 4)); + sig.append(m->addWire("$foo", 4)); + sig.append(Const(0, 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(0, 8); + EXPECT_EQ(extractedLastTwo.rep_, SigSpec::Representation::BITS); + } + } +}; + +YOSYS_NAMESPACE_END diff --git a/tests/unit/kernel/sigspecRemove2Test.cc b/tests/unit/kernel/sigspecRemove2Test.cc index a6789c774..9d7bf4863 100644 --- a/tests/unit/kernel/sigspecRemove2Test.cc +++ b/tests/unit/kernel/sigspecRemove2Test.cc @@ -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);