From 57493529372c14d674169d6a76a1a686a1eafda3 Mon Sep 17 00:00:00 2001 From: Rick Altherr Date: Tue, 16 Jan 2018 16:11:08 -0800 Subject: [PATCH 1/2] lib: xc7series: convience constructor for Configuration Configuration's original constructor expects Spans as part of the map to avoid copying the actual frame data. In tests or any other place that needs to directly construct a Configuration, the caller will already have a map with vector to hold the actual frame data. This new constructor just wraps those vectors in Spans. Signed-off-by: Rick Altherr --- lib/include/prjxray/xilinx/xc7series/configuration.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/include/prjxray/xilinx/xc7series/configuration.h b/lib/include/prjxray/xilinx/xc7series/configuration.h index 0258db12..fa10a19d 100644 --- a/lib/include/prjxray/xilinx/xc7series/configuration.h +++ b/lib/include/prjxray/xilinx/xc7series/configuration.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,15 @@ class Configuration { const Part& part, Collection& packets); + Configuration(const Part& part, + std::map>* frames) + : part_(part) { + for (auto& frame : *frames) { + frames_[frame.first] = + absl::Span(frame.second); + } + } + Configuration(const Part& part, const FrameMap& frames) : part_(part), frames_(std::move(frames)) {} From 012494552d5c4f67c06b3afdcca809e1445a69b7 Mon Sep 17 00:00:00 2001 From: Rick Altherr Date: Tue, 16 Jan 2018 16:15:11 -0800 Subject: [PATCH 2/2] lib: xc7series: generate packets from a Configuration ConfigurationPacketizer translates a Configuration's frame data to a sequence of ConfigurationPackets that will write that configuration to the configuration's Part. Signed-off-by: Rick Altherr --- lib/CMakeLists.txt | 2 + .../xc7series/configuration_packetizer.h | 62 +++++++++ .../xc7series/configuration_packetizer.cc | 128 ++++++++++++++++++ .../configuration_packetizer_test.cc | 118 ++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 lib/include/prjxray/xilinx/xc7series/configuration_packetizer.h create mode 100644 lib/xilinx/xc7series/configuration_packetizer.cc create mode 100644 lib/xilinx/xc7series/configuration_packetizer_test.cc 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()); +}