diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ba2f0142..bbbf5bda 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(libprjxray database.cc memory_mapped_file.cc - segbits_file_reader.cc) + segbits_file_reader.cc + xilinx_7series_configuration_packet.cc +) target_include_directories(libprjxray PUBLIC "include") -target_link_libraries(libprjxray absl::strings) +target_link_libraries(libprjxray absl::optional absl::strings absl::span) if (PRJXRAY_BUILD_TESTING) add_executable(big_endian_span_test big_endian_span_test.cc) @@ -27,4 +29,11 @@ if (PRJXRAY_BUILD_TESTING) add_test(NAME segbits_file_reader_test COMMAND segbits_file_reader_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) + + add_executable(xilinx_7series_configuration_packet_test + xilinx_7series_configuration_packet_test.cc) + target_link_libraries(xilinx_7series_configuration_packet_test + libprjxray gtest_main) + add_test(NAME xilinx_7series_configuration_packet_test + COMMAND xilinx_7series_configuration_packet_test) endif() diff --git a/lib/include/prjxray/xilinx_7series_configuration_packet.h b/lib/include/prjxray/xilinx_7series_configuration_packet.h new file mode 100644 index 00000000..018308bc --- /dev/null +++ b/lib/include/prjxray/xilinx_7series_configuration_packet.h @@ -0,0 +1,54 @@ +#ifndef PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H +#define PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H + +#include + +#include +#include + +namespace prjxray { + +class Xilinx7SeriesConfigurationPacket { + public: + typedef std::pair, + absl::optional> + ParseResult; + + enum Opcode { + NOP = 0, + Read = 1, + Write = 2, + /* reserved = 3 */ + }; + + Xilinx7SeriesConfigurationPacket(Opcode opcode, uint32_t address, + const absl::Span &data) + : opcode_(opcode), address_(address), data_(std::move(data)) {} + + // Attempt to read a configuration packet from a sequence of + // 32-bit, big-endian words. If successful, returns the packet read and + // a span containing any words remaining after the packet. If a valid + // header is found but there are insufficient words provided for the + // complete packet, the original span<> is returned unchanged and no + // packet is produced. If no valid header is found, an empty span is + // returned. + static ParseResult InitWithWords( + absl::Span words, + const Xilinx7SeriesConfigurationPacket *previous_packet = nullptr); + + const Opcode opcode() const { return opcode_; } + const uint32_t address() const { return address_; } + const absl::Span &data() const { return data_; } + + private: + Opcode opcode_; + uint32_t address_; + absl::Span data_; +}; + +std::ostream& operator<<(std::ostream& o, + const Xilinx7SeriesConfigurationPacket &packet); + +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H diff --git a/lib/xilinx_7series_configuration_packet.cc b/lib/xilinx_7series_configuration_packet.cc new file mode 100644 index 00000000..987897a4 --- /dev/null +++ b/lib/xilinx_7series_configuration_packet.cc @@ -0,0 +1,76 @@ +#include + +#include + +#include + +namespace prjxray { + +std::pair, absl::optional> +Xilinx7SeriesConfigurationPacket::InitWithWords( + absl::Span words, + const Xilinx7SeriesConfigurationPacket *previous_packet) { + // Need at least one 32-bit word to have a valid packet header. + if (words.size() < 1) return {words, {}}; + + uint32_t header_type = bit_field_get(words[0], 31, 29); + switch (header_type) { + case 0b001: { + Opcode opcode = static_cast( + bit_field_get(words[0], 28, 27)); + uint32_t address = 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 + // no valid packet was found. + if (data_word_count > words.size() - 1) { + return {words, {}}; + } + + return {words.subspan(data_word_count+1), + {{opcode, address, words.subspan(1, data_word_count)}}}; + } + case 0b010: { + absl::optional packet; + Opcode opcode = static_cast( + bit_field_get(words[0], 28, 27)); + uint32_t data_word_count = bit_field_get(words[0], 26, 0); + + // If the full packet has not been received, return as though + // no valid packet was found. + if (data_word_count > words.size() - 1) { + return {words, {}}; + } + + if (previous_packet) { + packet = Xilinx7SeriesConfigurationPacket( + opcode, previous_packet->address(), + words.subspan(1, data_word_count)); + } + + return {words.subspan(data_word_count + 1), packet}; + } + default: + return {{}, {}}; + } +} + +std::ostream& operator<<(std::ostream& o, + const Xilinx7SeriesConfigurationPacket &packet) { + switch (packet.opcode()) { + case Xilinx7SeriesConfigurationPacket::Opcode::NOP: + return o << "[NOP]" << std::endl; + case Xilinx7SeriesConfigurationPacket::Opcode::Read: + return o << "[Read Address=" << packet.address() + << " Length=" << packet.data().size() << + "]" << std::endl; + case Xilinx7SeriesConfigurationPacket::Opcode::Write: + return o << "[Write Address=" << packet.address() + << " Length=" << packet.data().size() << + "]" << std::endl; + default: + return o << "[Invalid Opcode]" << std::endl; + } +} + +} // namespace prjxray diff --git a/lib/xilinx_7series_configuration_packet_test.cc b/lib/xilinx_7series_configuration_packet_test.cc new file mode 100644 index 00000000..29a33af9 --- /dev/null +++ b/lib/xilinx_7series_configuration_packet_test.cc @@ -0,0 +1,91 @@ +#include + +#include + +#include +#include +#include + +constexpr uint32_t kType1NOP = prjxray::bit_field_set(0, 31, 29, 0x1); + +constexpr 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, 31, 29, 0x1), + 28, 27, opcode), + 26, 13, address), + 10, 0, word_count); +} + +constexpr uint32_t MakeType2(const int opcode, const int word_count) { + return prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set(0x0, 31, 29, 0x2), + 28, 27, opcode), + 26, 0, word_count); +} + + +TEST(ConfigPacket, InitWithZeroBytes) { + auto packet = prjxray::Xilinx7SeriesConfigurationPacket::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 = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::NOP); + EXPECT_EQ(packet.second->address(), static_cast(0)); + EXPECT_EQ(packet.second->data(), absl::Span()); +} + +TEST(ConfigPacket, InitWithType1Read) { + std::vector words{MakeType1(0x1, 0x1234, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), static_cast(0x1234)); + EXPECT_EQ(packet.second->data(), word_span.subspan(1)); +} + +TEST(ConfigPacket, InitWithType1Write) { + std::vector words{MakeType1(0x2, 0x1234, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet.second->address(), static_cast(0x1234)); + 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 = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span); + EXPECT_EQ(packet.first, words); + EXPECT_FALSE(packet.second); +} + +TEST(ConfigPacket, InitWithType2WithPreviousPacket) { + prjxray::Xilinx7SeriesConfigurationPacket previous_packet( + prjxray::Xilinx7SeriesConfigurationPacket::Read, + 0x1234, 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 = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords( + word_span, &previous_packet); + EXPECT_EQ(packet.first, absl::Span()); + ASSERT_TRUE(packet.second); + EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Read); + EXPECT_EQ(packet.second->address(), static_cast(0x1234)); + EXPECT_EQ(packet.second->data(), word_span.subspan(1)); +}