diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2edffb1d..49056db3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(libprjxray xilinx/xc7series/configuration_bus.cc xilinx/xc7series/configuration_column.cc xilinx/xc7series/configuration_packet.cc + xilinx/xc7series/configuration_packetizer.cc xilinx/xc7series/configuration_register.cc xilinx/xc7series/frame_address.cc xilinx/xc7series/global_clock_region.cc @@ -47,6 +48,7 @@ if (PRJXRAY_BUILD_TESTING) xilinx/xc7series/configuration_column_test.cc xilinx/xc7series/configuration_test.cc xilinx/xc7series/configuration_packet_test.cc + xilinx/xc7series/configuration_packetizer_test.cc xilinx/xc7series/global_clock_region_test.cc xilinx/xc7series/part_test.cc xilinx/xc7series/row_test.cc) diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_packetizer.h b/lib/include/prjxray/xilinx/xc7series/configuration_packetizer.h new file mode 100644 index 00000000..5eac8f17 --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/configuration_packetizer.h @@ -0,0 +1,62 @@ +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_ +#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_ + +#include +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +class ConfigurationPacketizer { + public: + class iterator + : std::iterator { + public: + iterator(const Part* part, + Configuration::FrameMap::const_iterator begin, + Configuration::FrameMap::const_iterator end); + + iterator& operator++(); + + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + + const ConfigurationPacket& operator*() const; + const ConfigurationPacket* operator->() const; + + private: + friend class ConfigurationPacketizer; + + enum class State { + Start, + FrameAddressWritten, + FrameDataWritten, + ZeroPadWritten, + Finished, + }; + + const Part* part_; + State state_; + Configuration::FrameMap::const_iterator frame_cur_; + Configuration::FrameMap::const_iterator frame_end_; + absl::optional frame_address_; + absl::optional packet_; + int zero_pad_packets_to_write_; + }; + + ConfigurationPacketizer(const Configuration& config); + + iterator begin() const; + iterator end() const; + + private: + const Configuration& config_; +}; + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_ diff --git a/lib/xilinx/xc7series/configuration_packetizer.cc b/lib/xilinx/xc7series/configuration_packetizer.cc new file mode 100644 index 00000000..e7282bfd --- /dev/null +++ b/lib/xilinx/xc7series/configuration_packetizer.cc @@ -0,0 +1,128 @@ +#include + +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +ConfigurationPacketizer::ConfigurationPacketizer(const Configuration& config) + : config_(config) {} + +ConfigurationPacketizer::iterator ConfigurationPacketizer::begin() const { + return iterator(&config_.part(), config_.frames().begin(), + config_.frames().end()); +} + +ConfigurationPacketizer::iterator ConfigurationPacketizer::end() const { + return iterator(&config_.part(), config_.frames().end(), + config_.frames().end()); +} + +ConfigurationPacketizer::iterator::iterator( + const Part* part, + Configuration::FrameMap::const_iterator begin, + Configuration::FrameMap::const_iterator end) + : part_(part), + state_(begin != end ? State::Start : State::Finished), + frame_cur_(begin), + frame_end_(end) { + this->operator++(); +} + +const ConfigurationPacket& ConfigurationPacketizer::iterator::operator*() + const { + return *packet_; +} + +const ConfigurationPacket* ConfigurationPacketizer::iterator::operator->() + const { + return &(*packet_); +} + +bool ConfigurationPacketizer::iterator::operator==( + const ConfigurationPacketizer::iterator& other) const { + return state_ == other.state_ && frame_cur_ == other.frame_cur_; +} + +bool ConfigurationPacketizer::iterator::operator!=( + const ConfigurationPacketizer::iterator& other) const { + return !(*this == other); +} + +ConfigurationPacketizer::iterator& ConfigurationPacketizer::iterator:: +operator++() { + // Frames are accessed via an indirect addressing scheme using the FAR + // and FDRI registers. Writes begin with writing the target frame + // address to FAR and then the frame data is written to FDRI. The + // following state machine primarily follows that flow: + // Start -> FrameAddressWritten -> FrameDataWritten -> Start.... + // When the last frame within a row is written, 2 full frames (202 + // words) of zero padding need to be written after the frame data. + switch (state_) { + case State::FrameDataWritten: { + // If this is the last address in this row (i.e. the + // next valid address known by the part is in a + // different row, half, or bus type), start a zero fill. + // Otherwise, increment the frame iterator and fall + // through to Start. + auto& this_address = frame_cur_->first; + auto next_address = + part_->GetNextFrameAddress(frame_cur_->first); + if (next_address && + (next_address->block_type() != + this_address.block_type() || + next_address->is_bottom_half_rows() != + this_address.is_bottom_half_rows() || + next_address->row() != this_address.row())) { + zero_pad_packets_to_write_ = 202; + // Type 0 frames aren't documented in UG470. In + // practice, they are used to zero pad in the + // bitstream. + packet_ = ConfigurationPacket( + 0, ConfigurationPacket::Opcode::NOP, + ConfigurationRegister::CRC, {}); + state_ = State::ZeroPadWritten; + break; + } + + ++frame_cur_; + } + case State::Start: + if (frame_cur_ == frame_end_) { + state_ = State::Finished; + frame_address_.reset(); + packet_.reset(); + return *this; + } + + frame_address_ = frame_cur_->first; + packet_ = ConfigurationPacket( + 1, ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FAR, + absl::Span(&frame_address_.value(), 1)); + state_ = State::FrameAddressWritten; + break; + case State::FrameAddressWritten: + packet_ = ConfigurationPacket( + 1, ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FDRI, frame_cur_->second); + state_ = State::FrameDataWritten; + break; + case State::ZeroPadWritten: + if (--zero_pad_packets_to_write_ == 1) { + ++frame_cur_; + state_ = State::Start; + } + break; + case State::Finished: + break; + } + + return *this; +} + +} // namespace xc7series +} // namespace xilinx +} // namespace prjxray diff --git a/lib/xilinx/xc7series/configuration_packetizer_test.cc b/lib/xilinx/xc7series/configuration_packetizer_test.cc new file mode 100644 index 00000000..00b73ab6 --- /dev/null +++ b/lib/xilinx/xc7series/configuration_packetizer_test.cc @@ -0,0 +1,118 @@ +#include + +#include +#include + +#include + +namespace xc7series = prjxray::xilinx::xc7series; + +TEST(ConfigurationPacketizerTest, EmptyConfigGeneratesNoPackets) { + auto part = xc7series::Part::FromFile("configuration_test.yaml"); + ASSERT_TRUE(part); + + std::map> frames; + xc7series::Configuration config(*part, &frames); + xc7series::ConfigurationPacketizer packetizer(config); + + EXPECT_EQ(packetizer.begin(), packetizer.end()); +} + +TEST(ConfigurationPacketizerTest, ConfigWithFramesGeneratesPackets) { + auto part = xc7series::Part::FromFile("configuration_test.yaml"); + ASSERT_TRUE(part); + + std::map> frames; + frames[0] = std::vector(101, 0xAA); + frames[1] = std::vector(101, 0xBB); + + xc7series::Configuration config(*part, &frames); + + EXPECT_EQ(config.frames().at(0), frames[0]); + EXPECT_EQ(config.frames().at(1), frames[1]); + + xc7series::ConfigurationPacketizer packetizer(config); + + auto packet = packetizer.begin(); + ASSERT_NE(packet, packetizer.end()); + + // Write 0x0 to FAR + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR); + EXPECT_EQ(packet->data(), std::vector{0}); + + ++packet; + ASSERT_NE(packet, packetizer.end()); + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI); + EXPECT_EQ(packet->data(), frames[0]); + + ++packet; + ASSERT_NE(packet, packetizer.end()); + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR); + EXPECT_EQ(packet->data(), std::vector{1}); + + ++packet; + ASSERT_NE(packet, packetizer.end()); + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI); + EXPECT_EQ(packet->data(), frames[1]); + + ++packet; + EXPECT_EQ(packet, packetizer.end()); +} + +TEST(ConfigurationPacketizerTest, ConfigWithFrameAtEndOfRowGeneratesZerofill) { + auto part = xc7series::Part::FromFile("configuration_test.yaml"); + ASSERT_TRUE(part); + + xc7series::FrameAddress last_frame_in_first_row( + xc7series::BlockType::CLB_IO_CLK, false, 0, 43, 41); + + std::map> frames; + frames[last_frame_in_first_row] = std::vector(101, 0xAA); + + xc7series::Configuration config(*part, &frames); + xc7series::ConfigurationPacketizer packetizer(config); + + auto packet = packetizer.begin(); + ASSERT_NE(packet, packetizer.end()); + + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR); + EXPECT_EQ(packet->data(), + std::vector{last_frame_in_first_row}); + + ++packet; + ASSERT_NE(packet, packetizer.end()); + EXPECT_EQ(packet->header_type(), static_cast(1)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::Write); + EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI); + EXPECT_EQ(packet->data(), frames[last_frame_in_first_row]); + + for (int ii = 0; ii < 202; ++ii) { + ++packet; + ASSERT_NE(packet, packetizer.end()); + EXPECT_EQ(packet->header_type(), static_cast(0)); + EXPECT_EQ(packet->opcode(), + xc7series::ConfigurationPacket::Opcode::NOP); + EXPECT_EQ(packet->address(), + xc7series::ConfigurationRegister::CRC); + EXPECT_EQ(packet->data(), std::vector()); + } + + ++packet; + EXPECT_EQ(packet, packetizer.end()); +}