From 3f574743cfeccc071b7e8096c990fd1e1dda259a Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Wed, 9 Oct 2019 13:03:11 +0200 Subject: [PATCH 1/4] Add initial support for UltraScale devices Signed-off-by: Tomasz Michalak --- lib/include/prjxray/xilinx/architectures.h | 28 +-- lib/include/prjxray/xilinx/bitstream_reader.h | 26 +++ lib/include/prjxray/xilinx/configuration.h | 135 ++++++++++++- lib/xilinx/bitstream_writer.cc | 5 + lib/xilinx/configuration.cc | 187 +++++++++++++++++- lib/xilinx/frames.cc | 7 + tools/xc7frames2bit.cc | 1 + 7 files changed, 375 insertions(+), 14 deletions(-) diff --git a/lib/include/prjxray/xilinx/architectures.h b/lib/include/prjxray/xilinx/architectures.h index cd94cae8..ce003ba8 100644 --- a/lib/include/prjxray/xilinx/architectures.h +++ b/lib/include/prjxray/xilinx/architectures.h @@ -14,13 +14,18 @@ namespace prjxray { namespace xilinx { class Series7; +class UltraScale; class UltraScalePlus; class Architecture { public: - using Container = absl::variant; - virtual const std::string& name() const = 0; + using Container = absl::variant; + Architecture(const std::string& name) : name_(name) {} + const std::string& name() const { return name_; } virtual ~Architecture() {} + + private: + const std::string name_; }; class Series7 : public Architecture { @@ -31,22 +36,21 @@ class Series7 : public Architecture { std::vector>>; using FrameAddress = xc7series::FrameAddress; using WordType = uint32_t; - Series7() : name_("Series7") {} - const std::string& name() const override { return name_; } + Series7() : Architecture("Series7") {} + Series7(const std::string& name) : Architecture(name) {} static constexpr int words_per_frame = 101; - - private: - std::string name_; }; class UltraScalePlus : public Series7 { public: - UltraScalePlus() : name_("UltraScalePlus") {} - const std::string& name() const override { return name_; } + UltraScalePlus() : Series7("UltraScalePlus") {} static constexpr int words_per_frame = 93; +}; - private: - std::string name_; +class UltraScale : public Series7 { + public: + UltraScale() : Series7("UltraScale") {} + static constexpr int words_per_frame = 123; }; class ArchitectureFactory { @@ -55,6 +59,8 @@ class ArchitectureFactory { const std::string& arch) { if (arch == "Series7") { return Series7(); + } else if (arch == "UltraScale") { + return UltraScale(); } else if (arch == "UltraScalePlus") { return UltraScalePlus(); } else { diff --git a/lib/include/prjxray/xilinx/bitstream_reader.h b/lib/include/prjxray/xilinx/bitstream_reader.h index 2088c8bf..4cf98c23 100644 --- a/lib/include/prjxray/xilinx/bitstream_reader.h +++ b/lib/include/prjxray/xilinx/bitstream_reader.h @@ -98,6 +98,32 @@ BitstreamReader::InitWithBytes(T bitstream) { return BitstreamReader(std::move(words)); } +template <> +template +absl::optional> +BitstreamReader::InitWithBytes(T bitstream) { + // If this is really a Xilinx bitstream, there will be a sync + // word somewhere toward the beginning. + auto sync_pos = std::search(bitstream.begin(), bitstream.end(), + kSyncWord.begin(), kSyncWord.end()); + if (sync_pos == bitstream.end()) { + return absl::optional>(); + } + sync_pos += kSyncWord.size(); + + // Wrap the provided container in a span that strips off the preamble. + absl::Span bitstream_span(bitstream); + auto config_packets = + bitstream_span.subspan(sync_pos - bitstream.begin()); + + // Convert the bytes into 32-bit, big-endian words. + auto big_endian_reader = make_big_endian_span(config_packets); + std::vector words{big_endian_reader.begin(), + big_endian_reader.end()}; + + return BitstreamReader(std::move(words)); +} + template <> template absl::optional> diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h index 77a0df9e..6022275f 100644 --- a/lib/include/prjxray/xilinx/configuration.h +++ b/lib/include/prjxray/xilinx/configuration.h @@ -196,11 +196,143 @@ absl::optional> Configuration::InitWithPackets( return Configuration(part, frames); } +template <> +template +absl::optional> +Configuration::InitWithPackets( + const typename UltraScale::Part& part, + Collection& packets) { + using ArchType = UltraScale; + // Registers that can be directly written to. + uint32_t command_register = 0; + uint32_t frame_address_register = 0; + uint32_t mask_register = 0; + uint32_t ctl1_register = 0; + + // Internal state machine for writes. + bool start_new_write = false; + typename ArchType::FrameAddress current_frame_address = 0; + + Configuration::FrameMap frames; + for (auto packet : packets) { + if (packet.opcode() != + ConfigurationPacket< + typename ArchType::ConfRegType>::Opcode::Write) { + continue; + } + + switch (packet.address()) { + case ArchType::ConfRegType::MASK: + if (packet.data().size() < 1) + continue; + mask_register = packet.data()[0]; + break; + case ArchType::ConfRegType::CTL1: + if (packet.data().size() < 1) + continue; + ctl1_register = + packet.data()[0] & mask_register; + break; + case ArchType::ConfRegType::CMD: + if (packet.data().size() < 1) + continue; + command_register = packet.data()[0]; + // Writes to CMD trigger an immediate action. In + // the case of WCFG, that is just setting a flag + // for the next FDIR. + if (command_register == 0x1) { + start_new_write = true; + } + break; + case ArchType::ConfRegType::IDCODE: + // This really should be a one-word write. + if (packet.data().size() < 1) + continue; + + // If the IDCODE doesn't match our expected + // part, consider the bitstream invalid. + if (packet.data()[0] != part.idcode()) { + return {}; + } + break; + case ArchType::ConfRegType::FAR: + // This really should be a one-word write. + if (packet.data().size() < 1) + continue; + frame_address_register = packet.data()[0]; + + // Per UG470, the command present in the CMD + // register is executed each time the FAR + // register is laoded with a new value. As we + // only care about WCFG commands, just check + // that here. CTRL1 is completely undocumented + // but looking at generated bitstreams, bit 21 + // is used when per-frame CRC is enabled. + // Setting this bit seems to inhibit the + // re-execution of CMD during a FAR write. In + // practice, this is used so FAR writes can be + // added in the bitstream to show progress + // markers without impacting the actual write + // operation. + if (bit_field_get(ctl1_register, 21, 21) == 0 && + command_register == 0x1) { + start_new_write = true; + } + break; + case ArchType::ConfRegType::FDRI: { + if (start_new_write) { + current_frame_address = + frame_address_register; + start_new_write = false; + } + + // UltraScale frames are 123-words long. Writes + // to this register can be multiples of that to + // do auto-incrementing block writes. + for (size_t ii = 0; ii < packet.data().size(); + ii += ArchType::words_per_frame) { + frames[current_frame_address] = + packet.data().subspan( + ii, ArchType::words_per_frame); + + auto next_address = + part.GetNextFrameAddress( + current_frame_address); + if (!next_address) + break; + + // Bitstreams appear to have 2 frames of + // padding between rows. + if (next_address && + (next_address->block_type() != + current_frame_address + .block_type() || + next_address + ->is_bottom_half_rows() != + current_frame_address + .is_bottom_half_rows() || + next_address->row() != + current_frame_address.row())) { + ii += 2 * + ArchType::words_per_frame; + } + current_frame_address = *next_address; + } + break; + } + default: + break; + } + } + + return Configuration(part, frames); +} + template <> template absl::optional> Configuration::InitWithPackets( - const typename UltraScalePlus::Part& part, + const typename UltraScale::Part& part, Collection& packets) { using ArchType = UltraScalePlus; // Registers that can be directly written to. @@ -327,7 +459,6 @@ Configuration::InitWithPackets( return Configuration(part, frames); } - } // namespace xilinx } // namespace prjxray diff --git a/lib/xilinx/bitstream_writer.cc b/lib/xilinx/bitstream_writer.cc index c80d075e..5a9cc0b6 100644 --- a/lib/xilinx/bitstream_writer.cc +++ b/lib/xilinx/bitstream_writer.cc @@ -12,6 +12,11 @@ template <> typename BitstreamWriter::header_t BitstreamWriter::header_{ 0xFFFFFFFF, 0x000000BB, 0x11220044, 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566}; +template <> +typename BitstreamWriter::header_t + BitstreamWriter::header_{0xFFFFFFFF, 0x000000BB, 0x11220044, + 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566}; + template <> typename BitstreamWriter::header_t BitstreamWriter::header_{ diff --git a/lib/xilinx/configuration.cc b/lib/xilinx/configuration.cc index ddaa5251..cff19d3d 100644 --- a/lib/xilinx/configuration.cc +++ b/lib/xilinx/configuration.cc @@ -41,6 +41,31 @@ Configuration::createType2ConfigurationPacketData( return packet_data; } +template <> +Configuration::PacketData +Configuration::createType2ConfigurationPacketData( + const Frames::Frames2Data& frames, + absl::optional& part) { + PacketData packet_data; + for (auto& frame : frames) { + std::copy(frame.second.begin(), frame.second.end(), + std::back_inserter(packet_data)); + + auto next_address = part->GetNextFrameAddress(frame.first); + if (next_address && + (next_address->block_type() != frame.first.block_type() || + next_address->is_bottom_half_rows() != + frame.first.is_bottom_half_rows() || + next_address->row() != frame.first.row())) { + packet_data.insert(packet_data.end(), + UltraScale::words_per_frame * 2, 0); + } + } + packet_data.insert(packet_data.end(), UltraScale::words_per_frame * 2, + 0); + return packet_data; +} + template <> Configuration::PacketData Configuration::createType2ConfigurationPacketData( @@ -239,6 +264,167 @@ void Configuration::createConfigurationPackage( } } +template <> +void Configuration::createConfigurationPackage( + UltraScale::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part) { + using ArchType = UltraScale; + using ConfigurationRegister = ArchType::ConfRegType; + // Initialization sequence + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::TIMER, {0x0})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::WBSTAR, {0x0})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::NOP)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::RCRC)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FAR, {0x0})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::UNKNOWN, {0x0})); + + // Configuration Options 0 + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::COR0, {0x38003fe5})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::COR1, {0x400000})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::IDCODE, {part->idcode()})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::SWITCH)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::MASK, {0x1})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CTL0, {0x101})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::MASK, {0x0})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CTL1, {0x0})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FAR, {0x0})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::WCFG)})); + out_packets.emplace_back(new NopPacket()); + + // Frame data write + out_packets.emplace_back(new ConfigurationPacket( + TYPE1, ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FDRI, {})); + out_packets.emplace_back(new ConfigurationPacket( + TYPE2, ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FDRI, packet_data)); + + // Finalization sequence + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::RCRC)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::GRESTORE)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::LFRM)})); + for (int ii = 0; ii < 100; ++ii) { + out_packets.emplace_back( + new NopPacket()); + } + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::START)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::FAR, {0x3be0000})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::MASK, {0x101})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CTL0, {0x101})); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::RCRC)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back( + new ConfigurationPacketWithPayload<1, ConfigurationRegister>( + ConfigurationPacket::Opcode::Write, + ConfigurationRegister::CMD, + {static_cast(xc7series::Command::DESYNC)})); + for (int ii = 0; ii < 400; ++ii) { + out_packets.emplace_back( + new NopPacket()); + } +} + template <> void Configuration::createConfigurationPackage( UltraScalePlus::ConfigurationPackage& out_packets, @@ -399,6 +585,5 @@ void Configuration::createConfigurationPackage( new NopPacket()); } } - } // namespace xilinx } // namespace prjxray diff --git a/lib/xilinx/frames.cc b/lib/xilinx/frames.cc index 364528b3..8e11cbc1 100644 --- a/lib/xilinx/frames.cc +++ b/lib/xilinx/frames.cc @@ -8,10 +8,17 @@ void Frames::updateECC(typename Frames::FrameData& data) { xc7series::updateECC(data); } +template <> +void Frames::updateECC( + typename Frames::FrameData& data) { + xc7series::updateECC(data); +} + template <> void Frames::updateECC( typename Frames::FrameData& data) { xc7series::updateECC(data); } + } // namespace xilinx } // namespace prjxray diff --git a/tools/xc7frames2bit.cc b/tools/xc7frames2bit.cc index 980f944e..28d952d0 100644 --- a/tools/xc7frames2bit.cc +++ b/tools/xc7frames2bit.cc @@ -40,6 +40,7 @@ struct Frames2BitWriter { } if (std::is_same::value || + std::is_same::value || std::is_same::value) { // In case the frames input file is missing some frames // that are in the tilegrid From 7bc54d0c563c202659fa0bda4223ae583cdc872a Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Thu, 17 Oct 2019 21:44:48 +0200 Subject: [PATCH 2/4] Refactor BitstreamReader::InitWithBytes template method Signed-off-by: Tomasz Michalak --- lib/include/prjxray/xilinx/bitstream_reader.h | 65 ++----------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/lib/include/prjxray/xilinx/bitstream_reader.h b/lib/include/prjxray/xilinx/bitstream_reader.h index 4cf98c23..61e807f1 100644 --- a/lib/include/prjxray/xilinx/bitstream_reader.h +++ b/lib/include/prjxray/xilinx/bitstream_reader.h @@ -72,16 +72,16 @@ class BitstreamReader { std::vector words_; }; -template <> +template template -absl::optional> -BitstreamReader::InitWithBytes(T bitstream) { +absl::optional> +BitstreamReader::InitWithBytes(T bitstream) { // If this is really a Xilinx bitstream, there will be a sync // word somewhere toward the beginning. auto sync_pos = std::search(bitstream.begin(), bitstream.end(), kSyncWord.begin(), kSyncWord.end()); if (sync_pos == bitstream.end()) { - return absl::optional>(); + return absl::optional>(); } sync_pos += kSyncWord.size(); @@ -91,63 +91,12 @@ BitstreamReader::InitWithBytes(T bitstream) { bitstream_span.subspan(sync_pos - bitstream.begin()); // Convert the bytes into 32-bit, big-endian words. - auto big_endian_reader = make_big_endian_span(config_packets); + auto big_endian_reader = + make_big_endian_span(config_packets); std::vector words{big_endian_reader.begin(), big_endian_reader.end()}; - return BitstreamReader(std::move(words)); -} - -template <> -template -absl::optional> -BitstreamReader::InitWithBytes(T bitstream) { - // If this is really a Xilinx bitstream, there will be a sync - // word somewhere toward the beginning. - auto sync_pos = std::search(bitstream.begin(), bitstream.end(), - kSyncWord.begin(), kSyncWord.end()); - if (sync_pos == bitstream.end()) { - return absl::optional>(); - } - sync_pos += kSyncWord.size(); - - // Wrap the provided container in a span that strips off the preamble. - absl::Span bitstream_span(bitstream); - auto config_packets = - bitstream_span.subspan(sync_pos - bitstream.begin()); - - // Convert the bytes into 32-bit, big-endian words. - auto big_endian_reader = make_big_endian_span(config_packets); - std::vector words{big_endian_reader.begin(), - big_endian_reader.end()}; - - return BitstreamReader(std::move(words)); -} - -template <> -template -absl::optional> -BitstreamReader::InitWithBytes(T bitstream) { - // If this is really a Xilinx bitstream, there will be a sync - // word somewhere toward the beginning. - auto sync_pos = std::search(bitstream.begin(), bitstream.end(), - kSyncWord.begin(), kSyncWord.end()); - if (sync_pos == bitstream.end()) { - return absl::optional>(); - } - sync_pos += kSyncWord.size(); - - // Wrap the provided container in a span that strips off the preamble. - absl::Span bitstream_span(bitstream); - auto config_packets = - bitstream_span.subspan(sync_pos - bitstream.begin()); - - // Convert the bytes into 32-bit, big-endian words. - auto big_endian_reader = make_big_endian_span(config_packets); - std::vector words{big_endian_reader.begin(), - big_endian_reader.end()}; - - return BitstreamReader(std::move(words)); + return BitstreamReader(std::move(words)); } // Sync word as specified in UG470 page 81 From d91b0a50069130220a140dcc03b568900518f9e7 Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Thu, 17 Oct 2019 21:52:10 +0200 Subject: [PATCH 3/4] Refactor Configuration::InitWithPackets template method Signed-off-by: Tomasz Michalak --- lib/include/prjxray/xilinx/configuration.h | 277 +-------------------- 1 file changed, 7 insertions(+), 270 deletions(-) diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h index 6022275f..e5430423 100644 --- a/lib/include/prjxray/xilinx/configuration.h +++ b/lib/include/prjxray/xilinx/configuration.h @@ -65,12 +65,11 @@ class Configuration { FrameMap frames_; }; -template <> +template template -absl::optional> Configuration::InitWithPackets( - const typename Series7::Part& part, - Collection& packets) { - using ArchType = Series7; +absl::optional> +Configuration::InitWithPackets(const typename ArchType::Part& part, + Collection& packets) { // Registers that can be directly written to. uint32_t command_register = 0; uint32_t frame_address_register = 0; @@ -154,8 +153,9 @@ absl::optional> Configuration::InitWithPackets( start_new_write = false; } - // 7-series frames are 101-words long. Writes - // to this register can be multiples of that to + // Number of words in configuration frames + // depend on tje architecture. Writes to this + // register can be multiples of that number to // do auto-incrementing block writes. for (size_t ii = 0; ii < packet.data().size(); ii += ArchType::words_per_frame) { @@ -196,269 +196,6 @@ absl::optional> Configuration::InitWithPackets( return Configuration(part, frames); } -template <> -template -absl::optional> -Configuration::InitWithPackets( - const typename UltraScale::Part& part, - Collection& packets) { - using ArchType = UltraScale; - // Registers that can be directly written to. - uint32_t command_register = 0; - uint32_t frame_address_register = 0; - uint32_t mask_register = 0; - uint32_t ctl1_register = 0; - - // Internal state machine for writes. - bool start_new_write = false; - typename ArchType::FrameAddress current_frame_address = 0; - - Configuration::FrameMap frames; - for (auto packet : packets) { - if (packet.opcode() != - ConfigurationPacket< - typename ArchType::ConfRegType>::Opcode::Write) { - continue; - } - - switch (packet.address()) { - case ArchType::ConfRegType::MASK: - if (packet.data().size() < 1) - continue; - mask_register = packet.data()[0]; - break; - case ArchType::ConfRegType::CTL1: - if (packet.data().size() < 1) - continue; - ctl1_register = - packet.data()[0] & mask_register; - break; - case ArchType::ConfRegType::CMD: - if (packet.data().size() < 1) - continue; - command_register = packet.data()[0]; - // Writes to CMD trigger an immediate action. In - // the case of WCFG, that is just setting a flag - // for the next FDIR. - if (command_register == 0x1) { - start_new_write = true; - } - break; - case ArchType::ConfRegType::IDCODE: - // This really should be a one-word write. - if (packet.data().size() < 1) - continue; - - // If the IDCODE doesn't match our expected - // part, consider the bitstream invalid. - if (packet.data()[0] != part.idcode()) { - return {}; - } - break; - case ArchType::ConfRegType::FAR: - // This really should be a one-word write. - if (packet.data().size() < 1) - continue; - frame_address_register = packet.data()[0]; - - // Per UG470, the command present in the CMD - // register is executed each time the FAR - // register is laoded with a new value. As we - // only care about WCFG commands, just check - // that here. CTRL1 is completely undocumented - // but looking at generated bitstreams, bit 21 - // is used when per-frame CRC is enabled. - // Setting this bit seems to inhibit the - // re-execution of CMD during a FAR write. In - // practice, this is used so FAR writes can be - // added in the bitstream to show progress - // markers without impacting the actual write - // operation. - if (bit_field_get(ctl1_register, 21, 21) == 0 && - command_register == 0x1) { - start_new_write = true; - } - break; - case ArchType::ConfRegType::FDRI: { - if (start_new_write) { - current_frame_address = - frame_address_register; - start_new_write = false; - } - - // UltraScale frames are 123-words long. Writes - // to this register can be multiples of that to - // do auto-incrementing block writes. - for (size_t ii = 0; ii < packet.data().size(); - ii += ArchType::words_per_frame) { - frames[current_frame_address] = - packet.data().subspan( - ii, ArchType::words_per_frame); - - auto next_address = - part.GetNextFrameAddress( - current_frame_address); - if (!next_address) - break; - - // Bitstreams appear to have 2 frames of - // padding between rows. - if (next_address && - (next_address->block_type() != - current_frame_address - .block_type() || - next_address - ->is_bottom_half_rows() != - current_frame_address - .is_bottom_half_rows() || - next_address->row() != - current_frame_address.row())) { - ii += 2 * - ArchType::words_per_frame; - } - current_frame_address = *next_address; - } - break; - } - default: - break; - } - } - - return Configuration(part, frames); -} - -template <> -template -absl::optional> -Configuration::InitWithPackets( - const typename UltraScale::Part& part, - Collection& packets) { - using ArchType = UltraScalePlus; - // Registers that can be directly written to. - uint32_t command_register = 0; - uint32_t frame_address_register = 0; - uint32_t mask_register = 0; - uint32_t ctl1_register = 0; - - // Internal state machine for writes. - bool start_new_write = false; - typename ArchType::FrameAddress current_frame_address = 0; - - Configuration::FrameMap frames; - for (auto packet : packets) { - if (packet.opcode() != - ConfigurationPacket< - typename ArchType::ConfRegType>::Opcode::Write) { - continue; - } - - switch (packet.address()) { - case ArchType::ConfRegType::MASK: - if (packet.data().size() < 1) - continue; - mask_register = packet.data()[0]; - break; - case ArchType::ConfRegType::CTL1: - if (packet.data().size() < 1) - continue; - ctl1_register = - packet.data()[0] & mask_register; - break; - case ArchType::ConfRegType::CMD: - if (packet.data().size() < 1) - continue; - command_register = packet.data()[0]; - // Writes to CMD trigger an immediate action. In - // the case of WCFG, that is just setting a flag - // for the next FDIR. - if (command_register == 0x1) { - start_new_write = true; - } - break; - case ArchType::ConfRegType::IDCODE: - // This really should be a one-word write. - if (packet.data().size() < 1) - continue; - - // If the IDCODE doesn't match our expected - // part, consider the bitstream invalid. - if (packet.data()[0] != part.idcode()) { - return {}; - } - break; - case ArchType::ConfRegType::FAR: - // This really should be a one-word write. - if (packet.data().size() < 1) - continue; - frame_address_register = packet.data()[0]; - - // Per UG470, the command present in the CMD - // register is executed each time the FAR - // register is laoded with a new value. As we - // only care about WCFG commands, just check - // that here. CTRL1 is completely undocumented - // but looking at generated bitstreams, bit 21 - // is used when per-frame CRC is enabled. - // Setting this bit seems to inhibit the - // re-execution of CMD during a FAR write. In - // practice, this is used so FAR writes can be - // added in the bitstream to show progress - // markers without impacting the actual write - // operation. - if (bit_field_get(ctl1_register, 21, 21) == 0 && - command_register == 0x1) { - start_new_write = true; - } - break; - case ArchType::ConfRegType::FDRI: { - if (start_new_write) { - current_frame_address = - frame_address_register; - start_new_write = false; - } - - // UltraScale frames are 93-words long. Writes - // to this register can be multiples of that to - // do auto-incrementing block writes. - for (size_t ii = 0; ii < packet.data().size(); - ii += ArchType::words_per_frame) { - frames[current_frame_address] = - packet.data().subspan( - ii, ArchType::words_per_frame); - - auto next_address = - part.GetNextFrameAddress( - current_frame_address); - if (!next_address) - break; - - // Bitstreams appear to have 2 frames of - // padding between rows. - if (next_address && - (next_address->block_type() != - current_frame_address - .block_type() || - next_address - ->is_bottom_half_rows() != - current_frame_address - .is_bottom_half_rows() || - next_address->row() != - current_frame_address.row())) { - ii += 2 * - ArchType::words_per_frame; - } - current_frame_address = *next_address; - } - break; - } - default: - break; - } - } - - return Configuration(part, frames); -} } // namespace xilinx } // namespace prjxray From 170169b01c26abcbd8b90b6d25ed2d946127f7c5 Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Thu, 17 Oct 2019 22:12:20 +0200 Subject: [PATCH 4/4] Refactor Configuration::createType2ConfigurationPacketData template method Signed-off-by: Tomasz Michalak --- lib/include/prjxray/xilinx/configuration.h | 29 +++++++++ lib/xilinx/configuration.cc | 74 ---------------------- 2 files changed, 29 insertions(+), 74 deletions(-) diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h index e5430423..752ee7ff 100644 --- a/lib/include/prjxray/xilinx/configuration.h +++ b/lib/include/prjxray/xilinx/configuration.h @@ -65,6 +65,35 @@ class Configuration { FrameMap frames_; }; +template +typename Configuration::PacketData +Configuration::createType2ConfigurationPacketData( + const typename Frames::Frames2Data& frames, + absl::optional& part) { + PacketData packet_data; + // Certain configuration frames blocks are separated by Zero Frames, + // i.e. frames with words with all zeroes. For Series-7, US and US+ + // there zero frames separator consists of two frames. + static const int kZeroFramesSeparatorWords = + ArchType::words_per_frame * 2; + for (auto& frame : frames) { + std::copy(frame.second.begin(), frame.second.end(), + std::back_inserter(packet_data)); + + auto next_address = part->GetNextFrameAddress(frame.first); + if (next_address && + (next_address->block_type() != frame.first.block_type() || + next_address->is_bottom_half_rows() != + frame.first.is_bottom_half_rows() || + next_address->row() != frame.first.row())) { + packet_data.insert(packet_data.end(), + kZeroFramesSeparatorWords, 0); + } + } + packet_data.insert(packet_data.end(), kZeroFramesSeparatorWords, 0); + return packet_data; +} + template template absl::optional> diff --git a/lib/xilinx/configuration.cc b/lib/xilinx/configuration.cc index cff19d3d..71ea317d 100644 --- a/lib/xilinx/configuration.cc +++ b/lib/xilinx/configuration.cc @@ -18,80 +18,6 @@ namespace prjxray { namespace xilinx { -template <> -Configuration::PacketData -Configuration::createType2ConfigurationPacketData( - const Frames::Frames2Data& frames, - absl::optional& part) { - PacketData packet_data; - for (auto& frame : frames) { - std::copy(frame.second.begin(), frame.second.end(), - std::back_inserter(packet_data)); - - auto next_address = part->GetNextFrameAddress(frame.first); - if (next_address && - (next_address->block_type() != frame.first.block_type() || - next_address->is_bottom_half_rows() != - frame.first.is_bottom_half_rows() || - next_address->row() != frame.first.row())) { - packet_data.insert(packet_data.end(), 202, 0); - } - } - packet_data.insert(packet_data.end(), 202, 0); - return packet_data; -} - -template <> -Configuration::PacketData -Configuration::createType2ConfigurationPacketData( - const Frames::Frames2Data& frames, - absl::optional& part) { - PacketData packet_data; - for (auto& frame : frames) { - std::copy(frame.second.begin(), frame.second.end(), - std::back_inserter(packet_data)); - - auto next_address = part->GetNextFrameAddress(frame.first); - if (next_address && - (next_address->block_type() != frame.first.block_type() || - next_address->is_bottom_half_rows() != - frame.first.is_bottom_half_rows() || - next_address->row() != frame.first.row())) { - packet_data.insert(packet_data.end(), - UltraScale::words_per_frame * 2, 0); - } - } - packet_data.insert(packet_data.end(), UltraScale::words_per_frame * 2, - 0); - return packet_data; -} - -template <> -Configuration::PacketData -Configuration::createType2ConfigurationPacketData( - const Frames::Frames2Data& frames, - absl::optional& part) { - PacketData packet_data; - for (auto& frame : frames) { - std::copy(frame.second.begin(), frame.second.end(), - std::back_inserter(packet_data)); - - auto next_address = part->GetNextFrameAddress(frame.first); - if (next_address && - (next_address->block_type() != frame.first.block_type() || - next_address->is_bottom_half_rows() != - frame.first.is_bottom_half_rows() || - next_address->row() != frame.first.row())) { - packet_data.insert(packet_data.end(), - UltraScalePlus::words_per_frame * 2, - 0); - } - } - packet_data.insert(packet_data.end(), - UltraScalePlus::words_per_frame * 2, 0); - return packet_data; -} - template <> void Configuration::createConfigurationPackage( Series7::ConfigurationPackage& out_packets,