diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9b345cda..8a90ce7c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,8 +2,10 @@ add_library(libprjxray database.cc memory_mapped_file.cc segbits_file_reader.cc - xilinx/xc7series/configuration_packet.cc xilinx/xc7series/bitstream_reader.cc + xilinx/xc7series/configuration_frame_address.cc + xilinx/xc7series/configuration_packet.cc + xilinx/xc7series/configuration_register.cc ) target_include_directories(libprjxray PUBLIC "include") target_link_libraries(libprjxray absl::optional absl::strings absl::span) diff --git a/lib/include/prjxray/xilinx/xc7series/block_type.h b/lib/include/prjxray/xilinx/xc7series/block_type.h new file mode 100644 index 00000000..b44aec29 --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/block_type.h @@ -0,0 +1,19 @@ +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_ +#define PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_ + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +enum class BlockType : unsigned int { + CLB_IO_CLK = 0b000, + BLOCK_RAM = 0b001, + CFG_CLB = 0b010, + /* reserved = 0b011, */ +}; + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_ diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_frame_address.h b/lib/include/prjxray/xilinx/xc7series/configuration_frame_address.h new file mode 100644 index 00000000..8068089b --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/configuration_frame_address.h @@ -0,0 +1,33 @@ +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_ +#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_ + +#include + +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +class ConfigurationFrameAddress { + public: + ConfigurationFrameAddress(uint32_t address) + : address_(address) {}; + + operator uint32_t() const { return address_; } + + BlockType block_type() const; + bool is_bottom_half_rows() const; + uint8_t row_address() const; + uint16_t column_address() const; + uint8_t minor_address() const; + + private: + uint32_t address_; +}; + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_ diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_packet.h b/lib/include/prjxray/xilinx/xc7series/configuration_packet.h index 22fc808d..0816d93f 100644 --- a/lib/include/prjxray/xilinx/xc7series/configuration_packet.h +++ b/lib/include/prjxray/xilinx/xc7series/configuration_packet.h @@ -5,6 +5,7 @@ #include #include +#include namespace prjxray { namespace xilinx { @@ -23,7 +24,7 @@ class ConfigurationPacket { /* reserved = 3 */ }; - ConfigurationPacket(Opcode opcode, uint32_t address, + ConfigurationPacket(Opcode opcode, ConfigurationRegister address, const absl::Span &data) : opcode_(opcode), address_(address), data_(std::move(data)) {} @@ -39,12 +40,12 @@ class ConfigurationPacket { const ConfigurationPacket *previous_packet = nullptr); const Opcode opcode() const { return opcode_; } - const uint32_t address() const { return address_; } + const ConfigurationRegister address() const { return address_; } const absl::Span &data() const { return data_; } private: Opcode opcode_; - uint32_t address_; + ConfigurationRegister address_; absl::Span data_; }; diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_register.h b/lib/include/prjxray/xilinx/xc7series/configuration_register.h new file mode 100644 index 00000000..80a7a316 --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/configuration_register.h @@ -0,0 +1,39 @@ +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_REGISTER_H_ +#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_REGISTER_H_ + +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +enum class ConfigurationRegister : unsigned int { + CRC = 0b00000, + FAR = 0b00001, + FDRI = 0b00010, + FDRO = 0b00011, + CMD = 0b00100, + CTL0 = 0b00101, + MASK = 0b00110, + STAT = 0b00111, + LOUT = 0b01000, + COR0 = 0b01001, + MFWR = 0b01010, + CBC = 0b01011, + IDCODE = 0b01100, + AXSS = 0b01101, + COR1 = 0b01110, + WBSTAR = 0b10000, + TIMER = 0b10001, + BOOTSTS = 0b10110, + CTL1 = 0b11000, + BSPI = 0b11111, +}; + +std::ostream& operator<<(std::ostream &o, const ConfigurationRegister &value); + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_REGISTER_H_ diff --git a/lib/xilinx/xc7series/bitstream_reader_test.cc b/lib/xilinx/xc7series/bitstream_reader_test.cc index 96071baa..b83a179f 100644 --- a/lib/xilinx/xc7series/bitstream_reader_test.cc +++ b/lib/xilinx/xc7series/bitstream_reader_test.cc @@ -3,19 +3,19 @@ #include #include #include +#include -using prjxray::xilinx::xc7series::BitstreamReader; -using prjxray::xilinx::xc7series::ConfigurationPacket; +namespace xc7series = prjxray::xilinx::xc7series; TEST(BitstreamReaderTest, InitWithEmptyBytesReturnsNull) { absl::Span bitstream; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::BitstreamReader::InitWithBytes(bitstream); EXPECT_FALSE(reader); } TEST(BitstreamReaderTest, InitWithOnlySyncReturnsObject) { std::vector bitstream{0xAA, 0x99, 0x55, 0x66}; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::BitstreamReader::InitWithBytes(bitstream); EXPECT_TRUE(reader); } @@ -25,7 +25,7 @@ TEST(BitstreamReaderTest, 0xFF, 0xFE, 0xAA, 0x99, 0x55, 0x66 }; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::BitstreamReader::InitWithBytes(bitstream); EXPECT_TRUE(reader); } @@ -35,7 +35,7 @@ TEST(BitstreamReaderTest, 0xFF, 0xFE, 0xFD, 0xFC, 0xAA, 0x99, 0x55, 0x66 }; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::BitstreamReader::InitWithBytes(bitstream); EXPECT_TRUE(reader); } @@ -44,12 +44,13 @@ TEST(BitstreamReaderTest, ParsesType1Packet) { 0xAA, 0x99, 0x55, 0x66, // sync 0b001'00'000, 0b00000000, 0b000'00'000, 0b00000000, // NOP }; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::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); + EXPECT_EQ(first_packet->opcode(), + xc7series::ConfigurationPacket::Opcode::NOP); EXPECT_EQ(++first_packet, reader->end()); } @@ -59,7 +60,7 @@ TEST(BitstreamReaderTest, ParseType2PacketWithoutType1Fails) { 0xAA, 0x99, 0x55, 0x66, // sync 0b010'00'000, 0b00000000, 0b000'00'000, 0b00000000, // NOP }; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::BitstreamReader::InitWithBytes(bitstream); ASSERT_TRUE(reader); EXPECT_EQ(reader->begin(), reader->end()); } @@ -78,19 +79,23 @@ TEST(BitstreamReaderTest, ParsesType2AfterType1Packet) { std::vector data_words{ 0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10}; - auto reader = BitstreamReader::InitWithBytes(bitstream); + auto reader = xc7series::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(), static_cast(0x3)); + EXPECT_EQ(first_packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Read); + EXPECT_EQ(first_packet->address(), + xc7series::ConfigurationRegister::FDRO); EXPECT_EQ(first_packet->data(), absl::Span()); auto second_packet = ++first_packet; ASSERT_NE(second_packet, reader->end()); - EXPECT_EQ(second_packet->opcode(), ConfigurationPacket::Opcode::Read); - EXPECT_EQ(second_packet->address(), static_cast(0x3)); + EXPECT_EQ(second_packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Read); + EXPECT_EQ(second_packet->address(), + xc7series::ConfigurationRegister::FDRO); EXPECT_EQ(first_packet->data(), absl::Span(data_words)); EXPECT_EQ(++first_packet, reader->end()); diff --git a/lib/xilinx/xc7series/configuration_frame_address.cc b/lib/xilinx/xc7series/configuration_frame_address.cc new file mode 100644 index 00000000..18918c03 --- /dev/null +++ b/lib/xilinx/xc7series/configuration_frame_address.cc @@ -0,0 +1,31 @@ +#include + +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +BlockType ConfigurationFrameAddress::block_type() const { + return static_cast(bit_field_get(address_, 25, 23)); +} + +bool ConfigurationFrameAddress::is_bottom_half_rows() const { + return bit_field_get(address_, 22, 22); +} + +uint8_t ConfigurationFrameAddress::row_address() const { + return bit_field_get(address_, 21, 17); +} + +uint16_t ConfigurationFrameAddress::column_address() const { + return bit_field_get(address_, 16, 7); +} + +uint8_t ConfigurationFrameAddress::minor_address() const { + return bit_field_get(address_, 6, 0); +} + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray diff --git a/lib/xilinx/xc7series/configuration_packet.cc b/lib/xilinx/xc7series/configuration_packet.cc index 3e7faf64..e3646ee7 100644 --- a/lib/xilinx/xc7series/configuration_packet.cc +++ b/lib/xilinx/xc7series/configuration_packet.cc @@ -1,5 +1,6 @@ #include +#include #include #include @@ -19,7 +20,9 @@ ConfigurationPacket::InitWithWords(absl::Span words, case 0b001: { Opcode opcode = static_cast( bit_field_get(words[0], 28, 27)); - uint32_t address = bit_field_get(words[0], 26, 13); + ConfigurationRegister address = + static_cast( + bit_field_get(words[0], 26, 13)); uint32_t data_word_count = bit_field_get(words[0], 10, 0); // If the full packet has not been received, return as though @@ -61,13 +64,21 @@ std::ostream& operator<<(std::ostream& o, const ConfigurationPacket &packet) { case ConfigurationPacket::Opcode::NOP: return o << "[NOP]" << std::endl; case ConfigurationPacket::Opcode::Read: - return o << "[Read Address=" << packet.address() - << " Length=" << packet.data().size() << - "]" << std::endl; + return o << "[Read Address=" + << std::setw(2) + << static_cast(packet.address()) + << " Length=" + << std::setw(10) << packet.data().size() + << " Reg=\"" << packet.address() << "\"" + << "]" << std::endl; case ConfigurationPacket::Opcode::Write: - return o << "[Write Address=" << packet.address() - << " Length=" << packet.data().size() << - "]" << std::endl; + return o << "[Write Address=" + << std::setw(2) + << static_cast(packet.address()) + << " Length=" + << std::setw(10) << packet.data().size() + << " Reg=\"" << packet.address() << "\"" + << "]" << std::endl; default: return o << "[Invalid Opcode]" << std::endl; } diff --git a/lib/xilinx/xc7series/configuration_packet_test.cc b/lib/xilinx/xc7series/configuration_packet_test.cc index c1432229..506557f1 100644 --- a/lib/xilinx/xc7series/configuration_packet_test.cc +++ b/lib/xilinx/xc7series/configuration_packet_test.cc @@ -6,7 +6,7 @@ #include #include -using prjxray::xilinx::xc7series::ConfigurationPacket; +namespace xc7series = prjxray::xilinx::xc7series; constexpr uint32_t kType1NOP = prjxray::bit_field_set(0, 31, 29, 0x1); @@ -31,7 +31,7 @@ constexpr uint32_t MakeType2(const int opcode, const int word_count) { TEST(ConfigPacket, InitWithZeroBytes) { - auto packet = ConfigurationPacket::InitWithWords({}); + auto packet = xc7series::ConfigurationPacket::InitWithWords({}); EXPECT_EQ(packet.first, absl::Span()); EXPECT_FALSE(packet.second); @@ -40,56 +40,65 @@ TEST(ConfigPacket, InitWithZeroBytes) { TEST(ConfigPacket, InitWithType1Nop) { std::vector words{kType1NOP}; absl::Span word_span(words); - auto packet = ConfigurationPacket::InitWithWords(word_span); + auto packet = xc7series::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(), static_cast(0)); + EXPECT_EQ(packet.second->opcode(), + xc7series::ConfigurationPacket::Opcode::NOP); + EXPECT_EQ(packet.second->address(), + xc7series::ConfigurationRegister::CRC); EXPECT_EQ(packet.second->data(), absl::Span()); } TEST(ConfigPacket, InitWithType1Read) { - std::vector words{MakeType1(0x1, 0x1234, 2), 0xAA, 0xBB}; + std::vector words{MakeType1(0x1, 0x2, 2), 0xAA, 0xBB}; absl::Span word_span(words); - auto packet = ConfigurationPacket::InitWithWords(word_span); + auto packet = xc7series::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(), static_cast(0x1234)); + EXPECT_EQ(packet.second->opcode(), + xc7series::ConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), + xc7series::ConfigurationRegister::FDRI); EXPECT_EQ(packet.second->data(), word_span.subspan(1)); } TEST(ConfigPacket, InitWithType1Write) { - std::vector words{MakeType1(0x2, 0x1234, 2), 0xAA, 0xBB}; + std::vector words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB}; absl::Span word_span(words); - auto packet = ConfigurationPacket::InitWithWords(word_span); + auto packet = xc7series::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(), static_cast(0x1234)); + EXPECT_EQ(packet.second->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet.second->address(), + xc7series::ConfigurationRegister::FDRO); EXPECT_EQ(packet.second->data(), word_span.subspan(1)); } TEST(ConfigPacket, InitWithType2WithoutPreviousPacketFails) { std::vector words{MakeType2(0x01, 12)}; absl::Span word_span(words); - auto packet = ConfigurationPacket::InitWithWords(word_span); + auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span); EXPECT_EQ(packet.first, words); EXPECT_FALSE(packet.second); } TEST(ConfigPacket, InitWithType2WithPreviousPacket) { - ConfigurationPacket previous_packet( - ConfigurationPacket::Opcode::Read, 0x1234, + xc7series::ConfigurationPacket previous_packet( + xc7series::ConfigurationPacket::Opcode::Read, + xc7series::ConfigurationRegister::MFWR, absl::Span()); std::vector words{ MakeType2(0x01, 12), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; absl::Span word_span(words); - auto packet = ConfigurationPacket::InitWithWords( + auto packet = xc7series::ConfigurationPacket::InitWithWords( word_span, &previous_packet); EXPECT_EQ(packet.first, absl::Span()); ASSERT_TRUE(packet.second); - EXPECT_EQ(packet.second->opcode(), ConfigurationPacket::Opcode::Read); - EXPECT_EQ(packet.second->address(), static_cast(0x1234)); + EXPECT_EQ(packet.second->opcode(), + xc7series::ConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), + xc7series::ConfigurationRegister::MFWR); EXPECT_EQ(packet.second->data(), word_span.subspan(1)); } diff --git a/lib/xilinx/xc7series/configuration_register.cc b/lib/xilinx/xc7series/configuration_register.cc new file mode 100644 index 00000000..9aef4857 --- /dev/null +++ b/lib/xilinx/xc7series/configuration_register.cc @@ -0,0 +1,57 @@ +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +std::ostream& operator<<(std::ostream &o, const ConfigurationRegister &value) { + switch (value) { + case ConfigurationRegister::CRC: + return o << "CRC"; + case ConfigurationRegister::FAR: + return o << "Frame Address"; + case ConfigurationRegister::FDRI: + return o << "Frame Data Input"; + case ConfigurationRegister::FDRO: + return o << "Frame Data Output"; + case ConfigurationRegister::CMD: + return o << "Command"; + case ConfigurationRegister::CTL0: + return o << "Control 0"; + case ConfigurationRegister::MASK: + return o << "Mask for CTL0 and CTL1"; + case ConfigurationRegister::STAT: + return o << "Status"; + case ConfigurationRegister::LOUT: + return o << "Legacy Output"; + case ConfigurationRegister::COR0: + return o << "Configuration Option 0"; + case ConfigurationRegister::MFWR: + return o << "Multiple Frame Write"; + case ConfigurationRegister::CBC: + return o << "Initial CBC Value"; + case ConfigurationRegister::IDCODE: + return o << "Device ID"; + case ConfigurationRegister::AXSS: + return o << "User Access"; + case ConfigurationRegister::COR1: + return o << "Configuration Option 1"; + case ConfigurationRegister::WBSTAR: + return o << "Warm Boot Start Address"; + case ConfigurationRegister::TIMER: + return o << "Watchdog Timer"; + case ConfigurationRegister::BOOTSTS: + return o << "Boot History Status"; + case ConfigurationRegister::CTL1: + return o << "Control 1"; + case ConfigurationRegister::BSPI: + return o << "BPI/SPI Configuration Options"; + default: + return o << "Unknown"; + } +}; + + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray