mirror of https://github.com/openXC7/prjxray.git
lib: xc7series: generate packets from a Configuration
ConfigurationPacketizer translates a Configuration's frame data to a sequence of ConfigurationPackets that will write that configuration to the configuration's Part. Signed-off-by: Rick Altherr <kc8apf@kc8apf.net>
This commit is contained in:
parent
5749352937
commit
012494552d
|
|
@ -7,6 +7,7 @@ add_library(libprjxray
|
|||
xilinx/xc7series/configuration_bus.cc
|
||||
xilinx/xc7series/configuration_column.cc
|
||||
xilinx/xc7series/configuration_packet.cc
|
||||
xilinx/xc7series/configuration_packetizer.cc
|
||||
xilinx/xc7series/configuration_register.cc
|
||||
xilinx/xc7series/frame_address.cc
|
||||
xilinx/xc7series/global_clock_region.cc
|
||||
|
|
@ -47,6 +48,7 @@ if (PRJXRAY_BUILD_TESTING)
|
|||
xilinx/xc7series/configuration_column_test.cc
|
||||
xilinx/xc7series/configuration_test.cc
|
||||
xilinx/xc7series/configuration_packet_test.cc
|
||||
xilinx/xc7series/configuration_packetizer_test.cc
|
||||
xilinx/xc7series/global_clock_region_test.cc
|
||||
xilinx/xc7series/part_test.cc
|
||||
xilinx/xc7series/row_test.cc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_
|
||||
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_
|
||||
|
||||
#include <absl/types/optional.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration_packet.h>
|
||||
|
||||
namespace prjxray {
|
||||
namespace xilinx {
|
||||
namespace xc7series {
|
||||
|
||||
class ConfigurationPacketizer {
|
||||
public:
|
||||
class iterator
|
||||
: std::iterator<std::input_iterator_tag, ConfigurationPacket> {
|
||||
public:
|
||||
iterator(const Part* part,
|
||||
Configuration::FrameMap::const_iterator begin,
|
||||
Configuration::FrameMap::const_iterator end);
|
||||
|
||||
iterator& operator++();
|
||||
|
||||
bool operator==(const iterator& other) const;
|
||||
bool operator!=(const iterator& other) const;
|
||||
|
||||
const ConfigurationPacket& operator*() const;
|
||||
const ConfigurationPacket* operator->() const;
|
||||
|
||||
private:
|
||||
friend class ConfigurationPacketizer;
|
||||
|
||||
enum class State {
|
||||
Start,
|
||||
FrameAddressWritten,
|
||||
FrameDataWritten,
|
||||
ZeroPadWritten,
|
||||
Finished,
|
||||
};
|
||||
|
||||
const Part* part_;
|
||||
State state_;
|
||||
Configuration::FrameMap::const_iterator frame_cur_;
|
||||
Configuration::FrameMap::const_iterator frame_end_;
|
||||
absl::optional<uint32_t> frame_address_;
|
||||
absl::optional<ConfigurationPacket> packet_;
|
||||
int zero_pad_packets_to_write_;
|
||||
};
|
||||
|
||||
ConfigurationPacketizer(const Configuration& config);
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
const Configuration& config_;
|
||||
};
|
||||
|
||||
} // namespace xc7series
|
||||
} // namespace xilinx
|
||||
} // namespace prjxray
|
||||
|
||||
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_PACKETIZER_H_
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
#include <prjxray/xilinx/xc7series/configuration_packetizer.h>
|
||||
|
||||
#include <absl/types/span.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration_register.h>
|
||||
|
||||
namespace prjxray {
|
||||
namespace xilinx {
|
||||
namespace xc7series {
|
||||
|
||||
ConfigurationPacketizer::ConfigurationPacketizer(const Configuration& config)
|
||||
: config_(config) {}
|
||||
|
||||
ConfigurationPacketizer::iterator ConfigurationPacketizer::begin() const {
|
||||
return iterator(&config_.part(), config_.frames().begin(),
|
||||
config_.frames().end());
|
||||
}
|
||||
|
||||
ConfigurationPacketizer::iterator ConfigurationPacketizer::end() const {
|
||||
return iterator(&config_.part(), config_.frames().end(),
|
||||
config_.frames().end());
|
||||
}
|
||||
|
||||
ConfigurationPacketizer::iterator::iterator(
|
||||
const Part* part,
|
||||
Configuration::FrameMap::const_iterator begin,
|
||||
Configuration::FrameMap::const_iterator end)
|
||||
: part_(part),
|
||||
state_(begin != end ? State::Start : State::Finished),
|
||||
frame_cur_(begin),
|
||||
frame_end_(end) {
|
||||
this->operator++();
|
||||
}
|
||||
|
||||
const ConfigurationPacket& ConfigurationPacketizer::iterator::operator*()
|
||||
const {
|
||||
return *packet_;
|
||||
}
|
||||
|
||||
const ConfigurationPacket* ConfigurationPacketizer::iterator::operator->()
|
||||
const {
|
||||
return &(*packet_);
|
||||
}
|
||||
|
||||
bool ConfigurationPacketizer::iterator::operator==(
|
||||
const ConfigurationPacketizer::iterator& other) const {
|
||||
return state_ == other.state_ && frame_cur_ == other.frame_cur_;
|
||||
}
|
||||
|
||||
bool ConfigurationPacketizer::iterator::operator!=(
|
||||
const ConfigurationPacketizer::iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
ConfigurationPacketizer::iterator& ConfigurationPacketizer::iterator::
|
||||
operator++() {
|
||||
// Frames are accessed via an indirect addressing scheme using the FAR
|
||||
// and FDRI registers. Writes begin with writing the target frame
|
||||
// address to FAR and then the frame data is written to FDRI. The
|
||||
// following state machine primarily follows that flow:
|
||||
// Start -> FrameAddressWritten -> FrameDataWritten -> Start....
|
||||
// When the last frame within a row is written, 2 full frames (202
|
||||
// words) of zero padding need to be written after the frame data.
|
||||
switch (state_) {
|
||||
case State::FrameDataWritten: {
|
||||
// If this is the last address in this row (i.e. the
|
||||
// next valid address known by the part is in a
|
||||
// different row, half, or bus type), start a zero fill.
|
||||
// Otherwise, increment the frame iterator and fall
|
||||
// through to Start.
|
||||
auto& this_address = frame_cur_->first;
|
||||
auto next_address =
|
||||
part_->GetNextFrameAddress(frame_cur_->first);
|
||||
if (next_address &&
|
||||
(next_address->block_type() !=
|
||||
this_address.block_type() ||
|
||||
next_address->is_bottom_half_rows() !=
|
||||
this_address.is_bottom_half_rows() ||
|
||||
next_address->row() != this_address.row())) {
|
||||
zero_pad_packets_to_write_ = 202;
|
||||
// Type 0 frames aren't documented in UG470. In
|
||||
// practice, they are used to zero pad in the
|
||||
// bitstream.
|
||||
packet_ = ConfigurationPacket(
|
||||
0, ConfigurationPacket::Opcode::NOP,
|
||||
ConfigurationRegister::CRC, {});
|
||||
state_ = State::ZeroPadWritten;
|
||||
break;
|
||||
}
|
||||
|
||||
++frame_cur_;
|
||||
}
|
||||
case State::Start:
|
||||
if (frame_cur_ == frame_end_) {
|
||||
state_ = State::Finished;
|
||||
frame_address_.reset();
|
||||
packet_.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
frame_address_ = frame_cur_->first;
|
||||
packet_ = ConfigurationPacket(
|
||||
1, ConfigurationPacket::Opcode::Write,
|
||||
ConfigurationRegister::FAR,
|
||||
absl::Span<uint32_t>(&frame_address_.value(), 1));
|
||||
state_ = State::FrameAddressWritten;
|
||||
break;
|
||||
case State::FrameAddressWritten:
|
||||
packet_ = ConfigurationPacket(
|
||||
1, ConfigurationPacket::Opcode::Write,
|
||||
ConfigurationRegister::FDRI, frame_cur_->second);
|
||||
state_ = State::FrameDataWritten;
|
||||
break;
|
||||
case State::ZeroPadWritten:
|
||||
if (--zero_pad_packets_to_write_ == 1) {
|
||||
++frame_cur_;
|
||||
state_ = State::Start;
|
||||
}
|
||||
break;
|
||||
case State::Finished:
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace xc7series
|
||||
} // namespace xilinx
|
||||
} // namespace prjxray
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
#include <prjxray/xilinx/xc7series/configuration_packetizer.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace xc7series = prjxray::xilinx::xc7series;
|
||||
|
||||
TEST(ConfigurationPacketizerTest, EmptyConfigGeneratesNoPackets) {
|
||||
auto part = xc7series::Part::FromFile("configuration_test.yaml");
|
||||
ASSERT_TRUE(part);
|
||||
|
||||
std::map<xc7series::FrameAddress, std::vector<uint32_t>> frames;
|
||||
xc7series::Configuration config(*part, &frames);
|
||||
xc7series::ConfigurationPacketizer packetizer(config);
|
||||
|
||||
EXPECT_EQ(packetizer.begin(), packetizer.end());
|
||||
}
|
||||
|
||||
TEST(ConfigurationPacketizerTest, ConfigWithFramesGeneratesPackets) {
|
||||
auto part = xc7series::Part::FromFile("configuration_test.yaml");
|
||||
ASSERT_TRUE(part);
|
||||
|
||||
std::map<xc7series::FrameAddress, std::vector<uint32_t>> frames;
|
||||
frames[0] = std::vector<uint32_t>(101, 0xAA);
|
||||
frames[1] = std::vector<uint32_t>(101, 0xBB);
|
||||
|
||||
xc7series::Configuration config(*part, &frames);
|
||||
|
||||
EXPECT_EQ(config.frames().at(0), frames[0]);
|
||||
EXPECT_EQ(config.frames().at(1), frames[1]);
|
||||
|
||||
xc7series::ConfigurationPacketizer packetizer(config);
|
||||
|
||||
auto packet = packetizer.begin();
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
|
||||
// Write 0x0 to FAR
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR);
|
||||
EXPECT_EQ(packet->data(), std::vector<uint32_t>{0});
|
||||
|
||||
++packet;
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI);
|
||||
EXPECT_EQ(packet->data(), frames[0]);
|
||||
|
||||
++packet;
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR);
|
||||
EXPECT_EQ(packet->data(), std::vector<uint32_t>{1});
|
||||
|
||||
++packet;
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI);
|
||||
EXPECT_EQ(packet->data(), frames[1]);
|
||||
|
||||
++packet;
|
||||
EXPECT_EQ(packet, packetizer.end());
|
||||
}
|
||||
|
||||
TEST(ConfigurationPacketizerTest, ConfigWithFrameAtEndOfRowGeneratesZerofill) {
|
||||
auto part = xc7series::Part::FromFile("configuration_test.yaml");
|
||||
ASSERT_TRUE(part);
|
||||
|
||||
xc7series::FrameAddress last_frame_in_first_row(
|
||||
xc7series::BlockType::CLB_IO_CLK, false, 0, 43, 41);
|
||||
|
||||
std::map<xc7series::FrameAddress, std::vector<uint32_t>> frames;
|
||||
frames[last_frame_in_first_row] = std::vector<uint32_t>(101, 0xAA);
|
||||
|
||||
xc7series::Configuration config(*part, &frames);
|
||||
xc7series::ConfigurationPacketizer packetizer(config);
|
||||
|
||||
auto packet = packetizer.begin();
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FAR);
|
||||
EXPECT_EQ(packet->data(),
|
||||
std::vector<uint32_t>{last_frame_in_first_row});
|
||||
|
||||
++packet;
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(1));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet->address(), xc7series::ConfigurationRegister::FDRI);
|
||||
EXPECT_EQ(packet->data(), frames[last_frame_in_first_row]);
|
||||
|
||||
for (int ii = 0; ii < 202; ++ii) {
|
||||
++packet;
|
||||
ASSERT_NE(packet, packetizer.end());
|
||||
EXPECT_EQ(packet->header_type(), static_cast<unsigned int>(0));
|
||||
EXPECT_EQ(packet->opcode(),
|
||||
xc7series::ConfigurationPacket::Opcode::NOP);
|
||||
EXPECT_EQ(packet->address(),
|
||||
xc7series::ConfigurationRegister::CRC);
|
||||
EXPECT_EQ(packet->data(), std::vector<uint32_t>());
|
||||
}
|
||||
|
||||
++packet;
|
||||
EXPECT_EQ(packet, packetizer.end());
|
||||
}
|
||||
Loading…
Reference in New Issue