From d328a2f57657382173b8db17a423dd8288bf5c3d Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Wed, 9 Oct 2019 13:03:11 +0200 Subject: [PATCH] Add initial support for UltraScalePlus devices Signed-off-by: Tomasz Michalak --- lib/CMakeLists.txt | 1 + lib/include/prjxray/xilinx/architectures.h | 15 +- lib/include/prjxray/xilinx/bitstream_reader.h | 26 +++ lib/include/prjxray/xilinx/configuration.h | 132 +++++++++++++ lib/include/prjxray/xilinx/frames.h | 5 - lib/xilinx/bitstream_writer.cc | 5 + lib/xilinx/configuration.cc | 187 ++++++++++++++++++ lib/xilinx/frames.cc | 17 ++ tools/xc7frames2bit.cc | 3 +- 9 files changed, 384 insertions(+), 7 deletions(-) create mode 100644 lib/xilinx/frames.cc diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0aee1b1f..6683dfb1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(libprjxray xilinx/configuration_packet.cc xilinx/configuration_register.cc xilinx/configuration.cc + xilinx/frames.cc # Series-7 specific xilinx/xc7series/frame_address.cc xilinx/xc7series/global_clock_region.cc diff --git a/lib/include/prjxray/xilinx/architectures.h b/lib/include/prjxray/xilinx/architectures.h index 11958531..ae3e42de 100644 --- a/lib/include/prjxray/xilinx/architectures.h +++ b/lib/include/prjxray/xilinx/architectures.h @@ -14,10 +14,11 @@ namespace prjxray { namespace xilinx { class Series7; +class UltraScalePlus; class Architecture { public: - using Container = absl::variant; + using Container = absl::variant; virtual const std::string& name() const = 0; }; @@ -37,12 +38,24 @@ class Series7 : public Architecture { std::string name_; }; +class UltraScalePlus : public Series7 { + public: + UltraScalePlus() : name_("UltraScalePlus") {} + const std::string& name() const override { return name_; } + static constexpr int words_per_frame = 93; + + private: + std::string name_; +}; + class ArchitectureFactory { public: static Architecture::Container create_architecture( const std::string& arch) { if (arch == "Series7") { return Series7(); + } else if (arch == "UltraScalePlus") { + return UltraScalePlus(); } else { return Architecture::Container(); } diff --git a/lib/include/prjxray/xilinx/bitstream_reader.h b/lib/include/prjxray/xilinx/bitstream_reader.h index d514fadd..2088c8bf 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)); +} + // Sync word as specified in UG470 page 81 template std::array BitstreamReader::kSyncWord{0xAA, 0x99, 0x55, diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h index 5b568818..77a0df9e 100644 --- a/lib/include/prjxray/xilinx/configuration.h +++ b/lib/include/prjxray/xilinx/configuration.h @@ -196,6 +196,138 @@ absl::optional> Configuration::InitWithPackets( return Configuration(part, frames); } +template <> +template +absl::optional> +Configuration::InitWithPackets( + const typename UltraScalePlus::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 diff --git a/lib/include/prjxray/xilinx/frames.h b/lib/include/prjxray/xilinx/frames.h index c39a125c..b54741ac 100644 --- a/lib/include/prjxray/xilinx/frames.h +++ b/lib/include/prjxray/xilinx/frames.h @@ -42,11 +42,6 @@ class Frames { void updateECC(FrameData& data); }; -template -void Frames::updateECC(typename Frames::FrameData& data) { - xc7series::updateECC(data); -} - template int Frames::readFrames(const std::string& frm_file_str) { assert(!frm_file_str.empty()); diff --git a/lib/xilinx/bitstream_writer.cc b/lib/xilinx/bitstream_writer.cc index 2426f4f0..c80d075e 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}; + uint32_t packet2header( const ConfigurationPacket& packet) { uint32_t ret = 0; diff --git a/lib/xilinx/configuration.cc b/lib/xilinx/configuration.cc index 2c105020..ddaa5251 100644 --- a/lib/xilinx/configuration.cc +++ b/lib/xilinx/configuration.cc @@ -41,6 +41,32 @@ 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(), + 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, @@ -213,5 +239,166 @@ void Configuration::createConfigurationPackage( } } +template <> +void Configuration::createConfigurationPackage( + UltraScalePlus::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part) { + using ArchType = UltraScalePlus; + 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()); + } +} + } // namespace xilinx } // namespace prjxray diff --git a/lib/xilinx/frames.cc b/lib/xilinx/frames.cc new file mode 100644 index 00000000..364528b3 --- /dev/null +++ b/lib/xilinx/frames.cc @@ -0,0 +1,17 @@ +#include +#include + +namespace prjxray { +namespace xilinx { +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 6b5c758f..980f944e 100644 --- a/tools/xc7frames2bit.cc +++ b/tools/xc7frames2bit.cc @@ -39,7 +39,8 @@ struct Frames2BitWriter { return 1; } - if (std::is_same::value) { + if (std::is_same::value || + std::is_same::value) { // In case the frames input file is missing some frames // that are in the tilegrid frames.addMissingFrames(part);