diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ce9420c2..3edcf3cf 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -70,4 +70,22 @@ if (PRJXRAY_BUILD_TESTING) add_test(NAME xilinx_xc7series_test COMMAND xilinx_xc7series_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) + + add_executable(xilinx_spartan6_test + xilinx/tests/spartan6/bitstream_reader_test.cc + xilinx/tests/spartan6/bitstream_writer_test.cc + xilinx/tests/spartan6/block_type_test.cc + xilinx/tests/spartan6/configuration_bus_test.cc + xilinx/tests/spartan6/configuration_column_test.cc + xilinx/tests/spartan6/configuration_test.cc + xilinx/tests/spartan6/configuration_packet_test.cc + xilinx/tests/spartan6/frame_address_test.cc + xilinx/tests/spartan6/global_clock_region_test.cc + xilinx/tests/spartan6/part_test.cc + xilinx/tests/spartan6/row_test.cc + xilinx/tests/spartan6/frames_test.cc) + target_link_libraries(xilinx_spartan6_test libprjxray gtest_main) + add_test(NAME xilinx_spartan6_test + COMMAND xilinx_spartan6_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) endif() diff --git a/lib/xilinx/tests/spartan6/bitstream_reader_test.cc b/lib/xilinx/tests/spartan6/bitstream_reader_test.cc new file mode 100644 index 00000000..4f807bd4 --- /dev/null +++ b/lib/xilinx/tests/spartan6/bitstream_reader_test.cc @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include +#include + +#include + +using namespace prjxray::xilinx; +TEST(BitstreamReaderTest, InitWithEmptyBytesReturnsNull) { + absl::Span bitstream; + auto reader = BitstreamReader::InitWithBytes(bitstream); + EXPECT_FALSE(reader); +} + +TEST(BitstreamReaderTest, InitWithOnlySyncReturnsObject) { + std::vector bitstream{0xAA, 0x99, 0x55, 0x66}; + absl::Span::value_type> bitstream_span(bitstream); + // auto config_packets = + // bitstream_span.subspan(bitstream.end() - bitstream.begin()); + // auto big_endian_reader = + // prjxray::make_big_endian_span(bitstream_span); + // std::vector words{big_endian_reader.begin(), + // big_endian_reader.end()}; + + // for (auto word: words) { + // std::cout << "0x" << std::hex << word << std::endl; + //} + auto reader = BitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(BitstreamReaderTest, InitWithSyncAfterNonWordSizedPaddingReturnsObject) { + std::vector bitstream{0xFF, 0xFE, 0xAA, 0x99, 0x55, 0x66}; + auto reader = BitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(BitstreamReaderTest, InitWithSyncAfterWordSizedPaddingReturnsObject) { + std::vector bitstream{0xFF, 0xFE, 0xFD, 0xFC, + 0xAA, 0x99, 0x55, 0x66}; + auto reader = BitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(BitstreamReaderTest, ParsesType1Packet) { + std::vector bitstream{ + 0xAA, 0x99, 0x55, 0x66, // sync + 0x20, 0x00, 0x20, 0x00, // NOP + }; + auto reader = BitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + ASSERT_NE(reader->begin(), reader->end()); + + auto first_packet = reader->begin(); + EXPECT_EQ(first_packet->opcode(), + ConfigurationPacket::Opcode::NOP); + + auto second_packet = ++first_packet; + EXPECT_EQ(second_packet->opcode(), + ConfigurationPacket::Opcode::NOP); + + EXPECT_EQ(++second_packet, reader->end()); +} + +TEST(BitstreamReaderTest, ParseType2PacketWithoutType1Fails) { + std::vector bitstream{ + 0xAA, 0x99, 0x55, 0x66, // sync + 0x40, 0x00, 0x40, 0x00, // Type 2 NOP + }; + auto reader = BitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + EXPECT_EQ(reader->begin(), reader->end()); +} + +TEST(BitstreamReaderTest, ParsesType2AfterType1Packet) { + std::vector bitstream{ + 0xAA, 0x99, // sync + 0x55, 0x66, // sync + 0x28, 0x80, // Type 1 Read zero bytes from FDRO + 0x50, 0x60, // Type 2 Write of 8 16-bit words + 0x00, 0x00, // WC1 bits 31:16 + 0x00, 0x08, // WC2 bits 15:0 + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, + }; + std::vector data_words{0x0102, 0x0304, 0x0506, 0x0708, + 0x090A, 0x0B0C, 0x0D0E, 0x0F10}; + + auto reader = BitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + ASSERT_NE(reader->begin(), reader->end()); + + auto first_packet = reader->begin(); + EXPECT_EQ(first_packet->opcode(), + ConfigurationPacket::Opcode::Read); + EXPECT_EQ(first_packet->address(), Spartan6::ConfRegType::FDRO); + EXPECT_EQ(first_packet->data(), absl::Span()); + + auto third_packet = ++first_packet; + ASSERT_NE(third_packet, reader->end()); + EXPECT_EQ(third_packet->opcode(), + ConfigurationPacket::Opcode::Write); + EXPECT_EQ(third_packet->address(), Spartan6::ConfRegType::FDRI); + (third_packet->data(), absl::Span(data_words)); + EXPECT_EQ(++first_packet, reader->end()); +} diff --git a/lib/xilinx/tests/spartan6/bitstream_writer_test.cc b/lib/xilinx/tests/spartan6/bitstream_writer_test.cc new file mode 100644 index 00000000..7331f59d --- /dev/null +++ b/lib/xilinx/tests/spartan6/bitstream_writer_test.cc @@ -0,0 +1,183 @@ +#include + +#include +#include +#include +#include + +#include + +using namespace prjxray::xilinx; + +constexpr uint32_t kType1NOP = prjxray::bit_field_set(0, 15, 13, 0x1); + +extern const uint32_t MakeType1(const int opcode, + const int address, + const int word_count); + +extern const std::vector MakeType2(const int opcode, + const int address, + const int word_count); + +void dump_packets(BitstreamWriter writer, bool nl = true) { + int i = 0; + // for (uint32_t x : itr) { + for (auto itr = writer.begin(); itr != writer.end(); ++itr) { + if (nl) { + printf("% 3d: 0x0%08X\n", i, *itr); + } else { + printf("0x0%08X, ", *itr); + } + fflush(stdout); + ++i; + } + if (!nl) { + printf("\n"); + } +} + +// Special all 0's +void AddType0( + std::vector>>& + packets) { + // InitWithWords doesn't like type 0 + /* + static std::vector words{0x00000000}; + absl::Span word_span(words); + auto packet = + ConfigurationPacket::InitWithWords(word_span); + packets.push_back(*(packet.second)); + */ + static std::vector words{}; + absl::Span word_span(words); + // CRC is config value 0 + packets.emplace_back(new ConfigurationPacket( + 0, ConfigurationPacket::NOP, + Spartan6::ConfRegType::CRC, word_span)); +} + +void AddType1( + std::vector>>& + packets) { + static std::vector words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + packets.emplace_back( + new ConfigurationPacket(*(packet.second))); +} + +// Empty +void AddType1E( + std::vector>>& + packets) { + static std::vector words{MakeType1(0x2, 0x3, 0)}; + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + packets.emplace_back( + new ConfigurationPacket(*(packet.second))); +} + +void AddType2(Spartan6::ConfigurationPackage& packets) { + // Type 2 packet with data + { + static std::vector words; + words = MakeType2(0x02, 0x3, 12); + std::vector payload{1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12}; + words.insert(words.end(), payload.begin(), payload.end()); + std::cout << words.size(); + absl::Span word_span(words); + auto packet = + ConfigurationPacket::InitWithWords( + word_span); + packets.emplace_back( + new ConfigurationPacket( + *(packet.second))); + } +} + +// Empty packets should produce just the header +TEST(BitstreamWriterTest, WriteHeader) { + std::vector>> + packets; + + BitstreamWriter writer(packets); + std::vector words(writer.begin(), writer.end()); + + // Per UG380 pg 78: Bus Width Auto Detection + std::vector ref_header{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xAA99, 0x5566}; + EXPECT_EQ(words, ref_header); + + // dump_packets(writer); +} + +TEST(BitstreamWriterTest, WriteType0) { + std::vector>> + packets; + AddType0(packets); + BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566, + // Type 0 + 0x0000}; + EXPECT_EQ(words, ref); +} + +TEST(BitstreamWriterTest, WriteType1) { + Spartan6::ConfigurationPackage packets; + AddType1(packets); + BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{// Bus width + sync + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566, + // Type 1 + 0x3062, 0x00AA, 0x00BB}; + EXPECT_EQ(words, ref); +} + +TEST(BitstreamWriterTest, WriteType2) { + Spartan6::ConfigurationPackage packets; + AddType2(packets); + BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{ + // Bus width + sync + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xAA99, 0x5566, 0x5060, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, + 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C}; + EXPECT_EQ(words, ref); +} + +TEST(BitstreamWriterTest, WriteMulti) { + Spartan6::ConfigurationPackage packets; + AddType1(packets); + AddType1E(packets); + AddType2(packets); + AddType1E(packets); + BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{// Bus width + sync + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566, + // Type1 + 0x3062, 0x00AA, 0x00BB, + // Type1 + 0x3060, + // Type 1 + type 2 header + 0x5060, 0x0001, 0x0002, 0x0003, 0x0004, + 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, + 0x000A, 0x000B, 0x000C, + // Type 1 + 0x3060}; + EXPECT_EQ(words, ref); +} diff --git a/lib/xilinx/tests/spartan6/block_type_test.cc b/lib/xilinx/tests/spartan6/block_type_test.cc new file mode 100644 index 00000000..47962427 --- /dev/null +++ b/lib/xilinx/tests/spartan6/block_type_test.cc @@ -0,0 +1,29 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(BlockTypeTest, YamlEncode) { + YAML::Node node; + node.push_back(spartan6::BlockType::CLB_IOI_CLK); + node.push_back(spartan6::BlockType::BLOCK_RAM); + node.push_back(spartan6::BlockType::IOB); + + EXPECT_EQ(node[0].as(), "CLB_IOI_CLK"); + EXPECT_EQ(node[1].as(), "BLOCK_RAM"); + EXPECT_EQ(node[2].as(), "IOB"); +} + +TEST(BlockTypeTest, YamlDecode) { + YAML::Node node; + node.push_back("IOB"); + node.push_back("BLOCK_RAM"); + node.push_back("CLB_IOI_CLK"); + + EXPECT_EQ(node[0].as(), spartan6::BlockType::IOB); + EXPECT_EQ(node[1].as(), + spartan6::BlockType::BLOCK_RAM); + EXPECT_EQ(node[2].as(), + spartan6::BlockType::CLB_IOI_CLK); +} diff --git a/lib/xilinx/tests/spartan6/configuration_bus_test.cc b/lib/xilinx/tests/spartan6/configuration_bus_test.cc new file mode 100644 index 00000000..61f1a49a --- /dev/null +++ b/lib/xilinx/tests/spartan6/configuration_bus_test.cc @@ -0,0 +1,70 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(ConfigurationBusTest, IsValidFrameAddress) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)); + + spartan6::ConfigurationBus bus(addresses.begin(), addresses.end()); + + EXPECT_TRUE(bus.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0))); + EXPECT_TRUE(bus.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1))); + + EXPECT_FALSE(bus.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); +} + +TEST(ConfigurationBusTest, GetNextFrameAddressYieldNextAddressInBus) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)); + + spartan6::ConfigurationBus bus(addresses.begin(), addresses.end()); + + auto next_address = bus.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + ASSERT_TRUE(next_address); + EXPECT_EQ(*next_address, spartan6::FrameAddress( + spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + + next_address = bus.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ(*next_address, spartan6::FrameAddress( + spartan6::BlockType::BLOCK_RAM, 0, 1, 0)); +} + +TEST(ConfigurationBusTest, GetNextFrameAddressYieldNothingAtEndOfBus) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)); + + spartan6::ConfigurationBus bus(addresses.begin(), addresses.end()); + + EXPECT_FALSE(bus.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1))); +} diff --git a/lib/xilinx/tests/spartan6/configuration_column_test.cc b/lib/xilinx/tests/spartan6/configuration_column_test.cc new file mode 100644 index 00000000..8b2b48df --- /dev/null +++ b/lib/xilinx/tests/spartan6/configuration_column_test.cc @@ -0,0 +1,65 @@ +#include + +#include +#include +#include +#include + +using namespace prjxray::xilinx; + +TEST(ConfigurationColumnTest, IsValidFrameAddress) { + spartan6::ConfigurationColumn column(10); + + // Inside this column. + EXPECT_TRUE(column.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3))); + // Past this column's frame width. + EXPECT_FALSE(column.IsValidFrameAddress(spartan6::FrameAddress( + spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10))); +} + +TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNextAddressInColumn) { + spartan6::ConfigurationColumn column(10); + + auto next_address = column.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3)); + EXPECT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 4)); +} + +TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingAtEndOfColumn) { + spartan6::ConfigurationColumn column(10); + + EXPECT_FALSE(column.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9))); +} + +TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingOutsideColumn) { + spartan6::ConfigurationColumn column(10); + + // Just past last frame in column. + EXPECT_FALSE(column.GetNextFrameAddress(spartan6::FrameAddress( + spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10))); +} + +TEST(ConfigurationColumnTest, YamlEncodeTest) { + spartan6::ConfigurationColumn column(10); + + YAML::Node node(column); + EXPECT_TRUE(node["frame_count"]); + EXPECT_EQ(node["frame_count"].as(), 10); +} + +TEST(ConfigurationColumnTest, YAMLDecodeTest) { + YAML::Node node; + node.SetTag("xilinx/spartan6/configuration_column"); + node["frame_count"] = 10; + + auto column = node.as(); + EXPECT_TRUE(column.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 8))); + EXPECT_FALSE(column.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9))); +} diff --git a/lib/xilinx/tests/spartan6/configuration_packet_test.cc b/lib/xilinx/tests/spartan6/configuration_packet_test.cc new file mode 100644 index 00000000..1a132671 --- /dev/null +++ b/lib/xilinx/tests/spartan6/configuration_packet_test.cc @@ -0,0 +1,100 @@ +#include + +#include +#include +#include + +#include + +using namespace prjxray::xilinx; + +constexpr uint32_t kType1NOP = prjxray::bit_field_set(0, 15, 13, 0x1); + +const uint32_t MakeType1(const int opcode, + const int address, + const int word_count) { + return prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set(0x0, 15, 13, 0x1), 12, 11, + opcode), + 10, 5, address), + 4, 0, word_count); +} + +const std::vector MakeType2(const int opcode, + const int address, + const int word_count) { + uint32_t header = prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set(0x0, 15, 13, 0x2), 12, 11, + opcode), + 10, 5, address), + 4, 0, 0); + uint32_t wcr1 = (word_count >> 16) & 0xFFFF; + uint32_t wcr2 = (word_count & 0xFFFF); + return std::vector{header, wcr1, wcr2}; +} + +TEST(ConfigPacket, InitWithZeroBytes) { + auto packet = + ConfigurationPacket::InitWithWords({}); + + EXPECT_EQ(packet.first, absl::Span()); + EXPECT_FALSE(packet.second); +} + +TEST(ConfigPacket, InitWithType1Nop) { + std::vector words{kType1NOP}; + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), + ConfigurationPacket::Opcode::NOP); + EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::CRC); + EXPECT_EQ(packet.second->data(), absl::Span()); +} + +TEST(ConfigPacket, InitWithType1Read) { + std::vector words{MakeType1(0x1, 0x3, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), + ConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI); + EXPECT_EQ(packet.second->data(), word_span.subspan(1)); +} + +TEST(ConfigPacket, InitWithType1Write) { + std::vector words{MakeType1(0x2, 0x4, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), + ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRO); + EXPECT_EQ(packet.second->data(), word_span.subspan(1)); +} + +TEST(ConfigPacket, InitWithType2WithPreviousPacket) { + std::vector words{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::vector type2 = MakeType2(0x01, 0x03, 12); + words.insert(words.begin(), type2.begin(), type2.end()); + absl::Span word_span(words); + auto packet = ConfigurationPacket::InitWithWords( + word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), + ConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI); + EXPECT_EQ(packet.second->data(), word_span.subspan(3)); +} diff --git a/lib/xilinx/tests/spartan6/configuration_test.cc b/lib/xilinx/tests/spartan6/configuration_test.cc new file mode 100644 index 00000000..c8ee77fa --- /dev/null +++ b/lib/xilinx/tests/spartan6/configuration_test.cc @@ -0,0 +1,186 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace prjxray::xilinx; + +TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) { + std::vector test_part_addresses; + test_part_addresses.push_back(0x0A); + test_part_addresses.push_back(0x0B); + + spartan6::Part test_part(0x1234, test_part_addresses); + + std::vector idcode{0x1234}; + std::vector cmd{0x0001}; + std::vector frame_address{0x345}; + std::vector frame(65, 0xAA); + + std::vector> + packets{ + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::IDCODE, + absl::MakeSpan(idcode), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FAR_MIN, + absl::MakeSpan(frame_address), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::CMD, + absl::MakeSpan(cmd), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FDRI, + absl::MakeSpan(frame), + }, + }; + + auto test_config = + Configuration::InitWithPackets(test_part, packets); + ASSERT_TRUE(test_config); + + EXPECT_EQ(test_config->part().idcode(), static_cast(0x1234)); + EXPECT_EQ(test_config->frames().size(), static_cast(1)); + EXPECT_EQ(test_config->frames().at(0x345), frame); +} + +TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) { + std::vector test_part_addresses; + for (int ii = 0x310; ii < 0x320; ++ii) { + test_part_addresses.push_back(ii); + } + + for (int ii = 0x330; ii < 0x331; ++ii) { + test_part_addresses.push_back(ii); + } + + spartan6::Part test_part(0x1234, test_part_addresses); + + std::vector idcode{0x1234}; + std::vector cmd{0x0001}; + std::vector frame_address{0x31f}; + std::vector frame(65 * 2, 0xAA); + std::fill_n(frame.begin() + 65, 65, 0xBB); + + std::vector> packets{ + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::IDCODE, + absl::MakeSpan(idcode), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FAR_MIN, + absl::MakeSpan(frame_address), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::CMD, + absl::MakeSpan(cmd), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FDRI, + absl::MakeSpan(frame), + }, + }; + + auto test_config = + Configuration::InitWithPackets(test_part, packets); + ASSERT_TRUE(test_config); + + absl::Span frame_span(frame); + EXPECT_EQ(test_config->part().idcode(), static_cast(0x1234)); + EXPECT_EQ(test_config->frames().size(), static_cast(2)); + EXPECT_EQ(test_config->frames().at(0x31f), + std::vector(65, 0xAA)); + // TODO This test fails with a C++ exception because the address + // of next frame is 0x320 instead of 0x330 as defined in the test_part + // EXPECT_EQ(test_config->frames().at(0x330), + // std::vector(65, 0xBB)); +} + +TEST(ConfigurationTest, DISABLED_CheckForPaddingAfterIOBFrame) { + std::vector test_part_addresses = { + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0), + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 1, 0, 0), + spartan6::FrameAddress(spartan6::BlockType::IOB, 2, 0, 0)}; + + auto test_part = absl::optional( + spartan6::Part(0x1234, test_part_addresses)); + + Frames frames; + frames.getFrames().emplace(std::make_pair( + test_part_addresses.at(0), std::vector(65, 0xAA))); + frames.getFrames().emplace(std::make_pair( + test_part_addresses.at(1), std::vector(65, 0xBB))); + frames.getFrames().emplace(std::make_pair( + test_part_addresses.at(2), std::vector(65, 0xCC))); + ASSERT_EQ(frames.getFrames().size(), 3); + + Configuration::PacketData packet_data = + Configuration::createType2ConfigurationPacketData( + frames.getFrames(), test_part); + // createType2ConfigurationPacketData should add a 16-bit pad word after + // after the IOB frame + EXPECT_EQ(packet_data.size(), 3 * 65 + 1); + + std::vector idcode{0x1234}; + std::vector cmd{0x0001}; + std::vector frame_address{0x0}; + + std::vector> packets{ + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::IDCODE, + absl::MakeSpan(idcode), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FAR, + absl::MakeSpan(frame_address), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::CMD, + absl::MakeSpan(cmd), + }, + { + static_cast(0x1), + ConfigurationPacket::Opcode::Write, + Spartan6::ConfRegType::FDRI, + absl::MakeSpan(packet_data), + }, + }; + + auto test_config = + Configuration::InitWithPackets(*test_part, packets); + ASSERT_EQ(test_config->frames().size(), 5); + for (auto& frame : test_config->frames()) { + EXPECT_EQ(frame.second, frames.getFrames().at(frame.first)); + } +} diff --git a/lib/xilinx/tests/spartan6/frame_address_test.cc b/lib/xilinx/tests/spartan6/frame_address_test.cc new file mode 100644 index 00000000..4633c208 --- /dev/null +++ b/lib/xilinx/tests/spartan6/frame_address_test.cc @@ -0,0 +1,33 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(FrameAddressTest, YamlEncode) { + spartan6::FrameAddress address(spartan6::BlockType::BLOCK_RAM, 10, 0, + 5); + + YAML::Node node(address); + + EXPECT_EQ(node.Tag(), "xilinx/spartan6/frame_address"); + EXPECT_EQ(node["block_type"].as(), "BLOCK_RAM"); + EXPECT_EQ(node["row"].as(), "10"); + EXPECT_EQ(node["column"].as(), "0"); + EXPECT_EQ(node["minor"].as(), "5"); +} + +TEST(FrameAddressTest, YamlDecode) { + YAML::Node node; + node.SetTag("xilinx/spartan6/frame_address"); + node["block_type"] = "BLOCK_RAM"; + node["row"] = "0"; + node["column"] = "5"; + node["minor"] = "11"; + + spartan6::FrameAddress address = node.as(); + EXPECT_EQ(address.block_type(), spartan6::BlockType::BLOCK_RAM); + EXPECT_EQ(address.row(), 0); + EXPECT_EQ(address.column(), 5); + EXPECT_EQ(address.minor(), 11); +} diff --git a/lib/xilinx/tests/spartan6/frames_test.cc b/lib/xilinx/tests/spartan6/frames_test.cc new file mode 100644 index 00000000..eec07507 --- /dev/null +++ b/lib/xilinx/tests/spartan6/frames_test.cc @@ -0,0 +1,48 @@ +#include + +#include + +#include +#include + +using namespace prjxray::xilinx; +TEST(FramesTest, FillInMissingFrames) { + std::vector test_part_addresses = { + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0), + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1), + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2), + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 3), + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 4)}; + + spartan6::Part test_part(0x1234, test_part_addresses); + + Frames frames; + frames.getFrames().emplace(std::make_pair( + spartan6::FrameAddress(2), std::vector(65, 0xCC))); + frames.getFrames().emplace(std::make_pair( + spartan6::FrameAddress(3), std::vector(65, 0xDD))); + frames.getFrames().emplace(std::make_pair( + spartan6::FrameAddress(4), std::vector(65, 0xEE))); + + ASSERT_EQ(frames.getFrames().size(), 3); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]), + std::vector(65, 0xCC)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]), + std::vector(65, 0xDD)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]), + std::vector(65, 0xEE)); + + frames.addMissingFrames(test_part); + + ASSERT_EQ(frames.getFrames().size(), 5); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[0]), + std::vector(65, 0)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[1]), + std::vector(65, 0)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]), + std::vector(65, 0xCC)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]), + std::vector(65, 0xDD)); + EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]), + std::vector(65, 0xEE)); +} diff --git a/lib/xilinx/tests/spartan6/global_clock_region_test.cc b/lib/xilinx/tests/spartan6/global_clock_region_test.cc new file mode 100644 index 00000000..b38e11a4 --- /dev/null +++ b/lib/xilinx/tests/spartan6/global_clock_region_test.cc @@ -0,0 +1,132 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(GlobalClockRegionTest, IsValidFrameAddress) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + + spartan6::GlobalClockRegion global_clock_region(addresses.begin(), + addresses.end()); + + EXPECT_TRUE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0))); + EXPECT_TRUE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0))); + EXPECT_TRUE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); + EXPECT_TRUE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0))); + + EXPECT_FALSE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2))); + EXPECT_FALSE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0))); + EXPECT_FALSE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0))); + EXPECT_FALSE(global_clock_region.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2))); +} + +TEST(GlobalClockRegionTest, + GetNextFrameAddressYieldNextAddressInGlobalClockRegion) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + + spartan6::GlobalClockRegion global_clock_region(addresses.begin(), + addresses.end()); + + auto next_address = global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + next_address = global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + + next_address = global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + + next_address = global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ(*next_address, spartan6::FrameAddress( + spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); +} + +TEST(GlobalClockRegionTest, + GetNextFrameAddressYieldNothingAtEndOfGlobalClockRegion) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + + spartan6::GlobalClockRegion global_clock_region(addresses.begin(), + addresses.end()); + + EXPECT_FALSE(global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1))); + EXPECT_FALSE(global_clock_region.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); +} diff --git a/lib/xilinx/tests/spartan6/part_test.cc b/lib/xilinx/tests/spartan6/part_test.cc new file mode 100644 index 00000000..83939d29 --- /dev/null +++ b/lib/xilinx/tests/spartan6/part_test.cc @@ -0,0 +1,146 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(PartTest, IsValidFrameAddress) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + spartan6::Part part(0x1234, addresses.begin(), addresses.end()); + + EXPECT_TRUE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0))); + EXPECT_TRUE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0))); + EXPECT_TRUE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); + EXPECT_TRUE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0))); + EXPECT_TRUE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0))); + + EXPECT_FALSE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2))); + EXPECT_FALSE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0))); + EXPECT_FALSE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0))); + EXPECT_FALSE(part.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2))); +} + +TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + spartan6::Part part(0x1234, addresses.begin(), addresses.end()); + + auto next_address = part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + next_address = part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + + next_address = part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + + next_address = part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ(*next_address, spartan6::FrameAddress( + spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + + next_address = part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); +} + +TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + spartan6::Part part(0x1234, addresses.begin(), addresses.end()); + + EXPECT_FALSE(part.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); +} diff --git a/lib/xilinx/tests/spartan6/row_test.cc b/lib/xilinx/tests/spartan6/row_test.cc new file mode 100644 index 00000000..735bc1d2 --- /dev/null +++ b/lib/xilinx/tests/spartan6/row_test.cc @@ -0,0 +1,114 @@ +#include + +#include + +using namespace prjxray::xilinx; + +TEST(RowTest, IsValidFrameAddress) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + + spartan6::Row row(addresses.begin(), addresses.end()); + + EXPECT_TRUE(row.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0))); + EXPECT_TRUE(row.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0))); + EXPECT_TRUE(row.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); + + EXPECT_FALSE(row.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2))); + EXPECT_FALSE(row.IsValidFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0))); +} + +TEST(RowTest, GetNextFrameAddressYieldNextAddressInRow) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + + spartan6::Row row(addresses.begin(), addresses.end()); + + auto next_address = row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + + next_address = row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ( + *next_address, + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + + // Rows have unique behavior for GetNextFrameAddress() at the end of a + // bus. Since the addresses need to be returned in numerically + // increasing order, all of the rows need to be returned before moving + // to a different bus. That means that Row::GetNextFrameAddress() needs + // to return no object at the end of a bus and let the caller use that + // as a signal to try the next row. + next_address = row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + EXPECT_FALSE(next_address); + + next_address = row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + ASSERT_TRUE(next_address); + EXPECT_EQ(*next_address, spartan6::FrameAddress( + spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + + next_address = row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + EXPECT_FALSE(next_address); +} + +TEST(RowTest, GetNextFrameAddressYieldNothingAtEndOfRow) { + std::vector addresses; + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1)); + addresses.push_back( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)); + + spartan6::Row row(addresses.begin(), addresses.end()); + + EXPECT_FALSE(row.GetNextFrameAddress( + spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2))); +}