blob: 20dea9c34674953df64a945ec71e8da1aa66b288 [file] [log] [blame]
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_H_
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_H_
#include <map>
#include <absl/types/span.h>
#include <prjxray/bit_ops.h>
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
#include <prjxray/xilinx/xc7series/configuration_packet.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <prjxray/xilinx/xc7series/part.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class Configuration {
public:
using FrameMap = std::map<FrameAddress, absl::Span<const uint32_t>>;
template <typename Collection>
static absl::optional<Configuration> InitWithPackets(
const Part& part,
Collection& packets);
Configuration(const Part& part,
std::map<FrameAddress, std::vector<uint32_t>>* frames)
: part_(part) {
for (auto& frame : *frames) {
frames_[frame.first] =
absl::Span<const uint32_t>(frame.second);
}
}
Configuration(const Part& part, const FrameMap& frames)
: part_(part), frames_(std::move(frames)) {}
const Part& part() const { return part_; }
const FrameMap& frames() const { return frames_; }
private:
static constexpr int kWordsPerFrame = 101;
Part part_;
FrameMap frames_;
};
template <typename Collection>
absl::optional<Configuration> Configuration::InitWithPackets(
const 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;
FrameAddress current_frame_address = 0;
Configuration::FrameMap frames;
for (auto packet : packets) {
if (packet.opcode() != ConfigurationPacket::Opcode::Write) {
continue;
}
switch (packet.address()) {
case ConfigurationRegister::MASK:
if (packet.data().size() < 1)
continue;
mask_register = packet.data()[0];
break;
case ConfigurationRegister::CTL1:
if (packet.data().size() < 1)
continue;
ctl1_register =
packet.data()[0] & mask_register;
break;
case ConfigurationRegister::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 ConfigurationRegister::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 ConfigurationRegister::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 ConfigurationRegister::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 += kWordsPerFrame) {
frames[current_frame_address] =
packet.data().subspan(
ii, kWordsPerFrame);
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->row() !=
current_frame_address.row()) {
ii += 2 * kWordsPerFrame;
}
current_frame_address = *next_address;
}
break;
}
default:
break;
}
}
return Configuration(part, frames);
}
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_H_