Refactor Configuration::InitWithPackets template method

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
Tomasz Michalak 2019-10-17 21:52:10 +02:00
parent 7bc54d0c56
commit d91b0a5006
1 changed files with 7 additions and 270 deletions

View File

@ -65,12 +65,11 @@ class Configuration {
FrameMap frames_;
};
template <>
template <typename ArchType>
template <typename Collection>
absl::optional<Configuration<Series7>> Configuration<Series7>::InitWithPackets(
const typename Series7::Part& part,
Collection& packets) {
using ArchType = Series7;
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;
@ -154,8 +153,9 @@ absl::optional<Configuration<Series7>> Configuration<Series7>::InitWithPackets(
start_new_write = false;
}
// 7-series frames are 101-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) {
@ -196,269 +196,6 @@ absl::optional<Configuration<Series7>> Configuration<Series7>::InitWithPackets(
return Configuration(part, frames);
}
template <>
template <typename Collection>
absl::optional<Configuration<UltraScale>>
Configuration<UltraScale>::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<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;
}
// 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 <typename Collection>
absl::optional<Configuration<UltraScalePlus>>
Configuration<UltraScalePlus>::InitWithPackets(
const typename UltraScale::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<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;
}
// 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