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