mirror of https://github.com/openXC7/prjxray.git
Merge pull request #1088 from antmicro/spartan6_support
Bitstream Tools: Add Spartan6 support
This commit is contained in:
commit
b243db2d05
|
|
@ -7,6 +7,14 @@ add_library(libprjxray
|
||||||
xilinx/configuration_register.cc
|
xilinx/configuration_register.cc
|
||||||
xilinx/configuration.cc
|
xilinx/configuration.cc
|
||||||
xilinx/frames.cc
|
xilinx/frames.cc
|
||||||
|
# Spartan6 specific
|
||||||
|
xilinx/spartan6/frame_address.cc
|
||||||
|
xilinx/spartan6/global_clock_region.cc
|
||||||
|
xilinx/spartan6/part.cc
|
||||||
|
xilinx/spartan6/configuration_row.cc
|
||||||
|
xilinx/spartan6/block_type.cc
|
||||||
|
xilinx/spartan6/configuration_bus.cc
|
||||||
|
xilinx/spartan6/configuration_column.cc
|
||||||
# Series-7 specific
|
# Series-7 specific
|
||||||
xilinx/xc7series/frame_address.cc
|
xilinx/xc7series/frame_address.cc
|
||||||
xilinx/xc7series/global_clock_region.cc
|
xilinx/xc7series/global_clock_region.cc
|
||||||
|
|
@ -62,4 +70,22 @@ if (PRJXRAY_BUILD_TESTING)
|
||||||
add_test(NAME xilinx_xc7series_test
|
add_test(NAME xilinx_xc7series_test
|
||||||
COMMAND xilinx_xc7series_test
|
COMMAND xilinx_xc7series_test
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
|
||||||
|
|
||||||
|
add_executable(xilinx_spartan6_test
|
||||||
|
xilinx/tests/spartan6/bitstream_reader_test.cc
|
||||||
|
xilinx/tests/spartan6/bitstream_writer_test.cc
|
||||||
|
xilinx/tests/spartan6/block_type_test.cc
|
||||||
|
xilinx/tests/spartan6/configuration_bus_test.cc
|
||||||
|
xilinx/tests/spartan6/configuration_column_test.cc
|
||||||
|
xilinx/tests/spartan6/configuration_test.cc
|
||||||
|
xilinx/tests/spartan6/configuration_packet_test.cc
|
||||||
|
xilinx/tests/spartan6/frame_address_test.cc
|
||||||
|
xilinx/tests/spartan6/global_clock_region_test.cc
|
||||||
|
xilinx/tests/spartan6/part_test.cc
|
||||||
|
xilinx/tests/spartan6/row_test.cc
|
||||||
|
xilinx/tests/spartan6/frames_test.cc)
|
||||||
|
target_link_libraries(xilinx_spartan6_test libprjxray gtest_main)
|
||||||
|
add_test(NAME xilinx_spartan6_test
|
||||||
|
COMMAND xilinx_spartan6_test
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,23 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <prjxray/xilinx/configuration_packet.h>
|
#include <prjxray/xilinx/configuration_packet.h>
|
||||||
#include <prjxray/xilinx/configuration_register.h>
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/part.h>
|
||||||
#include <prjxray/xilinx/xc7series/frame_address.h>
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
#include <prjxray/xilinx/xc7series/part.h>
|
#include <prjxray/xilinx/xc7series/part.h>
|
||||||
|
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
class Spartan6;
|
||||||
class Series7;
|
class Series7;
|
||||||
class UltraScale;
|
class UltraScale;
|
||||||
class UltraScalePlus;
|
class UltraScalePlus;
|
||||||
|
|
||||||
class Architecture {
|
class Architecture {
|
||||||
public:
|
public:
|
||||||
using Container = absl::variant<Series7, UltraScale, UltraScalePlus>;
|
using Container =
|
||||||
|
absl::variant<Series7, UltraScale, UltraScalePlus, Spartan6>;
|
||||||
Architecture(const std::string& name) : name_(name) {}
|
Architecture(const std::string& name) : name_(name) {}
|
||||||
const std::string& name() const { return name_; }
|
const std::string& name() const { return name_; }
|
||||||
virtual ~Architecture() {}
|
virtual ~Architecture() {}
|
||||||
|
|
@ -28,6 +31,18 @@ class Architecture {
|
||||||
const std::string name_;
|
const std::string name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Spartan6 : public Architecture {
|
||||||
|
public:
|
||||||
|
using ConfRegType = Spartan6ConfigurationRegister;
|
||||||
|
using Part = spartan6::Part;
|
||||||
|
using ConfigurationPackage =
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<ConfRegType>>>;
|
||||||
|
using FrameAddress = spartan6::FrameAddress;
|
||||||
|
using WordType = uint16_t;
|
||||||
|
Spartan6() : Architecture("Spartan6") {}
|
||||||
|
static constexpr int words_per_frame = 65;
|
||||||
|
};
|
||||||
|
|
||||||
class Series7 : public Architecture {
|
class Series7 : public Architecture {
|
||||||
public:
|
public:
|
||||||
using ConfRegType = Series7ConfigurationRegister;
|
using ConfRegType = Series7ConfigurationRegister;
|
||||||
|
|
@ -57,7 +72,9 @@ class ArchitectureFactory {
|
||||||
public:
|
public:
|
||||||
static Architecture::Container create_architecture(
|
static Architecture::Container create_architecture(
|
||||||
const std::string& arch) {
|
const std::string& arch) {
|
||||||
if (arch == "Series7") {
|
if (arch == "Spartan6") {
|
||||||
|
return Spartan6();
|
||||||
|
} else if (arch == "Series7") {
|
||||||
return Series7();
|
return Series7();
|
||||||
} else if (arch == "UltraScale") {
|
} else if (arch == "UltraScale") {
|
||||||
return UltraScale();
|
return UltraScale();
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,8 @@ BitstreamReader<ArchType>::InitWithBytes(T bitstream) {
|
||||||
auto config_packets =
|
auto config_packets =
|
||||||
bitstream_span.subspan(sync_pos - bitstream.begin());
|
bitstream_span.subspan(sync_pos - bitstream.begin());
|
||||||
|
|
||||||
// Convert the bytes into 32-bit, big-endian words.
|
// Convert the bytes into 32-bit or 16-bit in case of Spartan6,
|
||||||
|
// big-endian words.
|
||||||
auto big_endian_reader =
|
auto big_endian_reader =
|
||||||
make_big_endian_span<typename ArchType::WordType>(config_packets);
|
make_big_endian_span<typename ArchType::WordType>(config_packets);
|
||||||
std::vector<uint32_t> words{big_endian_reader.begin(),
|
std::vector<uint32_t> words{big_endian_reader.begin(),
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ namespace xilinx {
|
||||||
|
|
||||||
uint32_t packet2header(
|
uint32_t packet2header(
|
||||||
const ConfigurationPacket<Series7ConfigurationRegister>& packet);
|
const ConfigurationPacket<Series7ConfigurationRegister>& packet);
|
||||||
|
uint32_t packet2header(
|
||||||
|
const ConfigurationPacket<Spartan6ConfigurationRegister>& packet);
|
||||||
// Writes out the complete Xilinx bitstream including
|
// Writes out the complete Xilinx bitstream including
|
||||||
// header, sync word and configuration sequence.
|
// header, sync word and configuration sequence.
|
||||||
template <typename ArchType>
|
template <typename ArchType>
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,130 @@ Configuration<ArchType>::createType2ConfigurationPacketData(
|
||||||
return packet_data;
|
return packet_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
template <typename Collection>
|
||||||
|
absl::optional<Configuration<Spartan6>>
|
||||||
|
Configuration<Spartan6>::InitWithPackets(const typename Spartan6::Part& part,
|
||||||
|
Collection& packets) {
|
||||||
|
using ArchType = Spartan6;
|
||||||
|
// Registers that can be directly written to.
|
||||||
|
uint32_t command_register = 0;
|
||||||
|
uint32_t frame_address_register = 0;
|
||||||
|
uint32_t mask_register = 0;
|
||||||
|
__attribute__((unused)) 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::CTL:
|
||||||
|
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 FDRI.
|
||||||
|
if (command_register == 0x1) {
|
||||||
|
start_new_write = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ArchType::ConfRegType::IDCODE: {
|
||||||
|
// This really should be a two-word write.
|
||||||
|
if (packet.data().size() < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the IDCODE doesn't match our expected
|
||||||
|
// part, consider the bitstream invalid.
|
||||||
|
uint32_t idcode = (packet.data()[0] << 16) |
|
||||||
|
(packet.data()[1]);
|
||||||
|
if (idcode != part.idcode()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// UG380 describes the frame addressing scheme where two
|
||||||
|
// words for FAR_MAJ update FAR_MAJ anda FAR_MIN -
|
||||||
|
// FAR_MAJ comes first
|
||||||
|
case ArchType::ConfRegType::FAR_MAJ: {
|
||||||
|
size_t packet_size = packet.data().size();
|
||||||
|
assert(packet_size < 3);
|
||||||
|
if (packet_size < 1) {
|
||||||
|
continue;
|
||||||
|
} else if (packet_size < 2) {
|
||||||
|
frame_address_register =
|
||||||
|
(packet.data()[0] & 0xFFFF) << 16;
|
||||||
|
} else {
|
||||||
|
frame_address_register =
|
||||||
|
((packet.data()[0] & 0xFFFF)
|
||||||
|
<< 16) |
|
||||||
|
(packet.data()[1] & 0xFFFF);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArchType::ConfRegType::FAR_MIN:
|
||||||
|
// This really should be a one-word write.
|
||||||
|
if (packet.data().size() < 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
frame_address_register |=
|
||||||
|
packet.data()[0] & 0x3FF;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ArchType::ConfRegType::FDRI: {
|
||||||
|
if (start_new_write) {
|
||||||
|
current_frame_address =
|
||||||
|
frame_address_register;
|
||||||
|
start_new_write = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spartan6 frames are 65-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;
|
||||||
|
|
||||||
|
current_frame_address = *next_address;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Configuration(part, frames);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ArchType>
|
template <typename ArchType>
|
||||||
template <typename Collection>
|
template <typename Collection>
|
||||||
absl::optional<Configuration<ArchType>>
|
absl::optional<Configuration<ArchType>>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,47 @@
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
// Spartan6 configuration register addresses
|
||||||
|
// according to UG380, pg. 100
|
||||||
|
enum class Spartan6ConfigurationRegister : unsigned int {
|
||||||
|
CRC = 0x00,
|
||||||
|
FAR = 0x01,
|
||||||
|
FAR_MAJ = 0x01,
|
||||||
|
FAR_MIN = 0x02,
|
||||||
|
FDRI = 0x03,
|
||||||
|
FDRO = 0x04,
|
||||||
|
CMD = 0x05,
|
||||||
|
CTL = 0x06,
|
||||||
|
CTL1 = 0x06,
|
||||||
|
MASK = 0x07,
|
||||||
|
STAT = 0x08,
|
||||||
|
LOUT = 0x09,
|
||||||
|
COR1 = 0x0a,
|
||||||
|
COR2 = 0x0b,
|
||||||
|
PWRDN_REG = 0x0c,
|
||||||
|
FLR = 0x0d,
|
||||||
|
IDCODE = 0x0e,
|
||||||
|
CWDT = 0x0f,
|
||||||
|
HC_OPT_REG = 0x10,
|
||||||
|
CSBO = 0x12,
|
||||||
|
GENERAL1 = 0x13,
|
||||||
|
GENERAL2 = 0x14,
|
||||||
|
GENERAL3 = 0x15,
|
||||||
|
GENERAL4 = 0x16,
|
||||||
|
GENERAL5 = 0x17,
|
||||||
|
MODE_REG = 0x18,
|
||||||
|
PU_GWE = 0x19,
|
||||||
|
PU_GTS = 0x1a,
|
||||||
|
MFWR = 0x1b,
|
||||||
|
CCLK_FREQ = 0x1c,
|
||||||
|
SEU_OPT = 0x1d,
|
||||||
|
EXP_SIGN = 0x1e,
|
||||||
|
RDBK_SIGN = 0x1f,
|
||||||
|
BOOTSTS = 0x20,
|
||||||
|
EYE_MASK = 0x21,
|
||||||
|
CBC_REG = 0x22,
|
||||||
|
};
|
||||||
|
|
||||||
// Series-7 configuration register addresses
|
// Series-7 configuration register addresses
|
||||||
// according to UG470, pg. 109
|
// according to UG470, pg. 109
|
||||||
enum class Series7ConfigurationRegister : unsigned int {
|
enum class Series7ConfigurationRegister : unsigned int {
|
||||||
|
|
@ -32,6 +73,8 @@ enum class Series7ConfigurationRegister : unsigned int {
|
||||||
BSPI = 0x1F,
|
BSPI = 0x1F,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o,
|
||||||
|
const Spartan6ConfigurationRegister& value);
|
||||||
std::ostream& operator<<(std::ostream& o,
|
std::ostream& operator<<(std::ostream& o,
|
||||||
const Series7ConfigurationRegister& value);
|
const Series7ConfigurationRegister& value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <absl/strings/str_split.h>
|
#include <absl/strings/str_split.h>
|
||||||
#include <prjxray/xilinx/architectures.h>
|
#include <prjxray/xilinx/architectures.h>
|
||||||
#include <prjxray/xilinx/xc7series/ecc.h>
|
|
||||||
|
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
@ -67,13 +66,18 @@ int Frames<ArchType>::readFrames(const std::string& frm_file_str) {
|
||||||
std::vector<std::string> frame_data_strings =
|
std::vector<std::string> frame_data_strings =
|
||||||
absl::StrSplit(frame_delta.second, ',');
|
absl::StrSplit(frame_delta.second, ',');
|
||||||
|
|
||||||
if (frame_data_strings.size() != ArchType::words_per_frame) {
|
// Spartan6's IOB frames can have different word count
|
||||||
std::cerr << "Frame " << std::hex << frame_address
|
if (!std::is_same<ArchType, Spartan6>::value) {
|
||||||
<< ": found " << std::dec
|
if (frame_data_strings.size() !=
|
||||||
<< frame_data_strings.size()
|
ArchType::words_per_frame) {
|
||||||
<< " words instead of "
|
std::cerr
|
||||||
<< ArchType::words_per_frame << std::endl;
|
<< "Frame " << std::hex << frame_address
|
||||||
continue;
|
<< ": found " << std::dec
|
||||||
|
<< frame_data_strings.size()
|
||||||
|
<< " words instead of "
|
||||||
|
<< ArchType::words_per_frame << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameData frame_data(frame_data_strings.size(), 0);
|
FrameData frame_data(frame_data_strings.size(), 0);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// According to UG380 pg. 97 there are 3 types of configuration frames:
|
||||||
|
// Type 0: Core: CLB, DSP, input/output interconnect (IOI), clocking
|
||||||
|
// Type 1: Block RAM
|
||||||
|
// Type 2: IOB
|
||||||
|
enum class BlockType : unsigned int {
|
||||||
|
CLB_IOI_CLK = 0x0,
|
||||||
|
BLOCK_RAM = 0x1,
|
||||||
|
IOB = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, BlockType value);
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::spartan6::BlockType> {
|
||||||
|
static Node encode(const prjxray::xilinx::spartan6::BlockType& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::BlockType& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// Command register map according to UG380 pg. 102
|
||||||
|
enum class Command : uint32_t {
|
||||||
|
NOP = 0x0,
|
||||||
|
WCFG = 0x1,
|
||||||
|
MFW = 0x2,
|
||||||
|
LFRM = 0x3,
|
||||||
|
RCFG = 0x4,
|
||||||
|
START = 0x5,
|
||||||
|
RCAP = 0x6,
|
||||||
|
RCRC = 0x7,
|
||||||
|
AGHIGH = 0x8,
|
||||||
|
SWITCH = 0x9,
|
||||||
|
GRESTORE = 0xA,
|
||||||
|
SHUTDOWN = 0xB,
|
||||||
|
GCAPTURE = 0xC,
|
||||||
|
DESYNC = 0xD,
|
||||||
|
IPROG = 0xF,
|
||||||
|
CRCC = 0x10,
|
||||||
|
LTIMER = 0x11,
|
||||||
|
BSPI_READ = 0x12,
|
||||||
|
FALL_EDGE = 0x13,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_column.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// ConfigurationBus represents a bus for sending frames to a specific BlockType
|
||||||
|
// within a Row. An instance of ConfigurationBus will contain one or more
|
||||||
|
// ConfigurationColumns.
|
||||||
|
class ConfigurationBus {
|
||||||
|
public:
|
||||||
|
ConfigurationBus() = default;
|
||||||
|
|
||||||
|
// Constructs a ConfigurationBus from iterators yielding
|
||||||
|
// FrameAddresses. The frame address need not be contiguous or sorted
|
||||||
|
// but they must all have the same block type, row half, and row
|
||||||
|
// address components.
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationBus(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the provided address falls into a valid segment of
|
||||||
|
// the address range on this bus. Only the column and minor components
|
||||||
|
// of the address are considered as all other components are outside
|
||||||
|
// the scope of a bus.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next valid address on the bus in numerically increasing
|
||||||
|
// order. If the next address would fall outside this bus, no object is
|
||||||
|
// returned.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct YAML::convert<ConfigurationBus>;
|
||||||
|
|
||||||
|
std::map<unsigned int, ConfigurationColumn> configuration_columns_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationBus::ConfigurationBus(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.block_type() == first->block_type() &&
|
||||||
|
addr.row() == first->row());
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.column() < rhs.column();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto col_first = first; col_first != last;) {
|
||||||
|
auto col_last = std::upper_bound(
|
||||||
|
col_first, last, col_first->column(),
|
||||||
|
[](const unsigned int& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.column();
|
||||||
|
});
|
||||||
|
|
||||||
|
configuration_columns_.emplace(
|
||||||
|
col_first->column(),
|
||||||
|
std::move(ConfigurationColumn(col_first, col_last)));
|
||||||
|
col_first = col_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::spartan6::ConfigurationBus> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::spartan6::ConfigurationBus& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::ConfigurationBus& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// ConfigurationColumn represents an endpoint on a ConfigurationBus.
|
||||||
|
class ConfigurationColumn {
|
||||||
|
public:
|
||||||
|
ConfigurationColumn() = default;
|
||||||
|
ConfigurationColumn(unsigned int frame_count)
|
||||||
|
: frame_count_(frame_count) {}
|
||||||
|
|
||||||
|
// Returns a ConfigurationColumn that describes a continguous range of
|
||||||
|
// minor addresses that encompasses the given
|
||||||
|
// FrameAddresses. The provided addresses must only
|
||||||
|
// differ only by their minor addresses.
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationColumn(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the minor field of the address is within the valid
|
||||||
|
// range of this column.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next address in numerical order. If the next address
|
||||||
|
// would be outside this column, return no object.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct YAML::convert<ConfigurationColumn>;
|
||||||
|
|
||||||
|
unsigned int frame_count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationColumn::ConfigurationColumn(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.block_type() == first->block_type() &&
|
||||||
|
addr.row() == first->row() &&
|
||||||
|
addr.column() == first->column());
|
||||||
|
}));
|
||||||
|
|
||||||
|
auto max_minor = std::max_element(
|
||||||
|
first, last, [](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.minor() < rhs.minor();
|
||||||
|
});
|
||||||
|
|
||||||
|
frame_count_ = max_minor->minor() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::spartan6::ConfigurationColumn> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::spartan6::ConfigurationColumn& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::ConfigurationColumn& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/block_type.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_bus.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
class Row {
|
||||||
|
public:
|
||||||
|
Row() = default;
|
||||||
|
|
||||||
|
// Construct a row from a range of iterators that yield FrameAddresses.
|
||||||
|
// The addresses may be noncontinguous and/or unsorted but all must
|
||||||
|
// share the same row half and row components.
|
||||||
|
template <typename T>
|
||||||
|
Row(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the provided address falls within a valid range
|
||||||
|
// attributed to this row. Only the block type, column, and minor
|
||||||
|
// address components are considerd as the remaining components are
|
||||||
|
// outside the scope of a row.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next numerically increasing address within the Row. If
|
||||||
|
// the next address would fall outside the Row, no object is returned.
|
||||||
|
// If the next address would cross from one block type to another, no
|
||||||
|
// object is returned as other rows of the same block type come before
|
||||||
|
// other block types numerically.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct YAML::convert<Row>;
|
||||||
|
|
||||||
|
std::map<BlockType, ConfigurationBus> configuration_buses_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Row::Row(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows() &&
|
||||||
|
addr.row() == first->row());
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.block_type() < rhs.block_type();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto bus_first = first; bus_first != last;) {
|
||||||
|
auto bus_last = std::upper_bound(
|
||||||
|
bus_first, last, bus_first->block_type(),
|
||||||
|
[](const BlockType& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.block_type();
|
||||||
|
});
|
||||||
|
|
||||||
|
configuration_buses_.emplace(
|
||||||
|
bus_first->block_type(),
|
||||||
|
std::move(ConfigurationBus(bus_first, bus_last)));
|
||||||
|
bus_first = bus_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::spartan6::Row> {
|
||||||
|
static Node encode(const prjxray::xilinx::spartan6::Row& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::Row& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/spartan6/block_type.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
#ifdef _GNU_SOURCE
|
||||||
|
#undef minor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
class FrameAddress {
|
||||||
|
public:
|
||||||
|
FrameAddress() : address_(0) {}
|
||||||
|
|
||||||
|
FrameAddress(uint32_t address) : address_(address){};
|
||||||
|
|
||||||
|
FrameAddress(BlockType block_type,
|
||||||
|
uint8_t row,
|
||||||
|
uint8_t column,
|
||||||
|
uint16_t minor);
|
||||||
|
|
||||||
|
operator uint32_t() const { return address_; }
|
||||||
|
bool is_bottom_half_rows() const;
|
||||||
|
BlockType block_type() const;
|
||||||
|
uint8_t row() const;
|
||||||
|
uint8_t column() const;
|
||||||
|
uint16_t minor() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t address_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, const FrameAddress& addr);
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct convert<spartan6::FrameAddress> {
|
||||||
|
static Node encode(const spartan6::FrameAddress& rhs);
|
||||||
|
static bool decode(const Node& node, spartan6::FrameAddress& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_row.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// GlobalClockRegion represents all the resources associated with a single
|
||||||
|
// global clock buffer (BUFG) tile. In 7-Series FPGAs, there are two BUFG
|
||||||
|
// tiles that divide the chip into top and bottom "halves". Each half may
|
||||||
|
// contains any number of rows, buses, and columns.
|
||||||
|
class GlobalClockRegion {
|
||||||
|
public:
|
||||||
|
GlobalClockRegion() = default;
|
||||||
|
|
||||||
|
// Construct a GlobalClockRegion from iterators that yield
|
||||||
|
// FrameAddresses which are known to be valid. The addresses may be
|
||||||
|
// noncontinguous and/or unordered but they must share the same row
|
||||||
|
// half address component.
|
||||||
|
template <typename T>
|
||||||
|
GlobalClockRegion(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the address falls within a valid range inside the
|
||||||
|
// global clock region. The row half address component is ignored as it
|
||||||
|
// is outside the context of a global clock region.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next numerically increasing address known within this
|
||||||
|
// global clock region. If the next address would fall outside this
|
||||||
|
// global clock region, no address is returned. If the next address
|
||||||
|
// would jump to a different block type, no address is returned as the
|
||||||
|
// same block type in other global clock regions come numerically
|
||||||
|
// before other block types.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct YAML::convert<GlobalClockRegion>;
|
||||||
|
|
||||||
|
std::map<unsigned int, Row> rows_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
GlobalClockRegion::GlobalClockRegion(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows();
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.row() < rhs.row();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto row_first = first; row_first != last;) {
|
||||||
|
auto row_last = std::upper_bound(
|
||||||
|
row_first, last, row_first->row(),
|
||||||
|
[](const uint8_t& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.row();
|
||||||
|
});
|
||||||
|
|
||||||
|
rows_.emplace(row_first->row(),
|
||||||
|
std::move(Row(row_first, row_last)));
|
||||||
|
row_first = row_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::spartan6::GlobalClockRegion> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::spartan6::GlobalClockRegion& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::GlobalClockRegion& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/global_clock_region.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
class Part {
|
||||||
|
public:
|
||||||
|
constexpr static uint32_t kInvalidIdcode = 0;
|
||||||
|
|
||||||
|
static absl::optional<Part> FromFile(const std::string& path);
|
||||||
|
|
||||||
|
// Constructs an invalid part with a zero IDCODE. Required for YAML
|
||||||
|
// conversion but shouldn't be used otherwise.
|
||||||
|
Part() : idcode_(kInvalidIdcode) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Part(uint32_t idcode, T collection)
|
||||||
|
: Part(idcode, std::begin(collection), std::end(collection)) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Part(uint32_t idcode, T first, T last);
|
||||||
|
|
||||||
|
uint32_t idcode() const { return idcode_; }
|
||||||
|
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct YAML::convert<Part>;
|
||||||
|
|
||||||
|
uint32_t idcode_;
|
||||||
|
spartan6::GlobalClockRegion top_region_;
|
||||||
|
spartan6::GlobalClockRegion bottom_region_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
|
||||||
|
top_region_ = spartan6::GlobalClockRegion(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct convert<spartan6::Part> {
|
||||||
|
static Node encode(const spartan6::Part& rhs);
|
||||||
|
static bool decode(const Node& node, spartan6::Part& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
|
||||||
|
|
@ -7,6 +7,12 @@
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
// Per UG380 pg 78: Bus Width Auto Detection
|
||||||
|
typename BitstreamWriter<Spartan6>::header_t BitstreamWriter<Spartan6>::header_{
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566};
|
||||||
|
|
||||||
// Per UG470 pg 80: Bus Width Auto Detection
|
// Per UG470 pg 80: Bus Width Auto Detection
|
||||||
template <>
|
template <>
|
||||||
typename BitstreamWriter<Series7>::header_t BitstreamWriter<Series7>::header_{
|
typename BitstreamWriter<Series7>::header_t BitstreamWriter<Series7>::header_{
|
||||||
|
|
@ -22,6 +28,37 @@ typename BitstreamWriter<UltraScalePlus>::header_t
|
||||||
BitstreamWriter<UltraScalePlus>::header_{
|
BitstreamWriter<UltraScalePlus>::header_{
|
||||||
0xFFFFFFFF, 0x000000BB, 0x11220044, 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
|
0xFFFFFFFF, 0x000000BB, 0x11220044, 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
|
||||||
|
|
||||||
|
uint32_t packet2header(
|
||||||
|
const ConfigurationPacket<Spartan6ConfigurationRegister>& packet) {
|
||||||
|
uint32_t ret = 0;
|
||||||
|
|
||||||
|
ret = bit_field_set(ret, 15, 13, packet.header_type());
|
||||||
|
|
||||||
|
switch (packet.header_type()) {
|
||||||
|
case NONE:
|
||||||
|
// Bitstreams are 0 padded sometimes, essentially making
|
||||||
|
// a type 0 frame Ignore the other fields for now
|
||||||
|
break;
|
||||||
|
case TYPE1: {
|
||||||
|
// Table 5-20: Type 1 Packet Header Format
|
||||||
|
ret = bit_field_set(ret, 12, 11, packet.opcode());
|
||||||
|
ret = bit_field_set(ret, 10, 5, packet.address());
|
||||||
|
ret = bit_field_set(ret, 4, 0, packet.data().length());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE2: {
|
||||||
|
// Table 5-22: Type 2 Packet Header
|
||||||
|
ret = bit_field_set(ret, 12, 11, packet.opcode());
|
||||||
|
ret = bit_field_set(ret, 10, 5, packet.address());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t packet2header(
|
uint32_t packet2header(
|
||||||
const ConfigurationPacket<Series7ConfigurationRegister>& packet) {
|
const ConfigurationPacket<Series7ConfigurationRegister>& packet) {
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,285 @@
|
||||||
#include <prjxray/xilinx/architectures.h>
|
#include <prjxray/xilinx/architectures.h>
|
||||||
#include <prjxray/xilinx/bitstream_writer.h>
|
#include <prjxray/xilinx/bitstream_writer.h>
|
||||||
#include <prjxray/xilinx/configuration.h>
|
#include <prjxray/xilinx/configuration.h>
|
||||||
#include <prjxray/xilinx/configuration_packet.h>
|
|
||||||
#include <prjxray/xilinx/configuration_packet_with_payload.h>
|
#include <prjxray/xilinx/configuration_packet_with_payload.h>
|
||||||
#include <prjxray/xilinx/nop_packet.h>
|
#include <prjxray/xilinx/nop_packet.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/command.h>
|
||||||
#include <prjxray/xilinx/xc7series/command.h>
|
#include <prjxray/xilinx/xc7series/command.h>
|
||||||
#include <prjxray/xilinx/xc7series/configuration_options_0_value.h>
|
#include <prjxray/xilinx/xc7series/configuration_options_0_value.h>
|
||||||
|
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Configuration<Spartan6>::PacketData
|
||||||
|
Configuration<Spartan6>::createType2ConfigurationPacketData(
|
||||||
|
const Frames<Spartan6>::Frames2Data& frames,
|
||||||
|
absl::optional<Spartan6::Part>& part) {
|
||||||
|
// Generate a single type 2 packet that writes everything at once.
|
||||||
|
PacketData packet_data;
|
||||||
|
for (auto& frame : frames) {
|
||||||
|
std::copy(frame.second.begin(), frame.second.end(),
|
||||||
|
std::back_inserter(packet_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert payload length
|
||||||
|
size_t packet_data_size = packet_data.size() - 2;
|
||||||
|
packet_data.insert(packet_data.begin(), packet_data_size & 0xFFFF);
|
||||||
|
packet_data.insert(packet_data.begin(),
|
||||||
|
(packet_data_size >> 16) & 0xFFFF);
|
||||||
|
return packet_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void Configuration<Spartan6>::createConfigurationPackage(
|
||||||
|
Spartan6::ConfigurationPackage& out_packets,
|
||||||
|
const PacketData& packet_data,
|
||||||
|
absl::optional<Spartan6::Part>& part) {
|
||||||
|
using ArchType = Spartan6;
|
||||||
|
using ConfigurationRegister = ArchType::ConfRegType;
|
||||||
|
// Initialization sequence
|
||||||
|
//
|
||||||
|
// Reset CRC
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::RCRC)}));
|
||||||
|
|
||||||
|
// NOP
|
||||||
|
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
|
||||||
|
|
||||||
|
// Frame length
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::FLR, {0x0380}));
|
||||||
|
|
||||||
|
// Configuration Options 1
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::COR1, {0x3d08}));
|
||||||
|
|
||||||
|
// Configurations Options2
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::COR2, {0x9ee}));
|
||||||
|
|
||||||
|
// IDCODE
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::IDCODE,
|
||||||
|
{part->idcode() >> 16, part->idcode()}));
|
||||||
|
|
||||||
|
// Control MASK
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::MASK, {0xcf}));
|
||||||
|
|
||||||
|
// Control options
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CTL, {0x81}));
|
||||||
|
|
||||||
|
// NOP packets
|
||||||
|
for (int i = 0; i < 17; i++) {
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new NopPacket<ConfigurationRegister>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// CCLK FREQ
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CCLK_FREQ, {0x3cc8}));
|
||||||
|
|
||||||
|
// PWRDN_REG
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::PWRDN_REG, {0x881}));
|
||||||
|
|
||||||
|
// EYE MASK
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::EYE_MASK, {0x0}));
|
||||||
|
|
||||||
|
// House Clean Option
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::HC_OPT_REG, {0x1f}));
|
||||||
|
|
||||||
|
// Configuration Watchdog Timer
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CWDT, {0xffff}));
|
||||||
|
|
||||||
|
// GWE cycle during wake-up from suspend
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::PU_GWE, {0x5}));
|
||||||
|
|
||||||
|
// GTS cycle during wake-up from suspend
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::PU_GTS, {0x4}));
|
||||||
|
|
||||||
|
// Reboot mode
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::MODE_REG, {0x100}));
|
||||||
|
|
||||||
|
// General options 1
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::GENERAL1, {0x0}));
|
||||||
|
|
||||||
|
// General options 2
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::GENERAL2, {0x0}));
|
||||||
|
|
||||||
|
// General options 3
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::GENERAL3, {0x0}));
|
||||||
|
|
||||||
|
// General options 4
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::GENERAL4, {0x0}));
|
||||||
|
|
||||||
|
// General options 5
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::GENERAL5, {0x0}));
|
||||||
|
|
||||||
|
// SEU frequency, enable and status
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::SEU_OPT, {0x1be2}));
|
||||||
|
|
||||||
|
// Expected readback signature for SEU detection
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::EXP_SIGN, {0x0, 0x0}));
|
||||||
|
|
||||||
|
// NOP
|
||||||
|
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
|
||||||
|
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
|
||||||
|
|
||||||
|
// FAR
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::FAR_MAJ, {0x0, 0x0}));
|
||||||
|
|
||||||
|
// Write Configuration Data
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::WCFG)}));
|
||||||
|
|
||||||
|
// Frame data write
|
||||||
|
out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
|
||||||
|
TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::FDRI, {packet_data}));
|
||||||
|
|
||||||
|
// NOP packets
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new NopPacket<ConfigurationRegister>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalization sequence
|
||||||
|
//
|
||||||
|
// Set/reset the IOB and CLB flip-flops
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::GRESTORE)}));
|
||||||
|
|
||||||
|
// Last Frame
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::LFRM)}));
|
||||||
|
|
||||||
|
// NOP packets
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new NopPacket<ConfigurationRegister>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set/reset the IOB and CLB flip-flops
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::GRESTORE)}));
|
||||||
|
|
||||||
|
// Startup sequence
|
||||||
|
//
|
||||||
|
// Start
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::START)}));
|
||||||
|
|
||||||
|
// Control MASK
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::MASK, {0xff}));
|
||||||
|
|
||||||
|
// Control options
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CTL, {0x81}));
|
||||||
|
|
||||||
|
// CRC
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CRC, {0x39, 0xe423}));
|
||||||
|
|
||||||
|
// Desync
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
|
||||||
|
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
|
||||||
|
ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(spartan6::Command::DESYNC)}));
|
||||||
|
|
||||||
|
// NOP packets
|
||||||
|
for (int i = 0; i < 14; i++) {
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new NopPacket<ConfigurationRegister>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Configuration<Series7>::createConfigurationPackage(
|
void Configuration<Series7>::createConfigurationPackage(
|
||||||
Series7::ConfigurationPackage& out_packets,
|
Series7::ConfigurationPackage& out_packets,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,83 @@
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::pair<absl::Span<uint32_t>,
|
||||||
|
absl::optional<ConfigurationPacket<Spartan6ConfigurationRegister>>>
|
||||||
|
ConfigurationPacket<Spartan6ConfigurationRegister>::InitWithWords(
|
||||||
|
absl::Span<uint32_t> words,
|
||||||
|
const ConfigurationPacket<Spartan6ConfigurationRegister>* previous_packet) {
|
||||||
|
using ConfigurationRegister = Spartan6ConfigurationRegister;
|
||||||
|
// 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], 15, 13);
|
||||||
|
switch (header_type) {
|
||||||
|
case NONE:
|
||||||
|
// Type 0 is emitted at the end of a configuration row
|
||||||
|
// when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES.
|
||||||
|
// These seem to be padding that are interepreted as
|
||||||
|
// NOPs. Since Type 0 packets don't exist according to
|
||||||
|
// UG470 and they seem to be zero-filled, just consume
|
||||||
|
// the bytes without generating a packet.
|
||||||
|
return {words.subspan(1),
|
||||||
|
{{header_type,
|
||||||
|
Opcode::NOP,
|
||||||
|
ConfigurationRegister::CRC,
|
||||||
|
{}}}};
|
||||||
|
case TYPE1: {
|
||||||
|
Opcode opcode = static_cast<Opcode>(
|
||||||
|
bit_field_get(words[0], 12, 11));
|
||||||
|
ConfigurationRegister address =
|
||||||
|
static_cast<ConfigurationRegister>(
|
||||||
|
bit_field_get(words[0], 10, 5));
|
||||||
|
uint32_t data_word_count =
|
||||||
|
bit_field_get(words[0], 4, 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),
|
||||||
|
{{header_type, opcode, address,
|
||||||
|
words.subspan(1, data_word_count)}}};
|
||||||
|
}
|
||||||
|
case TYPE2: {
|
||||||
|
absl::optional<ConfigurationPacket> packet;
|
||||||
|
Opcode opcode = static_cast<Opcode>(
|
||||||
|
bit_field_get(words[0], 12, 11));
|
||||||
|
ConfigurationRegister address =
|
||||||
|
static_cast<ConfigurationRegister>(
|
||||||
|
bit_field_get(words[0], 10, 5));
|
||||||
|
// Type 2 packets according to UG380 consist of
|
||||||
|
// a header word followed by 2 WCD (Word Count Data)
|
||||||
|
// words
|
||||||
|
uint32_t data_word_count = (words[1] << 16) | words[2];
|
||||||
|
|
||||||
|
// 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, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a packet that contains as many data words
|
||||||
|
// as specified in the WCD packets, but omit them
|
||||||
|
// in the configuration packet along with the header
|
||||||
|
// FIXME Figure out why we need the extra 2 words
|
||||||
|
packet = ConfigurationPacket(
|
||||||
|
header_type, opcode, address,
|
||||||
|
words.subspan(3, data_word_count + 2));
|
||||||
|
|
||||||
|
return {words.subspan(data_word_count + 3), packet};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return {{}, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::pair<absl::Span<uint32_t>,
|
std::pair<absl::Span<uint32_t>,
|
||||||
absl::optional<ConfigurationPacket<Series7ConfigurationRegister>>>
|
absl::optional<ConfigurationPacket<Series7ConfigurationRegister>>>
|
||||||
|
|
@ -133,6 +210,9 @@ std::ostream& operator<<(std::ostream& o,
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template std::ostream& operator<<(
|
||||||
|
std::ostream&,
|
||||||
|
const ConfigurationPacket<Spartan6ConfigurationRegister>&);
|
||||||
template std::ostream& operator<<(
|
template std::ostream& operator<<(
|
||||||
std::ostream&,
|
std::ostream&,
|
||||||
const ConfigurationPacket<Series7ConfigurationRegister>&);
|
const ConfigurationPacket<Series7ConfigurationRegister>&);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,88 @@
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o,
|
||||||
|
const Spartan6ConfigurationRegister& value) {
|
||||||
|
switch (value) {
|
||||||
|
case Spartan6ConfigurationRegister::CRC:
|
||||||
|
return o << "CRC";
|
||||||
|
case Spartan6ConfigurationRegister::FAR_MAJ:
|
||||||
|
return o << "Frame Address Register Block and Major";
|
||||||
|
case Spartan6ConfigurationRegister::FAR_MIN:
|
||||||
|
return o << "Frame Address Register Minor";
|
||||||
|
case Spartan6ConfigurationRegister::FDRI:
|
||||||
|
return o << "Frame Data Input";
|
||||||
|
case Spartan6ConfigurationRegister::FDRO:
|
||||||
|
return o << "Frame Data Output";
|
||||||
|
case Spartan6ConfigurationRegister::CMD:
|
||||||
|
return o << "Command";
|
||||||
|
case Spartan6ConfigurationRegister::CTL:
|
||||||
|
return o << "Control";
|
||||||
|
case Spartan6ConfigurationRegister::MASK:
|
||||||
|
return o << "Control Mask";
|
||||||
|
case Spartan6ConfigurationRegister::STAT:
|
||||||
|
return o << "Status";
|
||||||
|
case Spartan6ConfigurationRegister::LOUT:
|
||||||
|
return o << "Legacy Output";
|
||||||
|
case Spartan6ConfigurationRegister::COR1:
|
||||||
|
return o << "Configuration Option 1";
|
||||||
|
case Spartan6ConfigurationRegister::COR2:
|
||||||
|
return o << "Configuration Option 2";
|
||||||
|
case Spartan6ConfigurationRegister::PWRDN_REG:
|
||||||
|
return o << "Power-down Option register";
|
||||||
|
case Spartan6ConfigurationRegister::FLR:
|
||||||
|
return o << "Frame Length register";
|
||||||
|
case Spartan6ConfigurationRegister::IDCODE:
|
||||||
|
return o << "Device ID";
|
||||||
|
case Spartan6ConfigurationRegister::CWDT:
|
||||||
|
return o << "Watchdog Timer";
|
||||||
|
case Spartan6ConfigurationRegister::HC_OPT_REG:
|
||||||
|
return o << "House Clean Option register";
|
||||||
|
case Spartan6ConfigurationRegister::CSBO:
|
||||||
|
return o << "CSB output for parallel daisy-chaining";
|
||||||
|
case Spartan6ConfigurationRegister::GENERAL1:
|
||||||
|
return o << "Power-up self test or loadable program "
|
||||||
|
"address";
|
||||||
|
case Spartan6ConfigurationRegister::GENERAL2:
|
||||||
|
return o << "Power-up self test or loadable program "
|
||||||
|
<< "address and new SPI opcode";
|
||||||
|
case Spartan6ConfigurationRegister::GENERAL3:
|
||||||
|
return o << "Golden bitstream address";
|
||||||
|
case Spartan6ConfigurationRegister::GENERAL4:
|
||||||
|
return o
|
||||||
|
<< "Golden bitstream address and new SPI opcode";
|
||||||
|
case Spartan6ConfigurationRegister::GENERAL5:
|
||||||
|
return o
|
||||||
|
<< "User-defined register for fail-safe scheme";
|
||||||
|
case Spartan6ConfigurationRegister::MODE_REG:
|
||||||
|
return o << "Reboot mode";
|
||||||
|
case Spartan6ConfigurationRegister::PU_GWE:
|
||||||
|
return o << "GWE cycle during wake-up from suspend";
|
||||||
|
case Spartan6ConfigurationRegister::PU_GTS:
|
||||||
|
return o << "GTS cycle during wake-up from suspend";
|
||||||
|
case Spartan6ConfigurationRegister::MFWR:
|
||||||
|
return o << "Multi-frame write register";
|
||||||
|
case Spartan6ConfigurationRegister::CCLK_FREQ:
|
||||||
|
return o << "CCLK frequency for master mode";
|
||||||
|
case Spartan6ConfigurationRegister::SEU_OPT:
|
||||||
|
return o << "SEU frequency, enable and status";
|
||||||
|
case Spartan6ConfigurationRegister::EXP_SIGN:
|
||||||
|
return o << "Expected readback signature for SEU "
|
||||||
|
"detection";
|
||||||
|
case Spartan6ConfigurationRegister::RDBK_SIGN:
|
||||||
|
return o << "Readback signature for readback command "
|
||||||
|
"and SEU";
|
||||||
|
case Spartan6ConfigurationRegister::BOOTSTS:
|
||||||
|
return o << "Boot History Register";
|
||||||
|
case Spartan6ConfigurationRegister::EYE_MASK:
|
||||||
|
return o << "Mask pins for Multi-Pin Wake-Up";
|
||||||
|
case Spartan6ConfigurationRegister::CBC_REG:
|
||||||
|
return o << "Initial CBC Value Register";
|
||||||
|
default:
|
||||||
|
return o << "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& o,
|
std::ostream& operator<<(std::ostream& o,
|
||||||
const Series7ConfigurationRegister& value) {
|
const Series7ConfigurationRegister& value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,9 @@ void Frames<UltraScalePlus>::updateECC(
|
||||||
xc7series::updateECC(data);
|
xc7series::updateECC(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spartan6 doesn't have ECC
|
||||||
|
template <>
|
||||||
|
void Frames<Spartan6>::updateECC(typename Frames<Spartan6>::FrameData& data) {}
|
||||||
|
|
||||||
} // namespace xilinx
|
} // namespace xilinx
|
||||||
} // namespace prjxray
|
} // namespace prjxray
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/block_type.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, BlockType value) {
|
||||||
|
switch (value) {
|
||||||
|
case BlockType::CLB_IOI_CLK:
|
||||||
|
o << "CLB/IOI/CLK";
|
||||||
|
break;
|
||||||
|
case BlockType::BLOCK_RAM:
|
||||||
|
o << "Block RAM";
|
||||||
|
break;
|
||||||
|
case BlockType::IOB:
|
||||||
|
o << "Config CLB";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<prjxray::xilinx::spartan6::BlockType>::encode(
|
||||||
|
const prjxray::xilinx::spartan6::BlockType& rhs) {
|
||||||
|
switch (rhs) {
|
||||||
|
case prjxray::xilinx::spartan6::BlockType::CLB_IOI_CLK:
|
||||||
|
return Node("CLB_IOI_CLK");
|
||||||
|
case prjxray::xilinx::spartan6::BlockType::BLOCK_RAM:
|
||||||
|
return Node("BLOCK_RAM");
|
||||||
|
case prjxray::xilinx::spartan6::BlockType::IOB:
|
||||||
|
return Node("IOB");
|
||||||
|
default:
|
||||||
|
return Node(static_cast<unsigned int>(rhs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool YAML::convert<prjxray::xilinx::spartan6::BlockType>::decode(
|
||||||
|
const Node& node,
|
||||||
|
prjxray::xilinx::spartan6::BlockType& lhs) {
|
||||||
|
auto type_str = node.as<std::string>();
|
||||||
|
|
||||||
|
if (type_str == "CLB_IOI_CLK") {
|
||||||
|
lhs = prjxray::xilinx::spartan6::BlockType::CLB_IOI_CLK;
|
||||||
|
return true;
|
||||||
|
} else if (type_str == "BLOCK_RAM") {
|
||||||
|
lhs = prjxray::xilinx::spartan6::BlockType::BLOCK_RAM;
|
||||||
|
return true;
|
||||||
|
} else if (type_str == "IOB") {
|
||||||
|
lhs = prjxray::xilinx::spartan6::BlockType::IOB;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_bus.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
bool ConfigurationBus::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_column = configuration_columns_.find(address.column());
|
||||||
|
if (addr_column == configuration_columns_.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return addr_column->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> ConfigurationBus::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the column for the current address.
|
||||||
|
auto addr_column = configuration_columns_.find(address.column());
|
||||||
|
|
||||||
|
// If the current address isn't in a known column, no way to know the
|
||||||
|
// next address.
|
||||||
|
if (addr_column == configuration_columns_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the column for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_column->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current column doesn't know what the next address is. Assume
|
||||||
|
// that the next valid address is the beginning of the next column.
|
||||||
|
if (++addr_column != configuration_columns_.end()) {
|
||||||
|
auto next_address = FrameAddress(
|
||||||
|
address.block_type(), address.row(), addr_column->first, 0);
|
||||||
|
if (addr_column->second.IsValidFrameAddress(next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in this bus.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<spartan6::ConfigurationBus>::encode(
|
||||||
|
const spartan6::ConfigurationBus& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/configuration_bus");
|
||||||
|
node["configuration_columns"] = rhs.configuration_columns_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::ConfigurationBus>::decode(
|
||||||
|
const Node& node,
|
||||||
|
spartan6::ConfigurationBus& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/spartan6/configuration_bus") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.configuration_columns_ =
|
||||||
|
node["configuration_columns"]
|
||||||
|
.as<std::map<unsigned int, spartan6::ConfigurationColumn>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_column.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
bool ConfigurationColumn::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
return address.minor() < frame_count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> ConfigurationColumn::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
if (!IsValidFrameAddress(address))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(address.minor() + 1) < frame_count_) {
|
||||||
|
return address + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next address is not in this column.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<spartan6::ConfigurationColumn>::encode(
|
||||||
|
const spartan6::ConfigurationColumn& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/configuration_column");
|
||||||
|
node["frame_count"] = rhs.frame_count_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::ConfigurationColumn>::decode(
|
||||||
|
const Node& node,
|
||||||
|
spartan6::ConfigurationColumn& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/spartan6/configuration_column") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.frame_count_ = node["frame_count"].as<unsigned int>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_row.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
bool Row::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_bus = configuration_buses_.find(address.block_type());
|
||||||
|
if (addr_bus == configuration_buses_.end())
|
||||||
|
return false;
|
||||||
|
return addr_bus->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> Row::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the bus for the current address.
|
||||||
|
auto addr_bus = configuration_buses_.find(address.block_type());
|
||||||
|
|
||||||
|
// If the current address isn't in a known bus, no way to know the next.
|
||||||
|
if (addr_bus == configuration_buses_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the bus for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_bus->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current bus doesn't know what the next address is. Rows come next
|
||||||
|
// in frame address numerical order so punt back to the caller to figure
|
||||||
|
// it out.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<spartan6::Row>::encode(const spartan6::Row& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/row");
|
||||||
|
node["configuration_buses"] = rhs.configuration_buses_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::Row>::decode(const Node& node, spartan6::Row& lhs) {
|
||||||
|
if (!node.Tag().empty() && node.Tag() != "xilinx/spartan6/row") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.configuration_buses_ =
|
||||||
|
node["configuration_buses"]
|
||||||
|
.as<std::map<spartan6::BlockType,
|
||||||
|
spartan6::ConfigurationBus>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <prjxray/bit_ops.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
// According to UG380 pg. 101 the Frame Address Register (FAR)
|
||||||
|
// consists of two 16-bit registers (FAR MAJOR and FAR MINOR).
|
||||||
|
// We construct the 32-bit frame address from these two.
|
||||||
|
FrameAddress::FrameAddress(spartan6::BlockType block_type,
|
||||||
|
uint8_t row,
|
||||||
|
uint8_t column,
|
||||||
|
uint16_t minor) {
|
||||||
|
address_ = bit_field_set(0, 31, 28, block_type);
|
||||||
|
address_ =
|
||||||
|
bit_field_set(address_, 27, 24, row); // high register, bit 8-11
|
||||||
|
address_ =
|
||||||
|
bit_field_set(address_, 23, 16, column); // high register, bits 0-7
|
||||||
|
address_ =
|
||||||
|
bit_field_set(address_, 9, 0, minor); // low register, bit 0-9
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FrameAddress::is_bottom_half_rows() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spartan6::BlockType FrameAddress::block_type() const {
|
||||||
|
return static_cast<typename spartan6::BlockType>(
|
||||||
|
bit_field_get(address_, 31, 28));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FrameAddress::row() const {
|
||||||
|
return bit_field_get(address_, 27, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FrameAddress::column() const {
|
||||||
|
return bit_field_get(address_, 23, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t FrameAddress::minor() const {
|
||||||
|
return bit_field_get(address_, 9, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, const FrameAddress& addr) {
|
||||||
|
o << "[" << std::hex << std::showbase << std::setw(10)
|
||||||
|
<< static_cast<uint32_t>(addr) << "] "
|
||||||
|
<< " Row=" << std::setw(2) << std::dec
|
||||||
|
<< static_cast<unsigned int>(addr.row()) << "Column =" << std::setw(2)
|
||||||
|
<< std::dec << addr.column() << " Minor=" << std::setw(2) << std::dec
|
||||||
|
<< static_cast<unsigned int>(addr.minor())
|
||||||
|
<< " Type=" << addr.block_type();
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
Node convert<spartan6::FrameAddress>::encode(
|
||||||
|
const spartan6::FrameAddress& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/frame_address");
|
||||||
|
node["block_type"] = rhs.block_type();
|
||||||
|
node["row"] = static_cast<unsigned int>(rhs.row());
|
||||||
|
node["column"] = static_cast<unsigned int>(rhs.column());
|
||||||
|
node["minor"] = static_cast<unsigned int>(rhs.minor());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::FrameAddress>::decode(const Node& node,
|
||||||
|
spartan6::FrameAddress& lhs) {
|
||||||
|
if (!(node.Tag() == "xilinx/spartan6/frame_address" ||
|
||||||
|
node.Tag() == "xilinx/spartan6/configuration_frame_address") ||
|
||||||
|
!node["block_type"] || !node["row"] || !node["column"] ||
|
||||||
|
!node["minor"])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lhs = spartan6::FrameAddress(
|
||||||
|
node["block_type"].as<spartan6::BlockType>(),
|
||||||
|
node["row"].as<unsigned int>(), node["column"].as<unsigned int>(),
|
||||||
|
node["minor"].as<unsigned int>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/global_clock_region.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
bool GlobalClockRegion::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_row = rows_.find(address.row());
|
||||||
|
if (addr_row == rows_.end())
|
||||||
|
return false;
|
||||||
|
return addr_row->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> GlobalClockRegion::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the row for the current address.
|
||||||
|
auto addr_row = rows_.find(address.row());
|
||||||
|
|
||||||
|
// If the current address isn't in a known row, no way to know the next.
|
||||||
|
if (addr_row == rows_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the row for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_row->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current row doesn't know what the next address is. Assume that
|
||||||
|
// the next valid address is the beginning of the next row.
|
||||||
|
if (++addr_row != rows_.end()) {
|
||||||
|
auto next_address =
|
||||||
|
FrameAddress(address.block_type(), addr_row->first, 0, 0);
|
||||||
|
if (addr_row->second.IsValidFrameAddress(next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be in a different global clock region.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<spartan6::GlobalClockRegion>::encode(
|
||||||
|
const spartan6::GlobalClockRegion& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/global_clock_region");
|
||||||
|
node["rows"] = rhs.rows_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::GlobalClockRegion>::decode(
|
||||||
|
const Node& node,
|
||||||
|
spartan6::GlobalClockRegion& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/spartan6/global_clock_region") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.rows_ = node["rows"].as<std::map<unsigned int, spartan6::Row>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/part.h>
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace spartan6 {
|
||||||
|
|
||||||
|
absl::optional<Part> Part::FromFile(const std::string& path) {
|
||||||
|
try {
|
||||||
|
YAML::Node yaml = YAML::LoadFile(path);
|
||||||
|
return yaml.as<Part>();
|
||||||
|
} catch (YAML::Exception& e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Part::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
if (address.is_bottom_half_rows()) {
|
||||||
|
return bottom_region_.IsValidFrameAddress(address);
|
||||||
|
} else {
|
||||||
|
return top_region_.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> Part::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Ask the current global clock region first.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
(address.is_bottom_half_rows()
|
||||||
|
? bottom_region_.GetNextFrameAddress(address)
|
||||||
|
: top_region_.GetNextFrameAddress(address));
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// If the current address is in the top region, the bottom region is
|
||||||
|
// next numerically.
|
||||||
|
if (!address.is_bottom_half_rows()) {
|
||||||
|
next_address = FrameAddress(address.block_type(), 0, 0, 0);
|
||||||
|
if (bottom_region_.IsValidFrameAddress(*next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block types are next numerically.
|
||||||
|
if (address.block_type() < spartan6::BlockType::BLOCK_RAM) {
|
||||||
|
next_address =
|
||||||
|
FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0);
|
||||||
|
if (IsValidFrameAddress(*next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.block_type() < spartan6::BlockType::IOB) {
|
||||||
|
next_address = FrameAddress(spartan6::BlockType::IOB, 0, 0, 0);
|
||||||
|
if (IsValidFrameAddress(*next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spartan6
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace spartan6 = prjxray::xilinx::spartan6;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<spartan6::Part>::encode(const spartan6::Part& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/part");
|
||||||
|
|
||||||
|
std::ostringstream idcode_str;
|
||||||
|
idcode_str << "0x" << std::hex << rhs.idcode_;
|
||||||
|
node["idcode"] = idcode_str.str();
|
||||||
|
node["global_clock_regions"]["top"] = rhs.top_region_;
|
||||||
|
node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<spartan6::Part>::decode(const Node& node, spartan6::Part& lhs) {
|
||||||
|
if (!node.Tag().empty() && node.Tag() != "xilinx/spartan6/part")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.idcode_ = node["idcode"].as<uint32_t>();
|
||||||
|
|
||||||
|
if (node["global_clock_regions"]) {
|
||||||
|
lhs.top_region_ = node["global_clock_regions"]["top"]
|
||||||
|
.as<spartan6::GlobalClockRegion>();
|
||||||
|
lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
|
||||||
|
.as<spartan6::GlobalClockRegion>();
|
||||||
|
} else if (node["configuration_ranges"]) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
for (auto range : node["configuration_ranges"]) {
|
||||||
|
auto begin =
|
||||||
|
range["begin"].as<spartan6::FrameAddress>();
|
||||||
|
auto end = range["end"].as<spartan6::FrameAddress>();
|
||||||
|
for (uint32_t cur = begin; cur < end; ++cur) {
|
||||||
|
addresses.push_back(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs = spartan6::Part(lhs.idcode_, addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <absl/types/span.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/xilinx/bitstream_reader.h>
|
||||||
|
#include <prjxray/xilinx/configuration_packet.h>
|
||||||
|
#include <prjxray/xilinx/configuration_register.h>
|
||||||
|
|
||||||
|
#include <prjxray/big_endian_span.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
TEST(BitstreamReaderTest, InitWithEmptyBytesReturnsNull) {
|
||||||
|
absl::Span<uint8_t> bitstream;
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
EXPECT_FALSE(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, InitWithOnlySyncReturnsObject) {
|
||||||
|
std::vector<uint8_t> bitstream{0xAA, 0x99, 0x55, 0x66};
|
||||||
|
absl::Span<std::vector<uint8_t>::value_type> bitstream_span(bitstream);
|
||||||
|
// auto config_packets =
|
||||||
|
// bitstream_span.subspan(bitstream.end() - bitstream.begin());
|
||||||
|
// auto big_endian_reader =
|
||||||
|
// prjxray::make_big_endian_span<uint16_t>(bitstream_span);
|
||||||
|
// std::vector<uint16_t> words{big_endian_reader.begin(),
|
||||||
|
// big_endian_reader.end()};
|
||||||
|
|
||||||
|
// for (auto word: words) {
|
||||||
|
// std::cout << "0x" << std::hex << word << std::endl;
|
||||||
|
//}
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
EXPECT_TRUE(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, InitWithSyncAfterNonWordSizedPaddingReturnsObject) {
|
||||||
|
std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xAA, 0x99, 0x55, 0x66};
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
EXPECT_TRUE(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, InitWithSyncAfterWordSizedPaddingReturnsObject) {
|
||||||
|
std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xFD, 0xFC,
|
||||||
|
0xAA, 0x99, 0x55, 0x66};
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
EXPECT_TRUE(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, ParsesType1Packet) {
|
||||||
|
std::vector<uint8_t> bitstream{
|
||||||
|
0xAA, 0x99, 0x55, 0x66, // sync
|
||||||
|
0x20, 0x00, 0x20, 0x00, // NOP
|
||||||
|
};
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
ASSERT_TRUE(reader);
|
||||||
|
ASSERT_NE(reader->begin(), reader->end());
|
||||||
|
|
||||||
|
auto first_packet = reader->begin();
|
||||||
|
EXPECT_EQ(first_packet->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6>::Opcode::NOP);
|
||||||
|
|
||||||
|
auto second_packet = ++first_packet;
|
||||||
|
EXPECT_EQ(second_packet->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6>::Opcode::NOP);
|
||||||
|
|
||||||
|
EXPECT_EQ(++second_packet, reader->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, ParseType2PacketWithoutType1Fails) {
|
||||||
|
std::vector<uint8_t> bitstream{
|
||||||
|
0xAA, 0x99, 0x55, 0x66, // sync
|
||||||
|
0x40, 0x00, 0x40, 0x00, // Type 2 NOP
|
||||||
|
};
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
ASSERT_TRUE(reader);
|
||||||
|
EXPECT_EQ(reader->begin(), reader->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamReaderTest, ParsesType2AfterType1Packet) {
|
||||||
|
std::vector<uint8_t> bitstream{
|
||||||
|
0xAA, 0x99, // sync
|
||||||
|
0x55, 0x66, // sync
|
||||||
|
0x28, 0x80, // Type 1 Read zero bytes from FDRO
|
||||||
|
0x50, 0x60, // Type 2 Write of 8 16-bit words
|
||||||
|
0x00, 0x00, // WC1 bits 31:16
|
||||||
|
0x00, 0x08, // WC2 bits 15:0
|
||||||
|
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
|
||||||
|
0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10,
|
||||||
|
};
|
||||||
|
std::vector<uint32_t> data_words{0x0102, 0x0304, 0x0506, 0x0708,
|
||||||
|
0x090A, 0x0B0C, 0x0D0E, 0x0F10};
|
||||||
|
|
||||||
|
auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
|
||||||
|
ASSERT_TRUE(reader);
|
||||||
|
ASSERT_NE(reader->begin(), reader->end());
|
||||||
|
|
||||||
|
auto first_packet = reader->begin();
|
||||||
|
EXPECT_EQ(first_packet->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6>::Opcode::Read);
|
||||||
|
EXPECT_EQ(first_packet->address(), Spartan6::ConfRegType::FDRO);
|
||||||
|
EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>());
|
||||||
|
|
||||||
|
auto third_packet = ++first_packet;
|
||||||
|
ASSERT_NE(third_packet, reader->end());
|
||||||
|
EXPECT_EQ(third_packet->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6>::Opcode::Write);
|
||||||
|
EXPECT_EQ(third_packet->address(), Spartan6::ConfRegType::FDRI);
|
||||||
|
(third_packet->data(), absl::Span<uint32_t>(data_words));
|
||||||
|
EXPECT_EQ(++first_packet, reader->end());
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/xilinx/architectures.h>
|
||||||
|
#include <prjxray/xilinx/bitstream_reader.h>
|
||||||
|
#include <prjxray/xilinx/bitstream_writer.h>
|
||||||
|
|
||||||
|
#include <prjxray/bit_ops.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 15, 13, 0x1);
|
||||||
|
|
||||||
|
extern const uint32_t MakeType1(const int opcode,
|
||||||
|
const int address,
|
||||||
|
const int word_count);
|
||||||
|
|
||||||
|
extern const std::vector<uint32_t> MakeType2(const int opcode,
|
||||||
|
const int address,
|
||||||
|
const int word_count);
|
||||||
|
|
||||||
|
void dump_packets(BitstreamWriter<Spartan6> writer, bool nl = true) {
|
||||||
|
int i = 0;
|
||||||
|
// for (uint32_t x : itr) {
|
||||||
|
for (auto itr = writer.begin(); itr != writer.end(); ++itr) {
|
||||||
|
if (nl) {
|
||||||
|
printf("% 3d: 0x0%08X\n", i, *itr);
|
||||||
|
} else {
|
||||||
|
printf("0x0%08X, ", *itr);
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (!nl) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special all 0's
|
||||||
|
void AddType0(
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>&
|
||||||
|
packets) {
|
||||||
|
// InitWithWords doesn't like type 0
|
||||||
|
/*
|
||||||
|
static std::vector<uint32_t> words{0x00000000};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet =
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(word_span);
|
||||||
|
packets.push_back(*(packet.second));
|
||||||
|
*/
|
||||||
|
static std::vector<uint32_t> words{};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
// CRC is config value 0
|
||||||
|
packets.emplace_back(new ConfigurationPacket<Spartan6::ConfRegType>(
|
||||||
|
0, ConfigurationPacket<Spartan6::ConfRegType>::NOP,
|
||||||
|
Spartan6::ConfRegType::CRC, word_span));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddType1(
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>&
|
||||||
|
packets) {
|
||||||
|
static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
packets.emplace_back(
|
||||||
|
new ConfigurationPacket<Spartan6::ConfRegType>(*(packet.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty
|
||||||
|
void AddType1E(
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>&
|
||||||
|
packets) {
|
||||||
|
static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
packets.emplace_back(
|
||||||
|
new ConfigurationPacket<Spartan6::ConfRegType>(*(packet.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddType2(Spartan6::ConfigurationPackage& packets) {
|
||||||
|
// Type 2 packet with data
|
||||||
|
{
|
||||||
|
static std::vector<uint32_t> words;
|
||||||
|
words = MakeType2(0x02, 0x3, 12);
|
||||||
|
std::vector<uint32_t> payload{1, 2, 3, 4, 5, 6,
|
||||||
|
7, 8, 9, 10, 11, 12};
|
||||||
|
words.insert(words.end(), payload.begin(), payload.end());
|
||||||
|
std::cout << words.size();
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet =
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
packets.emplace_back(
|
||||||
|
new ConfigurationPacket<Spartan6::ConfRegType>(
|
||||||
|
*(packet.second)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty packets should produce just the header
|
||||||
|
TEST(BitstreamWriterTest, WriteHeader) {
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>
|
||||||
|
packets;
|
||||||
|
|
||||||
|
BitstreamWriter<Spartan6> writer(packets);
|
||||||
|
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||||
|
|
||||||
|
// Per UG380 pg 78: Bus Width Auto Detection
|
||||||
|
std::vector<uint32_t> ref_header{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xAA99, 0x5566};
|
||||||
|
EXPECT_EQ(words, ref_header);
|
||||||
|
|
||||||
|
// dump_packets(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamWriterTest, WriteType0) {
|
||||||
|
std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>
|
||||||
|
packets;
|
||||||
|
AddType0(packets);
|
||||||
|
BitstreamWriter<Spartan6> writer(packets);
|
||||||
|
// dump_packets(writer, false);
|
||||||
|
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||||
|
std::vector<uint32_t> ref{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
|
||||||
|
// Type 0
|
||||||
|
0x0000};
|
||||||
|
EXPECT_EQ(words, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamWriterTest, WriteType1) {
|
||||||
|
Spartan6::ConfigurationPackage packets;
|
||||||
|
AddType1(packets);
|
||||||
|
BitstreamWriter<Spartan6> writer(packets);
|
||||||
|
// dump_packets(writer, false);
|
||||||
|
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||||
|
std::vector<uint32_t> ref{// Bus width + sync
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
|
||||||
|
// Type 1
|
||||||
|
0x3062, 0x00AA, 0x00BB};
|
||||||
|
EXPECT_EQ(words, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamWriterTest, WriteType2) {
|
||||||
|
Spartan6::ConfigurationPackage packets;
|
||||||
|
AddType2(packets);
|
||||||
|
BitstreamWriter<Spartan6> writer(packets);
|
||||||
|
// dump_packets(writer, false);
|
||||||
|
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||||
|
std::vector<uint32_t> ref{
|
||||||
|
// Bus width + sync
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xAA99, 0x5566, 0x5060, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005,
|
||||||
|
0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C};
|
||||||
|
EXPECT_EQ(words, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BitstreamWriterTest, WriteMulti) {
|
||||||
|
Spartan6::ConfigurationPackage packets;
|
||||||
|
AddType1(packets);
|
||||||
|
AddType1E(packets);
|
||||||
|
AddType2(packets);
|
||||||
|
AddType1E(packets);
|
||||||
|
BitstreamWriter<Spartan6> writer(packets);
|
||||||
|
// dump_packets(writer, false);
|
||||||
|
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||||
|
std::vector<uint32_t> ref{// Bus width + sync
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
||||||
|
0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
|
||||||
|
// Type1
|
||||||
|
0x3062, 0x00AA, 0x00BB,
|
||||||
|
// Type1
|
||||||
|
0x3060,
|
||||||
|
// Type 1 + type 2 header
|
||||||
|
0x5060, 0x0001, 0x0002, 0x0003, 0x0004,
|
||||||
|
0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
|
||||||
|
0x000A, 0x000B, 0x000C,
|
||||||
|
// Type 1
|
||||||
|
0x3060};
|
||||||
|
EXPECT_EQ(words, ref);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/block_type.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(BlockTypeTest, YamlEncode) {
|
||||||
|
YAML::Node node;
|
||||||
|
node.push_back(spartan6::BlockType::CLB_IOI_CLK);
|
||||||
|
node.push_back(spartan6::BlockType::BLOCK_RAM);
|
||||||
|
node.push_back(spartan6::BlockType::IOB);
|
||||||
|
|
||||||
|
EXPECT_EQ(node[0].as<std::string>(), "CLB_IOI_CLK");
|
||||||
|
EXPECT_EQ(node[1].as<std::string>(), "BLOCK_RAM");
|
||||||
|
EXPECT_EQ(node[2].as<std::string>(), "IOB");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockTypeTest, YamlDecode) {
|
||||||
|
YAML::Node node;
|
||||||
|
node.push_back("IOB");
|
||||||
|
node.push_back("BLOCK_RAM");
|
||||||
|
node.push_back("CLB_IOI_CLK");
|
||||||
|
|
||||||
|
EXPECT_EQ(node[0].as<spartan6::BlockType>(), spartan6::BlockType::IOB);
|
||||||
|
EXPECT_EQ(node[1].as<spartan6::BlockType>(),
|
||||||
|
spartan6::BlockType::BLOCK_RAM);
|
||||||
|
EXPECT_EQ(node[2].as<spartan6::BlockType>(),
|
||||||
|
spartan6::BlockType::CLB_IOI_CLK);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_bus.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, IsValidFrameAddress) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
|
||||||
|
|
||||||
|
spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(bus.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(bus.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(bus.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, GetNextFrameAddressYieldNextAddressInBus) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
|
||||||
|
|
||||||
|
spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
auto next_address = bus.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address, spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = bus.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address, spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, GetNextFrameAddressYieldNothingAtEndOfBus) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
|
||||||
|
|
||||||
|
spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(bus.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_column.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/block_type.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, IsValidFrameAddress) {
|
||||||
|
spartan6::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
// Inside this column.
|
||||||
|
EXPECT_TRUE(column.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3)));
|
||||||
|
// Past this column's frame width.
|
||||||
|
EXPECT_FALSE(column.IsValidFrameAddress(spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNextAddressInColumn) {
|
||||||
|
spartan6::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
auto next_address = column.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3));
|
||||||
|
EXPECT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingAtEndOfColumn) {
|
||||||
|
spartan6::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingOutsideColumn) {
|
||||||
|
spartan6::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
// Just past last frame in column.
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, YamlEncodeTest) {
|
||||||
|
spartan6::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
YAML::Node node(column);
|
||||||
|
EXPECT_TRUE(node["frame_count"]);
|
||||||
|
EXPECT_EQ(node["frame_count"].as<int>(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, YAMLDecodeTest) {
|
||||||
|
YAML::Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/configuration_column");
|
||||||
|
node["frame_count"] = 10;
|
||||||
|
|
||||||
|
auto column = node.as<spartan6::ConfigurationColumn>();
|
||||||
|
EXPECT_TRUE(column.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 8)));
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9)));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <absl/meta/type_traits.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/bit_ops.h>
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/architectures.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 15, 13, 0x1);
|
||||||
|
|
||||||
|
const 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, 15, 13, 0x1), 12, 11,
|
||||||
|
opcode),
|
||||||
|
10, 5, address),
|
||||||
|
4, 0, word_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint32_t> MakeType2(const int opcode,
|
||||||
|
const int address,
|
||||||
|
const int word_count) {
|
||||||
|
uint32_t header = 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, 15, 13, 0x2), 12, 11,
|
||||||
|
opcode),
|
||||||
|
10, 5, address),
|
||||||
|
4, 0, 0);
|
||||||
|
uint32_t wcr1 = (word_count >> 16) & 0xFFFF;
|
||||||
|
uint32_t wcr2 = (word_count & 0xFFFF);
|
||||||
|
return std::vector<uint32_t>{header, wcr1, wcr2};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigPacket, InitWithZeroBytes) {
|
||||||
|
auto packet =
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::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 = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||||
|
ASSERT_TRUE(packet.second);
|
||||||
|
EXPECT_EQ(packet.second->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::NOP);
|
||||||
|
EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::CRC);
|
||||||
|
EXPECT_EQ(packet.second->data(), absl::Span<uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigPacket, InitWithType1Read) {
|
||||||
|
std::vector<uint32_t> words{MakeType1(0x1, 0x3, 2), 0xAA, 0xBB};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||||
|
ASSERT_TRUE(packet.second);
|
||||||
|
EXPECT_EQ(packet.second->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Read);
|
||||||
|
EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI);
|
||||||
|
EXPECT_EQ(packet.second->data(), word_span.subspan(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigPacket, InitWithType1Write) {
|
||||||
|
std::vector<uint32_t> words{MakeType1(0x2, 0x4, 2), 0xAA, 0xBB};
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||||
|
ASSERT_TRUE(packet.second);
|
||||||
|
EXPECT_EQ(packet.second->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write);
|
||||||
|
EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRO);
|
||||||
|
EXPECT_EQ(packet.second->data(), word_span.subspan(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigPacket, InitWithType2WithPreviousPacket) {
|
||||||
|
std::vector<uint32_t> words{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||||
|
std::vector<uint32_t> type2 = MakeType2(0x01, 0x03, 12);
|
||||||
|
words.insert(words.begin(), type2.begin(), type2.end());
|
||||||
|
absl::Span<uint32_t> word_span(words);
|
||||||
|
auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
|
||||||
|
word_span);
|
||||||
|
EXPECT_EQ(packet.first, absl::Span<uint32_t>());
|
||||||
|
ASSERT_TRUE(packet.second);
|
||||||
|
EXPECT_EQ(packet.second->opcode(),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Read);
|
||||||
|
EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI);
|
||||||
|
EXPECT_EQ(packet.second->data(), word_span.subspan(3));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <absl/types/span.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/memory_mapped_file.h>
|
||||||
|
#include <prjxray/xilinx/architectures.h>
|
||||||
|
#include <prjxray/xilinx/configuration.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
|
||||||
|
std::vector<spartan6::FrameAddress> test_part_addresses;
|
||||||
|
test_part_addresses.push_back(0x0A);
|
||||||
|
test_part_addresses.push_back(0x0B);
|
||||||
|
|
||||||
|
spartan6::Part test_part(0x1234, test_part_addresses);
|
||||||
|
|
||||||
|
std::vector<uint32_t> idcode{0x1234};
|
||||||
|
std::vector<uint32_t> cmd{0x0001};
|
||||||
|
std::vector<uint32_t> frame_address{0x345};
|
||||||
|
std::vector<uint32_t> frame(65, 0xAA);
|
||||||
|
|
||||||
|
std::vector<ConfigurationPacket<typename Spartan6::ConfRegType>>
|
||||||
|
packets{
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::IDCODE,
|
||||||
|
absl::MakeSpan(idcode),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FAR_MIN,
|
||||||
|
absl::MakeSpan(frame_address),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::CMD,
|
||||||
|
absl::MakeSpan(cmd),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FDRI,
|
||||||
|
absl::MakeSpan(frame),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test_config =
|
||||||
|
Configuration<Spartan6>::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(0x345), frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
|
||||||
|
std::vector<spartan6::FrameAddress> test_part_addresses;
|
||||||
|
for (int ii = 0x310; ii < 0x320; ++ii) {
|
||||||
|
test_part_addresses.push_back(ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int ii = 0x330; ii < 0x331; ++ii) {
|
||||||
|
test_part_addresses.push_back(ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
spartan6::Part test_part(0x1234, test_part_addresses);
|
||||||
|
|
||||||
|
std::vector<uint32_t> idcode{0x1234};
|
||||||
|
std::vector<uint32_t> cmd{0x0001};
|
||||||
|
std::vector<uint32_t> frame_address{0x31f};
|
||||||
|
std::vector<uint32_t> frame(65 * 2, 0xAA);
|
||||||
|
std::fill_n(frame.begin() + 65, 65, 0xBB);
|
||||||
|
|
||||||
|
std::vector<ConfigurationPacket<Spartan6::ConfRegType>> packets{
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::IDCODE,
|
||||||
|
absl::MakeSpan(idcode),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FAR_MIN,
|
||||||
|
absl::MakeSpan(frame_address),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::CMD,
|
||||||
|
absl::MakeSpan(cmd),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FDRI,
|
||||||
|
absl::MakeSpan(frame),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test_config =
|
||||||
|
Configuration<Spartan6>::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(0x31f),
|
||||||
|
std::vector<uint32_t>(65, 0xAA));
|
||||||
|
// TODO This test fails with a C++ exception because the address
|
||||||
|
// of next frame is 0x320 instead of 0x330 as defined in the test_part
|
||||||
|
// EXPECT_EQ(test_config->frames().at(0x330),
|
||||||
|
// std::vector<uint32_t>(65, 0xBB));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationTest, DISABLED_CheckForPaddingAfterIOBFrame) {
|
||||||
|
std::vector<spartan6::FrameAddress> test_part_addresses = {
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 1, 0, 0),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::IOB, 2, 0, 0)};
|
||||||
|
|
||||||
|
auto test_part = absl::optional<spartan6::Part>(
|
||||||
|
spartan6::Part(0x1234, test_part_addresses));
|
||||||
|
|
||||||
|
Frames<Spartan6> frames;
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
test_part_addresses.at(0), std::vector<uint32_t>(65, 0xAA)));
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
test_part_addresses.at(1), std::vector<uint32_t>(65, 0xBB)));
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
test_part_addresses.at(2), std::vector<uint32_t>(65, 0xCC)));
|
||||||
|
ASSERT_EQ(frames.getFrames().size(), 3);
|
||||||
|
|
||||||
|
Configuration<Spartan6>::PacketData packet_data =
|
||||||
|
Configuration<Spartan6>::createType2ConfigurationPacketData(
|
||||||
|
frames.getFrames(), test_part);
|
||||||
|
// createType2ConfigurationPacketData should add a 16-bit pad word after
|
||||||
|
// after the IOB frame
|
||||||
|
EXPECT_EQ(packet_data.size(), 3 * 65 + 1);
|
||||||
|
|
||||||
|
std::vector<uint32_t> idcode{0x1234};
|
||||||
|
std::vector<uint32_t> cmd{0x0001};
|
||||||
|
std::vector<uint32_t> frame_address{0x0};
|
||||||
|
|
||||||
|
std::vector<ConfigurationPacket<Spartan6::ConfRegType>> packets{
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::IDCODE,
|
||||||
|
absl::MakeSpan(idcode),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FAR,
|
||||||
|
absl::MakeSpan(frame_address),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::CMD,
|
||||||
|
absl::MakeSpan(cmd),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<unsigned int>(0x1),
|
||||||
|
ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
|
||||||
|
Spartan6::ConfRegType::FDRI,
|
||||||
|
absl::MakeSpan(packet_data),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test_config =
|
||||||
|
Configuration<Spartan6>::InitWithPackets(*test_part, packets);
|
||||||
|
ASSERT_EQ(test_config->frames().size(), 5);
|
||||||
|
for (auto& frame : test_config->frames()) {
|
||||||
|
EXPECT_EQ(frame.second, frames.getFrames().at(frame.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/frame_address.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(FrameAddressTest, YamlEncode) {
|
||||||
|
spartan6::FrameAddress address(spartan6::BlockType::BLOCK_RAM, 10, 0,
|
||||||
|
5);
|
||||||
|
|
||||||
|
YAML::Node node(address);
|
||||||
|
|
||||||
|
EXPECT_EQ(node.Tag(), "xilinx/spartan6/frame_address");
|
||||||
|
EXPECT_EQ(node["block_type"].as<std::string>(), "BLOCK_RAM");
|
||||||
|
EXPECT_EQ(node["row"].as<std::string>(), "10");
|
||||||
|
EXPECT_EQ(node["column"].as<std::string>(), "0");
|
||||||
|
EXPECT_EQ(node["minor"].as<std::string>(), "5");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameAddressTest, YamlDecode) {
|
||||||
|
YAML::Node node;
|
||||||
|
node.SetTag("xilinx/spartan6/frame_address");
|
||||||
|
node["block_type"] = "BLOCK_RAM";
|
||||||
|
node["row"] = "0";
|
||||||
|
node["column"] = "5";
|
||||||
|
node["minor"] = "11";
|
||||||
|
|
||||||
|
spartan6::FrameAddress address = node.as<spartan6::FrameAddress>();
|
||||||
|
EXPECT_EQ(address.block_type(), spartan6::BlockType::BLOCK_RAM);
|
||||||
|
EXPECT_EQ(address.row(), 0);
|
||||||
|
EXPECT_EQ(address.column(), 5);
|
||||||
|
EXPECT_EQ(address.minor(), 11);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/frames.h>
|
||||||
|
#include <prjxray/xilinx/spartan6/part.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
TEST(FramesTest, FillInMissingFrames) {
|
||||||
|
std::vector<spartan6::FrameAddress> test_part_addresses = {
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 3),
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 4)};
|
||||||
|
|
||||||
|
spartan6::Part test_part(0x1234, test_part_addresses);
|
||||||
|
|
||||||
|
Frames<Spartan6> frames;
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
spartan6::FrameAddress(2), std::vector<uint32_t>(65, 0xCC)));
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
spartan6::FrameAddress(3), std::vector<uint32_t>(65, 0xDD)));
|
||||||
|
frames.getFrames().emplace(std::make_pair(
|
||||||
|
spartan6::FrameAddress(4), std::vector<uint32_t>(65, 0xEE)));
|
||||||
|
|
||||||
|
ASSERT_EQ(frames.getFrames().size(), 3);
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
|
||||||
|
std::vector<uint32_t>(65, 0xCC));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
|
||||||
|
std::vector<uint32_t>(65, 0xDD));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
|
||||||
|
std::vector<uint32_t>(65, 0xEE));
|
||||||
|
|
||||||
|
frames.addMissingFrames(test_part);
|
||||||
|
|
||||||
|
ASSERT_EQ(frames.getFrames().size(), 5);
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[0]),
|
||||||
|
std::vector<uint32_t>(65, 0));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[1]),
|
||||||
|
std::vector<uint32_t>(65, 0));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
|
||||||
|
std::vector<uint32_t>(65, 0xCC));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
|
||||||
|
std::vector<uint32_t>(65, 0xDD));
|
||||||
|
EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
|
||||||
|
std::vector<uint32_t>(65, 0xEE));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/global_clock_region.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest, IsValidFrameAddress) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
|
||||||
|
spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
|
||||||
|
EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0)));
|
||||||
|
EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest,
|
||||||
|
GetNextFrameAddressYieldNextAddressInGlobalClockRegion) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
|
||||||
|
spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
auto next_address = global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
|
||||||
|
next_address = global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
|
||||||
|
next_address = global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address, spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest,
|
||||||
|
GetNextFrameAddressYieldNothingAtEndOfGlobalClockRegion) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
|
||||||
|
spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)));
|
||||||
|
EXPECT_FALSE(global_clock_region.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/part.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(PartTest, IsValidFrameAddress) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
spartan6::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
spartan6::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
auto next_address = part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address, spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
spartan6::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(part.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <prjxray/xilinx/spartan6/configuration_row.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace prjxray::xilinx;
|
||||||
|
|
||||||
|
TEST(RowTest, IsValidFrameAddress) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
|
||||||
|
spartan6::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(row.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(row.IsValidFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RowTest, GetNextFrameAddressYieldNextAddressInRow) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
|
||||||
|
spartan6::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
auto next_address = row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(
|
||||||
|
*next_address,
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
|
||||||
|
// Rows have unique behavior for GetNextFrameAddress() at the end of a
|
||||||
|
// bus. Since the addresses need to be returned in numerically
|
||||||
|
// increasing order, all of the rows need to be returned before moving
|
||||||
|
// to a different bus. That means that Row::GetNextFrameAddress() needs
|
||||||
|
// to return no object at the end of a bus and let the caller use that
|
||||||
|
// as a signal to try the next row.
|
||||||
|
next_address = row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
EXPECT_FALSE(next_address);
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address, spartan6::FrameAddress(
|
||||||
|
spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
EXPECT_FALSE(next_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RowTest, GetNextFrameAddressYieldNothingAtEndOfRow) {
|
||||||
|
std::vector<spartan6::FrameAddress> addresses;
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
|
||||||
|
addresses.push_back(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
|
||||||
|
|
||||||
|
spartan6::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(row.GetNextFrameAddress(
|
||||||
|
spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
@ -150,7 +150,12 @@ struct BitReader {
|
||||||
i++) {
|
i++) {
|
||||||
for (int k = 0; k < word_length; k++) {
|
for (int k = 0; k < word_length; k++) {
|
||||||
if (((i != 50 || k > 12 ||
|
if (((i != 50 || k > 12 ||
|
||||||
FLAGS_C)) &&
|
FLAGS_C) ||
|
||||||
|
std::is_same<
|
||||||
|
ArchType,
|
||||||
|
prjxray::xilinx::
|
||||||
|
Spartan6>::
|
||||||
|
value) &&
|
||||||
((it.second.at(i) &
|
((it.second.at(i) &
|
||||||
(1 << k)) != 0)) {
|
(1 << k)) != 0)) {
|
||||||
if (FLAGS_x)
|
if (FLAGS_x)
|
||||||
|
|
@ -200,12 +205,23 @@ struct BitReader {
|
||||||
static_cast<uint32_t>(it.first));
|
static_cast<uint32_t>(it.first));
|
||||||
|
|
||||||
for (size_t i = 0; i < it.second.size(); i++)
|
for (size_t i = 0; i < it.second.size(); i++)
|
||||||
fprintf(f, "%08x%s",
|
if (std::is_same<ArchType,
|
||||||
it.second.at(i) &
|
prjxray::xilinx::
|
||||||
((i != 50 || FLAGS_C)
|
Spartan6>::value) {
|
||||||
? 0xffffffff
|
fprintf(
|
||||||
: 0xffffe000),
|
f, "%08x%s",
|
||||||
(i % 6) == 5 ? "\n" : " ");
|
it.second.at(i) &
|
||||||
|
0xffffffff,
|
||||||
|
(i % 6) == 5 ? "\n" : " ");
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
f, "%08x%s",
|
||||||
|
it.second.at(i) &
|
||||||
|
((i != 50 || FLAGS_C)
|
||||||
|
? 0xffffffff
|
||||||
|
: 0xffffe000),
|
||||||
|
(i % 6) == 5 ? "\n" : " ");
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(f, "\n\n");
|
fprintf(f, "\n\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,14 +170,28 @@ struct DeviceIdGetter {
|
||||||
ArchType::ConfRegType::IDCODE);
|
ArchType::ConfRegType::IDCODE);
|
||||||
});
|
});
|
||||||
if (idcode_packet != reader->end()) {
|
if (idcode_packet != reader->end()) {
|
||||||
if (idcode_packet->data().size() != 1) {
|
if (std::is_same<ArchType, xilinx::Spartan6>::value) {
|
||||||
std::cerr
|
if (idcode_packet->data().size() != 2) {
|
||||||
<< "Write to IDCODE with word_count != 1"
|
std::cerr << "Write to IDCODE with "
|
||||||
<< std::endl;
|
"word_count != 2"
|
||||||
return 1;
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "0x" << std::hex
|
||||||
|
<< ((idcode_packet->data()[0] << 16) |
|
||||||
|
idcode_packet->data()[1])
|
||||||
|
<< std::endl;
|
||||||
|
} else {
|
||||||
|
if (idcode_packet->data().size() != 1) {
|
||||||
|
std::cerr << "Write to IDCODE with "
|
||||||
|
"word_count != 1"
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "0x" << std::hex
|
||||||
|
<< idcode_packet->data()[0]
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
std::cout << "0x" << std::hex
|
|
||||||
<< idcode_packet->data()[0] << std::endl;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue