blob: 6236345ceb4461fe598ac11aceb31e16eb246f69 [file] [log] [blame]
#ifndef PRJXRAY_LIB_XILINX_CONFIGURATION_H_
#define PRJXRAY_LIB_XILINX_CONFIGURATION_H_
#include <map>
#include <type_traits>
#include <absl/types/span.h>
#include <prjxray/bit_ops.h>
#include <prjxray/xilinx/architectures.h>
#include <prjxray/xilinx/frames.h>
namespace prjxray {
namespace xilinx {
template <typename ArchType>
class Configuration {
public:
using FrameMap = std::map<typename ArchType::FrameAddress,
absl::Span<const uint32_t>>;
using PacketData = std::vector<uint32_t>;
// Returns a configuration, i.e. collection of frame addresses
// and corresponding data from a collection of configuration packets.
template <typename Collection>
static absl::optional<Configuration<ArchType>> InitWithPackets(
const typename ArchType::Part& part,
Collection& packets);
// Creates the complete configuration package which is later on
// used by the bitstream writer to generate the bitstream file.
// The pacakge forms a sequence suitable for Xilinx devices.
// The programming sequence for Series-7 is taken from
// https://www.kc8apf.net/2018/05/unpacking-xilinx-7-series-bitstreams-part-2/
static void createConfigurationPackage(
typename ArchType::ConfigurationPackage& out_packets,
const PacketData& packet_data,
absl::optional<typename ArchType::Part>& part);
// Returns the payload for a type 2 packet
// which allows for bigger payload compared to type 1.
static PacketData createType2ConfigurationPacketData(
const typename Frames<ArchType>::Frames2Data& frames,
absl::optional<typename ArchType::Part>& part);
Configuration(const typename ArchType::Part& part,
std::map<typename ArchType::FrameAddress,
std::vector<uint32_t>>* frames)
: part_(part) {
for (auto& frame : *frames) {
frames_[frame.first] =
absl::Span<const uint32_t>(frame.second);
}
}
Configuration(const typename ArchType::Part& part,
const FrameMap& frames)
: part_(part), frames_(std::move(frames)) {}
const typename ArchType::Part& part() const { return part_; }
const FrameMap& frames() const { return frames_; }
private:
typename ArchType::Part part_;
FrameMap frames_;
};
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));
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 <typename Collection>
absl::optional<Configuration<Spartan6>>
Configuration<Spartan6>::InitWithPackets(const typename Spartan6::Part& part,
Collection& packets) {
using ArchType = Spartan6;
// Registers that can be directly written to.
uint32_t command_register = 0;
uint32_t frame_address_register = 0;
uint32_t mask_register = 0;
__attribute__((unused)) uint32_t ctl1_register = 0;
// 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::CTL:
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 FDRI.
if (command_register == 0x1) {
start_new_write = true;
}
break;
case ArchType::ConfRegType::IDCODE: {
// This really should be a two-word write.
if (packet.data().size() < 2)
continue;
// If the IDCODE doesn't match our expected
// part, consider the bitstream invalid.
uint32_t idcode = (packet.data()[0] << 16) |
(packet.data()[1]);
if (idcode != part.idcode()) {
return {};
}
break;
}
// UG380 describes the frame addressing scheme where two
// words for FAR_MAJ update FAR_MAJ anda FAR_MIN -
// FAR_MAJ comes first
case ArchType::ConfRegType::FAR_MAJ: {
size_t packet_size = packet.data().size();
assert(packet_size < 3);
if (packet_size < 1) {
continue;
} else if (packet_size < 2) {
frame_address_register =
(packet.data()[0] & 0xFFFF) << 16;
} else {
frame_address_register =
((packet.data()[0] & 0xFFFF)
<< 16) |
(packet.data()[1] & 0xFFFF);
}
break;
}
case ArchType::ConfRegType::FAR_MIN:
// This really should be a one-word write.
if (packet.data().size() < 1)
continue;
frame_address_register |=
packet.data()[0] & 0x3FF;
break;
case ArchType::ConfRegType::FDRI: {
if (start_new_write) {
current_frame_address =
frame_address_register;
start_new_write = false;
}
// Spartan6 frames are 65-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;
current_frame_address = *next_address;
}
break;
}
default:
break;
}
}
return Configuration(part, frames);
}
template <typename ArchType>
template <typename Collection>
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;
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<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;
}
// 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) {
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
#endif // PRJXRAY_LIB_XILINX_CONFIGURATION_H_