mirror of https://github.com/openXC7/prjxray.git
lib: Xilinx 7-Series configuration packet
Encapsulates configuration packets used in Xilinx 7-Series bitstream format. The same class handles both Type 1 and Type 2 packets. Signed-off-by: Rick Altherr <kc8apf@kc8apf.net> Signed-off-by: Tim 'mithro' Ansell <mithro@mithis.com>
This commit is contained in:
parent
fdf0bc46a1
commit
4bc1567103
|
|
@ -1,9 +1,11 @@
|
|||
add_library(libprjxray
|
||||
database.cc
|
||||
memory_mapped_file.cc
|
||||
segbits_file_reader.cc)
|
||||
segbits_file_reader.cc
|
||||
xilinx_7series_configuration_packet.cc
|
||||
)
|
||||
target_include_directories(libprjxray PUBLIC "include")
|
||||
target_link_libraries(libprjxray absl::strings)
|
||||
target_link_libraries(libprjxray absl::optional absl::strings absl::span)
|
||||
|
||||
if (PRJXRAY_BUILD_TESTING)
|
||||
add_executable(big_endian_span_test big_endian_span_test.cc)
|
||||
|
|
@ -27,4 +29,11 @@ if (PRJXRAY_BUILD_TESTING)
|
|||
add_test(NAME segbits_file_reader_test
|
||||
COMMAND segbits_file_reader_test
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
|
||||
|
||||
add_executable(xilinx_7series_configuration_packet_test
|
||||
xilinx_7series_configuration_packet_test.cc)
|
||||
target_link_libraries(xilinx_7series_configuration_packet_test
|
||||
libprjxray gtest_main)
|
||||
add_test(NAME xilinx_7series_configuration_packet_test
|
||||
COMMAND xilinx_7series_configuration_packet_test)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H
|
||||
#define PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <absl/types/optional.h>
|
||||
#include <absl/types/span.h>
|
||||
|
||||
namespace prjxray {
|
||||
|
||||
class Xilinx7SeriesConfigurationPacket {
|
||||
public:
|
||||
typedef std::pair<absl::Span<uint32_t>,
|
||||
absl::optional<Xilinx7SeriesConfigurationPacket>>
|
||||
ParseResult;
|
||||
|
||||
enum Opcode {
|
||||
NOP = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
/* reserved = 3 */
|
||||
};
|
||||
|
||||
Xilinx7SeriesConfigurationPacket(Opcode opcode, uint32_t address,
|
||||
const absl::Span<uint32_t> &data)
|
||||
: opcode_(opcode), address_(address), data_(std::move(data)) {}
|
||||
|
||||
// Attempt to read a configuration packet from a sequence of
|
||||
// 32-bit, big-endian words. If successful, returns the packet read and
|
||||
// a span containing any words remaining after the packet. If a valid
|
||||
// header is found but there are insufficient words provided for the
|
||||
// complete packet, the original span<> is returned unchanged and no
|
||||
// packet is produced. If no valid header is found, an empty span is
|
||||
// returned.
|
||||
static ParseResult InitWithWords(
|
||||
absl::Span<uint32_t> words,
|
||||
const Xilinx7SeriesConfigurationPacket *previous_packet = nullptr);
|
||||
|
||||
const Opcode opcode() const { return opcode_; }
|
||||
const uint32_t address() const { return address_; }
|
||||
const absl::Span<uint32_t> &data() const { return data_; }
|
||||
|
||||
private:
|
||||
Opcode opcode_;
|
||||
uint32_t address_;
|
||||
absl::Span<uint32_t> data_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& o,
|
||||
const Xilinx7SeriesConfigurationPacket &packet);
|
||||
|
||||
} // namespace prjxray
|
||||
|
||||
#endif // PRJXRAY_LIB_XILINX_7SERIES_CONFIGURATION_PACKET_H
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
#include <prjxray/xilinx_7series_configuration_packet.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include <prjxray/bit_ops.h>
|
||||
|
||||
namespace prjxray {
|
||||
|
||||
std::pair<absl::Span<uint32_t>, absl::optional<Xilinx7SeriesConfigurationPacket>>
|
||||
Xilinx7SeriesConfigurationPacket::InitWithWords(
|
||||
absl::Span<uint32_t> words,
|
||||
const Xilinx7SeriesConfigurationPacket *previous_packet) {
|
||||
// Need at least one 32-bit word to have a valid packet header.
|
||||
if (words.size() < 1) return {words, {}};
|
||||
|
||||
uint32_t header_type = bit_field_get(words[0], 31, 29);
|
||||
switch (header_type) {
|
||||
case 0b001: {
|
||||
Opcode opcode = static_cast<Opcode>(
|
||||
bit_field_get(words[0], 28, 27));
|
||||
uint32_t address = bit_field_get(words[0], 26, 13);
|
||||
uint32_t data_word_count = bit_field_get(words[0], 10, 0);
|
||||
|
||||
// If the full packet has not been received, return as though
|
||||
// no valid packet was found.
|
||||
if (data_word_count > words.size() - 1) {
|
||||
return {words, {}};
|
||||
}
|
||||
|
||||
return {words.subspan(data_word_count+1),
|
||||
{{opcode, address, words.subspan(1, data_word_count)}}};
|
||||
}
|
||||
case 0b010: {
|
||||
absl::optional<Xilinx7SeriesConfigurationPacket> packet;
|
||||
Opcode opcode = static_cast<Opcode>(
|
||||
bit_field_get(words[0], 28, 27));
|
||||
uint32_t data_word_count = bit_field_get(words[0], 26, 0);
|
||||
|
||||
// If the full packet has not been received, return as though
|
||||
// no valid packet was found.
|
||||
if (data_word_count > words.size() - 1) {
|
||||
return {words, {}};
|
||||
}
|
||||
|
||||
if (previous_packet) {
|
||||
packet = Xilinx7SeriesConfigurationPacket(
|
||||
opcode, previous_packet->address(),
|
||||
words.subspan(1, data_word_count));
|
||||
}
|
||||
|
||||
return {words.subspan(data_word_count + 1), packet};
|
||||
}
|
||||
default:
|
||||
return {{}, {}};
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o,
|
||||
const Xilinx7SeriesConfigurationPacket &packet) {
|
||||
switch (packet.opcode()) {
|
||||
case Xilinx7SeriesConfigurationPacket::Opcode::NOP:
|
||||
return o << "[NOP]" << std::endl;
|
||||
case Xilinx7SeriesConfigurationPacket::Opcode::Read:
|
||||
return o << "[Read Address=" << packet.address()
|
||||
<< " Length=" << packet.data().size() <<
|
||||
"]" << std::endl;
|
||||
case Xilinx7SeriesConfigurationPacket::Opcode::Write:
|
||||
return o << "[Write Address=" << packet.address()
|
||||
<< " Length=" << packet.data().size() <<
|
||||
"]" << std::endl;
|
||||
default:
|
||||
return o << "[Invalid Opcode]" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace prjxray
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
#include <prjxray/xilinx_7series_configuration_packet.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <absl/meta/type_traits.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <prjxray/bit_ops.h>
|
||||
|
||||
constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 31, 29, 0x1);
|
||||
|
||||
constexpr uint32_t MakeType1(const int opcode, const int address, const int word_count) {
|
||||
return prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(0x0, 31, 29, 0x1),
|
||||
28, 27, opcode),
|
||||
26, 13, address),
|
||||
10, 0, word_count);
|
||||
}
|
||||
|
||||
constexpr uint32_t MakeType2(const int opcode, const int word_count) {
|
||||
return prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(0x0, 31, 29, 0x2),
|
||||
28, 27, opcode),
|
||||
26, 0, word_count);
|
||||
}
|
||||
|
||||
|
||||
TEST(ConfigPacket, InitWithZeroBytes) {
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords({});
|
||||
|
||||
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||
EXPECT_FALSE(packet.second);
|
||||
}
|
||||
|
||||
TEST(ConfigPacket, InitWithType1Nop) {
|
||||
std::vector<uint32_t> words{kType1NOP};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span);
|
||||
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||
ASSERT_TRUE(packet.second);
|
||||
EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::NOP);
|
||||
EXPECT_EQ(packet.second->address(), static_cast<uint32_t>(0));
|
||||
EXPECT_EQ(packet.second->data(), absl::Span<uint32_t>());
|
||||
}
|
||||
|
||||
TEST(ConfigPacket, InitWithType1Read) {
|
||||
std::vector<uint32_t> words{MakeType1(0x1, 0x1234, 2), 0xAA, 0xBB};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span);
|
||||
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||
ASSERT_TRUE(packet.second);
|
||||
EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Read);
|
||||
EXPECT_EQ(packet.second->address(), static_cast<uint32_t>(0x1234));
|
||||
EXPECT_EQ(packet.second->data(), word_span.subspan(1));
|
||||
}
|
||||
|
||||
TEST(ConfigPacket, InitWithType1Write) {
|
||||
std::vector<uint32_t> words{MakeType1(0x2, 0x1234, 2), 0xAA, 0xBB};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span);
|
||||
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||
ASSERT_TRUE(packet.second);
|
||||
EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Write);
|
||||
EXPECT_EQ(packet.second->address(), static_cast<uint32_t>(0x1234));
|
||||
EXPECT_EQ(packet.second->data(), word_span.subspan(1));
|
||||
}
|
||||
|
||||
TEST(ConfigPacket, InitWithType2WithoutPreviousPacketFails) {
|
||||
std::vector<uint32_t> words{MakeType2(0x01, 12)};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(word_span);
|
||||
EXPECT_EQ(packet.first, words);
|
||||
EXPECT_FALSE(packet.second);
|
||||
}
|
||||
|
||||
TEST(ConfigPacket, InitWithType2WithPreviousPacket) {
|
||||
prjxray::Xilinx7SeriesConfigurationPacket previous_packet(
|
||||
prjxray::Xilinx7SeriesConfigurationPacket::Read,
|
||||
0x1234, absl::Span<uint32_t>());
|
||||
std::vector<uint32_t> words{MakeType2(0x01, 12), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = prjxray::Xilinx7SeriesConfigurationPacket::InitWithWords(
|
||||
word_span, &previous_packet);
|
||||
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||
ASSERT_TRUE(packet.second);
|
||||
EXPECT_EQ(packet.second->opcode(), prjxray::Xilinx7SeriesConfigurationPacket::Opcode::Read);
|
||||
EXPECT_EQ(packet.second->address(), static_cast<uint32_t>(0x1234));
|
||||
EXPECT_EQ(packet.second->data(), word_span.subspan(1));
|
||||
}
|
||||
Loading…
Reference in New Issue