lib: xc7series: class to hold a configuration

Currently is able to parse IDCODE and frames from a sequence of
ConfigurationPackets.

Signed-off-by: Rick Altherr <kc8apf@kc8apf.net>
Signed-off-by: Tim 'mithro' Ansell <mithro@mithis.com>
This commit is contained in:
Rick Altherr 2017-12-05 13:58:40 -08:00 committed by Tim 'mithro' Ansell
parent 6a17a92d83
commit bd3fa58584
3 changed files with 263 additions and 0 deletions

View File

@ -57,6 +57,13 @@ if (PRJXRAY_BUILD_TESTING)
add_test(NAME xilinx_xc7series_configuration_frame_address_test
COMMAND xilinx_xc7series_configuration_frame_address_test)
add_executable(xilinx_xc7series_configuration_test
xilinx/xc7series/configuration_test.cc)
target_link_libraries(xilinx_xc7series_configuration_test
libprjxray gtest_main absl::span)
add_test(NAME xilinx_xc7series_configuration_test
COMMAND xilinx_xc7series_configuration_test)
add_executable(xilinx_xc7series_configuration_packet_test
xilinx/xc7series/configuration_packet_test.cc)
target_link_libraries(xilinx_xc7series_configuration_packet_test

View File

@ -0,0 +1,145 @@
#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_frame_address.h>
#include <prjxray/xilinx/xc7series/part.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class Configuration {
public:
using FrameMap = std::map<ConfigurationFrameAddress,
absl::Span<uint32_t>>;
template<typename Collection>
static absl::optional<Configuration> InitWithPackets(
const Part& part, Collection &packets);
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;
ConfigurationFrameAddress 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.
int frames_written = packet.data().size() /
kWordsPerFrame;
for (int ii = 0; ii < frames_written; ++ii) {
frames[current_frame_address] =
packet.data().subspan(
ii * kWordsPerFrame,
kWordsPerFrame);
auto next_address =
part.GetNextConfigurationFrameAddress(
current_frame_address);
if (next_address) {
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_

View File

@ -0,0 +1,111 @@
#include <prjxray/xilinx/xc7series/configuration.h>
#include <cstdint>
#include <vector>
#include <absl/types/span.h>
#include <gtest/gtest.h>
#include <prjxray/xilinx/xc7series/configuration_packet.h>
#include <prjxray/xilinx/xc7series/configuration_register.h>
#include <prjxray/xilinx/xc7series/part.h>
namespace xc7series = prjxray::xilinx::xc7series;
TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4567, 0x4568));
xc7series::Part test_part(0x1234, test_part_ranges);
std::vector<uint32_t> idcode{0x1234};
std::vector<uint32_t> cmd{0x0001};
std::vector<uint32_t> frame_address{0x4567};
std::vector<uint32_t> frame(101, 0xAA);
std::vector<xc7series::ConfigurationPacket> packets{
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::IDCODE,
absl::MakeSpan(idcode),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::FAR,
absl::MakeSpan(frame_address),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::CMD,
absl::MakeSpan(cmd),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::FDRI,
absl::MakeSpan(frame),
},
};
auto test_config = xc7series::Configuration::InitWithPackets(test_part, packets);
ASSERT_TRUE(test_config);
EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(1));
EXPECT_EQ(test_config->frames().at(0x4567), frame);
}
TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4560, 0x4570));
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4580, 0x4590));
xc7series::Part test_part(0x1234, test_part_ranges);
std::vector<uint32_t> idcode{0x1234};
std::vector<uint32_t> cmd{0x0001};
std::vector<uint32_t> frame_address{0x456F};
std::vector<uint32_t> frame(202, 0xAA);
std::fill_n(frame.begin() + 101, 101, 0xBB);
std::vector<xc7series::ConfigurationPacket> packets{
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::IDCODE,
absl::MakeSpan(idcode),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::FAR,
absl::MakeSpan(frame_address),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::CMD,
absl::MakeSpan(cmd),
},
{
static_cast<unsigned int>(0x1),
xc7series::ConfigurationPacket::Opcode::Write,
xc7series::ConfigurationRegister::FDRI,
absl::MakeSpan(frame),
},
};
auto test_config = xc7series::Configuration::InitWithPackets(test_part, packets);
ASSERT_TRUE(test_config);
absl::Span<uint32_t> frame_span(frame);
EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(2));
EXPECT_EQ(test_config->frames().at(0x456F), frame_span.subspan(0, 101));
EXPECT_EQ(test_config->frames().at(0x4580), frame_span.subspan(101));
}