Merge pull request #1091 from antmicro/ultrascale_support

Bitstream Tools: Add UltraScale devices support
This commit is contained in:
Tomasz Michalak 2019-10-21 21:33:34 +02:00 committed by GitHub
commit ee5e0f80c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 229 additions and 227 deletions

View File

@ -14,13 +14,18 @@ namespace prjxray {
namespace xilinx {
class Series7;
class UltraScale;
class UltraScalePlus;
class Architecture {
public:
using Container = absl::variant<Series7, UltraScalePlus>;
virtual const std::string& name() const = 0;
using Container = absl::variant<Series7, UltraScale, UltraScalePlus>;
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<std::unique_ptr<ConfigurationPacket<ConfRegType>>>;
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 {

View File

@ -72,16 +72,16 @@ class BitstreamReader {
std::vector<uint32_t> words_;
};
template <>
template <typename ArchType>
template <typename T>
absl::optional<BitstreamReader<Series7>>
BitstreamReader<Series7>::InitWithBytes(T bitstream) {
absl::optional<BitstreamReader<ArchType>>
BitstreamReader<ArchType>::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<BitstreamReader<Series7>>();
return absl::optional<BitstreamReader<ArchType>>();
}
sync_pos += kSyncWord.size();
@ -91,37 +91,12 @@ BitstreamReader<Series7>::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<uint32_t>(config_packets);
auto big_endian_reader =
make_big_endian_span<typename ArchType::WordType>(config_packets);
std::vector<uint32_t> words{big_endian_reader.begin(),
big_endian_reader.end()};
return BitstreamReader<Series7>(std::move(words));
}
template <>
template <typename T>
absl::optional<BitstreamReader<UltraScalePlus>>
BitstreamReader<UltraScalePlus>::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<BitstreamReader<UltraScalePlus>>();
}
sync_pos += kSyncWord.size();
// Wrap the provided container in a span that strips off the preamble.
absl::Span<typename T::value_type> 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<uint32_t>(config_packets);
std::vector<uint32_t> words{big_endian_reader.begin(),
big_endian_reader.end()};
return BitstreamReader<UltraScalePlus>(std::move(words));
return BitstreamReader<ArchType>(std::move(words));
}
// Sync word as specified in UG470 page 81

View File

@ -65,144 +65,40 @@ class Configuration {
FrameMap frames_;
};
template <>
template <typename Collection>
absl::optional<Configuration<Series7>> Configuration<Series7>::InitWithPackets(
const typename Series7::Part& part,
Collection& packets) {
using ArchType = Series7;
// 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;
template <typename ArchType>
typename Configuration<ArchType>::PacketData
Configuration<ArchType>::createType2ConfigurationPacketData(
const typename Frames<ArchType>::Frames2Data& frames,
absl::optional<typename ArchType::Part>& 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));
// Internal state machine for writes.
bool start_new_write = false;
typename ArchType::FrameAddress current_frame_address = 0;
Configuration<ArchType>::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;
}
// 7-series frames are 101-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;
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);
}
}
return Configuration(part, frames);
packet_data.insert(packet_data.end(), kZeroFramesSeparatorWords, 0);
return packet_data;
}
template <>
template <typename ArchType>
template <typename Collection>
absl::optional<Configuration<UltraScalePlus>>
Configuration<UltraScalePlus>::InitWithPackets(
const typename UltraScalePlus::Part& part,
Collection& packets) {
using ArchType = UltraScalePlus;
absl::optional<Configuration<ArchType>>
Configuration<ArchType>::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;
@ -286,8 +182,9 @@ Configuration<UltraScalePlus>::InitWithPackets(
start_new_write = false;
}
// UltraScale frames are 93-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) {

View File

@ -12,6 +12,11 @@ template <>
typename BitstreamWriter<Series7>::header_t BitstreamWriter<Series7>::header_{
0xFFFFFFFF, 0x000000BB, 0x11220044, 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
template <>
typename BitstreamWriter<UltraScale>::header_t
BitstreamWriter<UltraScale>::header_{0xFFFFFFFF, 0x000000BB, 0x11220044,
0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
template <>
typename BitstreamWriter<UltraScalePlus>::header_t
BitstreamWriter<UltraScalePlus>::header_{

View File

@ -18,55 +18,6 @@
namespace prjxray {
namespace xilinx {
template <>
Configuration<Series7>::PacketData
Configuration<Series7>::createType2ConfigurationPacketData(
const Frames<Series7>::Frames2Data& frames,
absl::optional<Series7::Part>& 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<UltraScalePlus>::PacketData
Configuration<UltraScalePlus>::createType2ConfigurationPacketData(
const Frames<UltraScalePlus>::Frames2Data& frames,
absl::optional<UltraScalePlus::Part>& 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<Series7>::createConfigurationPackage(
Series7::ConfigurationPackage& out_packets,
@ -239,6 +190,167 @@ void Configuration<Series7>::createConfigurationPackage(
}
}
template <>
void Configuration<UltraScale>::createConfigurationPackage(
UltraScale::ConfigurationPackage& out_packets,
const PacketData& packet_data,
absl::optional<UltraScale::Part>& part) {
using ArchType = UltraScale;
using ConfigurationRegister = ArchType::ConfRegType;
// Initialization sequence
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::TIMER, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::WBSTAR, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::NOP)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::RCRC)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FAR, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::UNKNOWN, {0x0}));
// Configuration Options 0
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::COR0, {0x38003fe5}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::COR1, {0x400000}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::IDCODE, {part->idcode()}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::SWITCH)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x1}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL0, {0x101}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL1, {0x0}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FAR, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::WCFG)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
// Frame data write
out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
TYPE1, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FDRI, {}));
out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FDRI, packet_data));
// Finalization sequence
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::RCRC)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::GRESTORE)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::LFRM)}));
for (int ii = 0; ii < 100; ++ii) {
out_packets.emplace_back(
new NopPacket<ConfigurationRegister>());
}
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::START)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FAR, {0x3be0000}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x101}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL0, {0x101}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::RCRC)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::DESYNC)}));
for (int ii = 0; ii < 400; ++ii) {
out_packets.emplace_back(
new NopPacket<ConfigurationRegister>());
}
}
template <>
void Configuration<UltraScalePlus>::createConfigurationPackage(
UltraScalePlus::ConfigurationPackage& out_packets,
@ -399,6 +511,5 @@ void Configuration<UltraScalePlus>::createConfigurationPackage(
new NopPacket<ConfigurationRegister>());
}
}
} // namespace xilinx
} // namespace prjxray

View File

@ -8,10 +8,17 @@ void Frames<Series7>::updateECC(typename Frames<Series7>::FrameData& data) {
xc7series::updateECC(data);
}
template <>
void Frames<UltraScale>::updateECC(
typename Frames<UltraScale>::FrameData& data) {
xc7series::updateECC(data);
}
template <>
void Frames<UltraScalePlus>::updateECC(
typename Frames<UltraScalePlus>::FrameData& data) {
xc7series::updateECC(data);
}
} // namespace xilinx
} // namespace prjxray

View File

@ -40,6 +40,7 @@ struct Frames2BitWriter {
}
if (std::is_same<ArchType, xilinx::Series7>::value ||
std::is_same<ArchType, xilinx::UltraScale>::value ||
std::is_same<ArchType, xilinx::UltraScalePlus>::value) {
// In case the frames input file is missing some frames
// that are in the tilegrid