Merge branch 'master' into tileconnloops

This commit is contained in:
Clifford Wolf 2018-01-05 22:32:40 +01:00 committed by GitHub
commit 89854a55e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2314 additions and 138 deletions

View File

@ -4,7 +4,7 @@ add_library(libprjxray
segbits_file_reader.cc
xilinx/xc7series/bitstream_reader.cc
xilinx/xc7series/block_type.cc
xilinx/xc7series/configuration_frame_address.cc
xilinx/xc7series/frame_address.cc
xilinx/xc7series/configuration_frame_range.cc
xilinx/xc7series/configuration_packet.cc
xilinx/xc7series/configuration_register.cc
@ -50,19 +50,20 @@ if (PRJXRAY_BUILD_TESTING)
add_test(NAME xilinx_xc7series_block_type_test
COMMAND xilinx_xc7series_block_type_test)
add_executable(xilinx_xc7series_configuration_frame_address_test
xilinx/xc7series/configuration_frame_address_test.cc)
target_link_libraries(xilinx_xc7series_configuration_frame_address_test
add_executable(xilinx_xc7series_frame_address_test
xilinx/xc7series/frame_address_test.cc)
target_link_libraries(xilinx_xc7series_frame_address_test
libprjxray gtest_main absl::span)
add_test(NAME xilinx_xc7series_configuration_frame_address_test
COMMAND xilinx_xc7series_configuration_frame_address_test)
add_test(NAME xilinx_xc7series_frame_address_test
COMMAND xilinx_xc7series_frame_address_test)
add_executable(xilinx_xc7series_configuration_test
xilinx/xc7series/configuration_test.cc)
target_link_libraries(xilinx_xc7series_configuration_test
libprjxray gtest_main absl::span)
add_test(NAME xilinx_xc7series_configuration_test
COMMAND xilinx_xc7series_configuration_test)
COMMAND xilinx_xc7series_configuration_test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
add_executable(xilinx_xc7series_configuration_packet_test
xilinx/xc7series/configuration_packet_test.cc)

View File

@ -4,6 +4,8 @@
#include <memory>
#include <string>
#include <absl/types/span.h>
namespace prjxray {
class MemoryMappedFile {
@ -16,6 +18,10 @@ class MemoryMappedFile {
void* const data() const { return data_; }
const size_t size() const { return size_; }
absl::Span<uint8_t> as_bytes() const {
return {static_cast<uint8_t*>(data_), size_};
}
private:
MemoryMappedFile(void *data, size_t size)
: data_(data), size_(size) {};

View File

@ -47,7 +47,7 @@ class BitstreamReader {
// Construct a `BitstreamReader` from a Container of bytes.
// Any bytes preceding an initial sync word are ignored.
template<typename T>
static absl::optional<BitstreamReader> InitWithBytes(T &bitstream);
static absl::optional<BitstreamReader> InitWithBytes(T bitstream);
const std::vector<uint32_t> &words() { return words_; };
@ -62,7 +62,7 @@ class BitstreamReader {
};
template<typename T>
absl::optional<BitstreamReader> BitstreamReader::InitWithBytes(T &bitstream) {
absl::optional<BitstreamReader> BitstreamReader::InitWithBytes(T bitstream) {
// If this is really a Xilinx 7-Series bitstream, there will be a sync
// word somewhere toward the beginning.
auto sync_pos = std::search(bitstream.begin(), bitstream.end(),

View File

@ -6,7 +6,7 @@
#include <absl/types/span.h>
#include <prjxray/bit_ops.h>
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <prjxray/xilinx/xc7series/part.h>
namespace prjxray {
@ -15,7 +15,7 @@ namespace xc7series {
class Configuration {
public:
using FrameMap = std::map<ConfigurationFrameAddress,
using FrameMap = std::map<FrameAddress,
absl::Span<uint32_t>>;
template<typename Collection>
@ -46,7 +46,7 @@ absl::optional<Configuration> Configuration::InitWithPackets(
// Internal state machine for writes.
bool start_new_write = false;
ConfigurationFrameAddress current_frame_address = 0;
FrameAddress current_frame_address = 0;
Configuration::FrameMap frames;
for (auto packet : packets) {
@ -112,20 +112,24 @@ absl::optional<Configuration> Configuration::InitWithPackets(
// 7-series frames are 101-words long. Writes to this
// register can be multiples of that to do
// auto-incrementing block writes.
int frames_written = packet.data().size() /
kWordsPerFrame;
for (int ii = 0; ii < frames_written; ++ii) {
for (size_t ii = 0;
ii < packet.data().size();
ii += kWordsPerFrame) {
frames[current_frame_address] =
packet.data().subspan(
ii * kWordsPerFrame,
kWordsPerFrame);
auto next_address =
part.GetNextConfigurationFrameAddress(
ii, kWordsPerFrame);
auto next_address = part.GetNextFrameAddress(
current_frame_address);
if (next_address) {
current_frame_address = *next_address;
if (!next_address) break;
// Bitstreams appear to have 2 frames of
// padding between rows.
if (next_address->row_address() !=
current_frame_address.row_address()) {
ii += 2 * kWordsPerFrame;
}
current_frame_address = *next_address;
}
break;
}

View File

@ -1,49 +0,0 @@
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_
#include <cstdint>
#include <prjxray/xilinx/xc7series/block_type.h>
#include <yaml-cpp/yaml.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class ConfigurationFrameAddress {
public:
ConfigurationFrameAddress()
: address_(0) {}
ConfigurationFrameAddress(uint32_t address)
: address_(address) {};
ConfigurationFrameAddress(
BlockType block_type, bool is_bottom_half_rows,
uint8_t row, uint16_t column, uint8_t minor);
operator uint32_t() const { return address_; }
BlockType block_type() const;
bool is_bottom_half_rows() const;
uint8_t row_address() const;
uint16_t column_address() const;
uint8_t minor_address() const;
private:
uint32_t address_;
};
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace YAML {
template<>
struct convert<prjxray::xilinx::xc7series::ConfigurationFrameAddress> {
static Node encode(const prjxray::xilinx::xc7series::ConfigurationFrameAddress &rhs);
static bool decode(const Node& node,
prjxray::xilinx::xc7series::ConfigurationFrameAddress &lhs);
};
} // namespace YAML
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_ADDRESS_H_

View File

@ -3,7 +3,7 @@
#include <cstdint>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <yaml-cpp/yaml.h>
namespace prjxray {
@ -14,21 +14,20 @@ class ConfigurationFrameRange {
public:
ConfigurationFrameRange() : begin_(0), end_(0) {}
ConfigurationFrameRange(ConfigurationFrameAddress begin,
ConfigurationFrameAddress end)
ConfigurationFrameRange(FrameAddress begin, FrameAddress end)
: begin_(begin), end_(end) {};
ConfigurationFrameAddress begin() const { return begin_; }
ConfigurationFrameAddress end() const { return end_; }
FrameAddress begin() const { return begin_; }
FrameAddress end() const { return end_; }
size_t size() const { return end_ - begin_; }
bool empty() const { return size() == 0; }
bool Contains(ConfigurationFrameAddress address) const;
bool Contains(FrameAddress address) const;
private:
ConfigurationFrameAddress begin_;
ConfigurationFrameAddress end_;
FrameAddress begin_;
FrameAddress end_;
};
} // namespace xc7series

View File

@ -0,0 +1,50 @@
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_
#define PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_
#include <cstdint>
#include <ostream>
#include <prjxray/xilinx/xc7series/block_type.h>
#include <yaml-cpp/yaml.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class FrameAddress {
public:
FrameAddress() : address_(0) {}
FrameAddress(uint32_t address)
: address_(address) {};
FrameAddress(BlockType block_type, bool is_bottom_half_rows,
uint8_t row, uint16_t column, uint8_t minor);
operator uint32_t() const { return address_; }
BlockType block_type() const;
bool is_bottom_half_rows() const;
uint8_t row_address() const;
uint16_t column_address() const;
uint8_t minor_address() const;
private:
uint32_t address_;
};
std::ostream &operator<<(std::ostream &o, const FrameAddress& addr);
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace YAML {
template<>
struct convert<prjxray::xilinx::xc7series::FrameAddress> {
static Node encode(const prjxray::xilinx::xc7series::FrameAddress &rhs);
static bool decode(const Node& node,
prjxray::xilinx::xc7series::FrameAddress &lhs);
};
} // namespace YAML
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_

View File

@ -4,7 +4,7 @@
#include <vector>
#include <absl/types/optional.h>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
namespace prjxray {
@ -28,9 +28,8 @@ class Part {
const std::vector<ConfigurationFrameRange>&
configuration_frame_ranges() const { return frame_ranges_; }
absl::optional<ConfigurationFrameAddress>
GetNextConfigurationFrameAddress(
ConfigurationFrameAddress address) const;
absl::optional<FrameAddress>
GetNextFrameAddress(FrameAddress address) const;
private:
uint32_t idcode_;

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,7 @@ namespace prjxray {
namespace xilinx {
namespace xc7series {
bool ConfigurationFrameRange::Contains(
ConfigurationFrameAddress address) const {
bool ConfigurationFrameRange::Contains(FrameAddress address) const {
return address >= begin_ && address < end_;
}
@ -34,8 +33,8 @@ bool convert<xc7series::ConfigurationFrameRange>::decode(
!node["end"]) return false;
lhs = xc7series::ConfigurationFrameRange(
node["begin"].as<xc7series::ConfigurationFrameAddress>(),
node["end"].as<xc7series::ConfigurationFrameAddress>());
node["begin"].as<xc7series::FrameAddress>(),
node["end"].as<xc7series::FrameAddress>());
return true;
}

View File

@ -5,6 +5,7 @@
#include <absl/types/span.h>
#include <gtest/gtest.h>
#include <prjxray/memory_mapped_file.h>
#include <prjxray/xilinx/xc7series/configuration_packet.h>
#include <prjxray/xilinx/xc7series/configuration_register.h>
#include <prjxray/xilinx/xc7series/part.h>
@ -106,6 +107,106 @@ TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
absl::Span<uint32_t> frame_span(frame);
EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(2));
EXPECT_EQ(test_config->frames().at(0x456F), frame_span.subspan(0, 101));
EXPECT_EQ(test_config->frames().at(0x4580), frame_span.subspan(101));
EXPECT_EQ(test_config->frames().at(0x456F), std::vector<uint32_t>(101, 0xAA));
EXPECT_EQ(test_config->frames().at(0x4580), std::vector<uint32_t>(101, 0xBB));
}
TEST(ConfigurationTest, DebugAndPerFrameCrcBitstreamsProduceEqualConfigurations) {
auto part = xc7series::Part::FromFile("configuration_test.yaml");
ASSERT_TRUE(part);
auto debug_bitstream = prjxray::MemoryMappedFile::InitWithFile(
"configuration_test.debug.bit");
ASSERT_TRUE(debug_bitstream);
auto debug_reader = xc7series::BitstreamReader::InitWithBytes(
debug_bitstream->as_bytes());
ASSERT_TRUE(debug_reader);
auto debug_configuration = xc7series::Configuration::InitWithPackets(
*part, *debug_reader);
ASSERT_TRUE(debug_configuration);
auto perframecrc_bitstream = prjxray::MemoryMappedFile::InitWithFile(
"configuration_test.perframecrc.bit");
ASSERT_TRUE(perframecrc_bitstream);
auto perframecrc_reader = xc7series::BitstreamReader::InitWithBytes(
perframecrc_bitstream->as_bytes());
ASSERT_TRUE(perframecrc_reader);
auto perframecrc_configuration = xc7series::Configuration::InitWithPackets(
*part, *perframecrc_reader);
ASSERT_TRUE(perframecrc_configuration);
for (auto debug_frame : debug_configuration->frames()) {
auto perframecrc_frame = perframecrc_configuration->frames().find(debug_frame.first);
if (perframecrc_frame == perframecrc_configuration->frames().end()) {
ADD_FAILURE() << debug_frame.first << ": missing in perframecrc bitstream";
continue;
}
for (int ii = 0; ii < 101; ++ii) {
EXPECT_EQ(perframecrc_frame->second[ii], debug_frame.second[ii])
<< debug_frame.first << ": word " << ii;
}
}
for (auto perframecrc_frame : perframecrc_configuration->frames()) {
auto debug_frame = debug_configuration->frames().find(perframecrc_frame.first);
if (debug_frame == debug_configuration->frames().end()) {
ADD_FAILURE() << perframecrc_frame.first
<< ": unexpectedly present in perframecrc bitstream";
}
}
}
TEST(ConfigurationTest, DebugAndNormalBitstreamsProduceEqualConfigurations) {
auto part = xc7series::Part::FromFile("configuration_test.yaml");
ASSERT_TRUE(part);
auto debug_bitstream = prjxray::MemoryMappedFile::InitWithFile(
"configuration_test.debug.bit");
ASSERT_TRUE(debug_bitstream);
auto debug_reader = xc7series::BitstreamReader::InitWithBytes(
debug_bitstream->as_bytes());
ASSERT_TRUE(debug_reader);
auto debug_configuration = xc7series::Configuration::InitWithPackets(
*part, *debug_reader);
ASSERT_TRUE(debug_configuration);
auto normal_bitstream = prjxray::MemoryMappedFile::InitWithFile(
"configuration_test.bit");
ASSERT_TRUE(normal_bitstream);
auto normal_reader = xc7series::BitstreamReader::InitWithBytes(
normal_bitstream->as_bytes());
ASSERT_TRUE(normal_reader);
auto normal_configuration = xc7series::Configuration::InitWithPackets(
*part, *normal_reader);
ASSERT_TRUE(normal_configuration);
for (auto debug_frame : debug_configuration->frames()) {
auto normal_frame = normal_configuration->frames().find(debug_frame.first);
if (normal_frame == normal_configuration->frames().end()) {
ADD_FAILURE() << debug_frame.first << ": missing in normal bitstream";
continue;
}
for (int ii = 0; ii < 101; ++ii) {
EXPECT_EQ(normal_frame->second[ii], debug_frame.second[ii])
<< debug_frame.first << ": word " << ii;
}
}
for (auto normal_frame : normal_configuration->frames()) {
auto debug_frame = debug_configuration->frames().find(normal_frame.first);
if (debug_frame == debug_configuration->frames().end()) {
ADD_FAILURE() << normal_frame.first
<< ": unexpectedly present in normal bitstream";
}
}
}

View File

@ -1,4 +1,6 @@
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <iomanip>
#include <prjxray/bit_ops.h>
@ -6,10 +8,8 @@ namespace prjxray {
namespace xilinx {
namespace xc7series {
ConfigurationFrameAddress::ConfigurationFrameAddress(
BlockType block_type, bool is_bottom_half_rows,
uint8_t row, uint16_t column, uint8_t minor) {
FrameAddress::FrameAddress(BlockType block_type, bool is_bottom_half_rows,
uint8_t row, uint16_t column, uint8_t minor) {
address_ = bit_field_set(0, 25, 23, block_type);
address_ = bit_field_set(address_, 22, 22, is_bottom_half_rows);
address_ = bit_field_set(address_, 21, 17, row);
@ -17,26 +17,46 @@ ConfigurationFrameAddress::ConfigurationFrameAddress(
address_ = bit_field_set(address_, 6, 0, minor);
}
BlockType ConfigurationFrameAddress::block_type() const {
BlockType FrameAddress::block_type() const {
return static_cast<BlockType>(bit_field_get(address_, 25, 23));
}
bool ConfigurationFrameAddress::is_bottom_half_rows() const {
bool FrameAddress::is_bottom_half_rows() const {
return bit_field_get(address_, 22, 22);
}
uint8_t ConfigurationFrameAddress::row_address() const {
uint8_t FrameAddress::row_address() const {
return bit_field_get(address_, 21, 17);
}
uint16_t ConfigurationFrameAddress::column_address() const {
uint16_t FrameAddress::column_address() const {
return bit_field_get(address_, 16, 7);
}
uint8_t ConfigurationFrameAddress::minor_address() const {
uint8_t FrameAddress::minor_address() const {
return bit_field_get(address_, 6, 0);
}
std::ostream &operator<<(std::ostream &o, const FrameAddress& addr) {
o << "["
<< std::hex << std::showbase << std::setw(10)
<< static_cast<uint32_t>(addr)
<< "] "
<< (addr.is_bottom_half_rows() ? "BOTTOM" : "TOP")
<< " Row="
<< std::setw(2) << std::dec
<< static_cast<unsigned int>(addr.row_address())
<< " Column="
<< std::setw(2) << std::dec
<< addr.column_address()
<< " Minor="
<< std::setw(2) << std::dec
<< static_cast<unsigned int>(addr.minor_address())
<< " Type="
<< addr.block_type();
return o;
}
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
@ -45,10 +65,10 @@ namespace YAML {
namespace xc7series = prjxray::xilinx::xc7series;
Node convert<xc7series::ConfigurationFrameAddress>::encode(
const xc7series::ConfigurationFrameAddress &rhs) {
Node convert<xc7series::FrameAddress>::encode(
const xc7series::FrameAddress &rhs) {
Node node;
node.SetTag("xilinx/xc7series/configuration_frame_address");
node.SetTag("xilinx/xc7series/frame_address");
node["block_type"] = rhs.block_type();
node["row_half"] = (rhs.is_bottom_half_rows() ? "bottom" : "top");
node["row"] = static_cast<unsigned int>(rhs.row_address());
@ -57,9 +77,10 @@ Node convert<xc7series::ConfigurationFrameAddress>::encode(
return node;
}
bool convert<xc7series::ConfigurationFrameAddress>::decode(
const Node &node, xc7series::ConfigurationFrameAddress &lhs) {
if (node.Tag() != "xilinx/xc7series/configuration_frame_address" ||
bool convert<xc7series::FrameAddress>::decode(
const Node &node, xc7series::FrameAddress &lhs) {
if (!(node.Tag() == "xilinx/xc7series/frame_address" ||
node.Tag() == "xilinx/xc7series/configuration_frame_address") ||
!node["block_type"] ||
!node["row_half"] ||
!node["row"] ||
@ -75,7 +96,7 @@ bool convert<xc7series::ConfigurationFrameAddress>::decode(
return false;
}
lhs = prjxray::xilinx::xc7series::ConfigurationFrameAddress(
lhs = prjxray::xilinx::xc7series::FrameAddress(
node["block_type"].as<xc7series::BlockType>(),
row_half,
node["row"].as<unsigned int>(),

View File

@ -1,17 +1,16 @@
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <gtest/gtest.h>
namespace xc7series = prjxray::xilinx::xc7series;
TEST(ConfigurationFrameAddressTest, YamlEncode) {
xc7series::ConfigurationFrameAddress address(
xc7series::BlockType::BLOCK_RAM,
false, 10, 0, 5);
TEST(FrameAddressTest, YamlEncode) {
xc7series::FrameAddress address(xc7series::BlockType::BLOCK_RAM,
false, 10, 0, 5);
YAML::Node node(address);
EXPECT_EQ(node.Tag(), "xilinx/xc7series/configuration_frame_address");
EXPECT_EQ(node.Tag(), "xilinx/xc7series/frame_address");
EXPECT_EQ(node["block_type"].as<std::string>(), "BLOCK_RAM");
EXPECT_EQ(node["row_half"].as<std::string>(), "top");
EXPECT_EQ(node["row"].as<std::string>(), "10");
@ -19,17 +18,17 @@ TEST(ConfigurationFrameAddressTest, YamlEncode) {
EXPECT_EQ(node["minor"].as<std::string>(), "5");
}
TEST(ConfigurationFrameAddressTest, YamlDecode) {
TEST(FrameAddressTest, YamlDecode) {
YAML::Node node;
node.SetTag("xilinx/xc7series/configuration_frame_address");
node.SetTag("xilinx/xc7series/frame_address");
node["block_type"] = "BLOCK_RAM";
node["row_half"] = "bottom";
node["row"] = "0";
node["column"] = "5";
node["minor"] = "11";
xc7series::ConfigurationFrameAddress address =
node.as<xc7series::ConfigurationFrameAddress>();
xc7series::FrameAddress address =
node.as<xc7series::FrameAddress>();
EXPECT_EQ(address.block_type(), xc7series::BlockType::BLOCK_RAM);
EXPECT_TRUE(address.is_bottom_half_rows());
EXPECT_EQ(address.row_address(), 0);

View File

@ -16,16 +16,16 @@ absl::optional<Part> Part::FromFile(const std::string &path) {
}
}
absl::optional<ConfigurationFrameAddress>
Part::GetNextConfigurationFrameAddress(ConfigurationFrameAddress address) const {
absl::optional<FrameAddress>
Part::GetNextFrameAddress(FrameAddress address) const {
// Start with the next linear address.
ConfigurationFrameAddress target_address(address + 1);
FrameAddress target_address(address + 1);
// The address space is non-continguous. If the next linear address
// happens to fall in a valid range, that's the next address.
// Otherwise, find the closest valid range and use it's beginning
// address.
absl::optional<ConfigurationFrameAddress> closest_address;
absl::optional<FrameAddress> closest_address;
int32_t closest_distance;
for (auto iter = frame_ranges_.begin();
iter != frame_ranges_.end();

View File

@ -11,7 +11,7 @@ TEST(PartTest, GetNextAddressWhereNextIsInValidRange) {
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0x4);
auto next_address = part.GetNextFrameAddress(0x4);
EXPECT_TRUE(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x5), *next_address);
}
@ -23,7 +23,7 @@ TEST(PartTest, GetNextAddressWhereNextIsBetweenRanges) {
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0xF);
auto next_address = part.GetNextFrameAddress(0xF);
EXPECT_TRUE(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x20), *next_address);
}
@ -35,6 +35,6 @@ TEST(PartTest, GetNextAddressWhereNextWouldBePastLastRange) {
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0x2F);
auto next_address = part.GetNextFrameAddress(0x2F);
EXPECT_FALSE(next_address);
}

8
minitests/roi_harness/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/.Xil
/design/
/design.bit
/design.bits
/design.dcp
/usage_statistics_webtalk.*
/vivado*
/design.txt

View File

@ -0,0 +1,8 @@
all:
bash runme.sh
clean:
rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.segbits vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit design.txt .Xil
.PHONY: all clean

View File

@ -0,0 +1,12 @@
Creates an ROI with clk, inputs, and outputs to use as a partial reconfiguration test harness
Basic idea:
-LOC LUTs in the ROI to terminate input and output routing
-Let Vivado LOC the rest of the logic
-Manually route signals in and out of the ROI enough to avoid routing loops into the ROI
-Let Vivado finish the rest of the routes
There is no logic outside of the ROI in order to keep IOB to ROI delays short
Its expected the end user will rip out everything inside the ROI
To target Arty A7 you should source the artix DB environment script then source arty.sh

View File

@ -0,0 +1 @@
export XRAY_PART=xc7a35tcsg324-1

View File

@ -0,0 +1,6 @@
#!/bin/bash
set -ex
vivado -mode batch -source runme.tcl
test -z "$(fgrep CRITICAL vivado.log)"

View File

@ -0,0 +1,273 @@
# WARNING: this is somewhat paramaterized, but is only tested on A50T/A35T with the traditional ROI
# Your ROI should at least have a SLICEL on the left
set DIN_N 8
set DOUT_N 8
# X12 in the ROI, X10 just to the left
# Start at bottom left of ROI and work up
# (IOs are to left)
# SLICE_X12Y100:SLICE_X27Y149
# set X_BASE 12
set X_BASE [lindex [split [lindex [split "$::env(XRAY_ROI)" Y] 0] X] 1]
set Y_BASE [lindex [split [lindex [split "$::env(XRAY_ROI)" Y] 1] :] 0]
# set Y_DIN_BASE 100
set Y_CLK_BASE $Y_BASE
# Clock lut in middle
set Y_DIN_BASE [expr "$Y_CLK_BASE + 1"]
set Y_DOUT_BASE [expr "$Y_DIN_BASE + $DIN_N"]
puts "Environment"
puts " XRAY_ROI: $::env(XRAY_ROI)"
puts " X_BASE: $X_BASE"
puts " Y_DIN_BASE: $Y_DIN_BASE"
puts " Y_CLK_BASE: $Y_CLK_BASE"
puts " Y_DOUT_BASE: $Y_DOUT_BASE"
source ../../utils/utils.tcl
create_project -force -part $::env(XRAY_PART) design design
read_verilog top.v
# added flatten_hierarchy
# dout_shr was getting folded into the pblock
# synth_design -top top -flatten_hierarchy none -no_lc -keep_equivalent_registers -resource_sharing off
synth_design -top top -flatten_hierarchy none -verilog_define DIN_N=$DIN_N -verilog_define DOUT_N=$DOUT_N
# TODO: find a way to more automatically assign these?
# Sequential I/O Bank 16 layout
set part "$::env(XRAY_PART)"
if {$part eq "xc7a50tfgg484-1"} {
# Partial list, expand as needed
set bank_16 "F21 G22 G21 D21 E21 D22 E22 A21 B21 B22 C22 C20 D20 F20 F19 A19 A18"
set banki 0
# CLK
set pin [lindex $bank_16 $banki]
incr banki
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports "clk"]
# DIN
for {set j 0} {$j < $DIN_N} {incr j} {
set pin [lindex $bank_16 $banki]
incr banki
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports "din[$j]"]
}
# DOUT
for {set j 0} {$j < $DOUT_N} {incr j} {
set pin [lindex $bank_16 $banki]
incr banki
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports "dout[$j]"]
}
# Arty A7 optimized I/O layout
} elseif {$part eq "xc7a35tcsg324-1"} {
# https://reference.digilentinc.com/reference/programmable-logic/arty/reference-manual?redirect=1
set pmod_ja "G13 B11 A11 D12 D13 B18 A18 K16"
set pmod_jb "E15 E16 D15 C15 J17 J18 K15 J15"
set pmod_jc "U12 V12 V10 V11 U14 V14 T13 U13"
# CLK on Pmod JA
set pin [lindex $pmod_ja 0]
set_property -dict "PACKAGE_PIN G13 IOSTANDARD LVCMOS33" [get_ports "clk"]
# DIN on Pmod JB
for {set i 0} {$i < $DIN_N} {incr i} {
set pin [lindex $pmod_jb $i]
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports "din[$i]"]
}
# DOUT on Pmod JC
for {set i 0} {$i < $DOUT_N} {incr i} {
set pin [lindex $pmod_jc $i]
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports "dout[$i]"]
}
} else {
error "Unsupported part $part"
}
create_pblock roi
set_property EXCLUDE_PLACEMENT 1 [get_pblocks roi]
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
#write_checkpoint -force synth.dcp
proc loc_roi_clk_left {ff_x ff_y} {
# Place an ROI clk on the left edge of the ROI
# It doesn't actually matter where we place this, just do it to keep things neat looking
# ff_x: ROI SLICE X position
# ff_yy: row primitives will be placed at
set slice_ff "SLICE_X${ff_x}Y${ff_y}"
# Fix FFs to guide route in
set cell [get_cells "roi/clk_reg_reg"]
set_property LOC $slice_ff $cell
set_property BEL AFF $cell
}
proc loc_roi_in_left {index lut_x y} {
# Place an ROI input on the left edge of the ROI
# index: input bus index
# lut_x: ROI SLICE X position. FF position is implicit to left
# y: row primitives will be placed at
set slice_lut "SLICE_X${lut_x}Y${y}"
# Fix LUTs near the edge
set cell [get_cells "roi/ins[$index].lut"]
set_property LOC $slice_lut $cell
set_property BEL A6LUT $cell
}
proc loc_roi_out_left {index lut_x y} {
# Place an ROI output on the left edge of the ROI
# index: input bus index
# lut_x: ROI SLICE X position. FF position is implicit to left
# y: row primitives will be placed at
set slice_lut "SLICE_X${lut_x}Y${y}"
# Fix LUTs near the edge
set cell [get_cells "roi/outs[$index].lut"]
set_property LOC $slice_lut $cell
set_property BEL A6LUT $cell
}
if {1} {
set x $X_BASE
# Place ROI clock right after inputs
puts "Placing ROI clock"
loc_roi_clk_left $x $Y_CLK_BASE
# Place ROI inputs
puts "Placing ROI inputs"
set y $Y_DIN_BASE
for {set i 0} {$i < $DIN_N} {incr i} {
loc_roi_in_left $i $x $y
set y [expr {$y + 1}]
}
# Place ROI outputs
set y $Y_DOUT_BASE
puts "Placing ROI outputs"
for {set i 0} {$i < $DOUT_N} {incr i} {
loc_roi_out_left $i $x $y
set y [expr {$y + 1}]
}
}
place_design
#write_checkpoint -force placed.dcp
# Version with more error checking for missing end node
# Will do best effort in this case
proc route_via2 {net nodes} {
# net: net as string
# nodes: string list of one or more intermediate routing nodes to visit
set net [get_nets $net]
# Start at the net source
set fixed_route [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]]
# End at the net destination
# For sone reason this doesn't always show up
set site_pins [get_site_pins -filter {DIRECTION == IN} -of_objects $net]
if {$site_pins eq ""} {
puts "WARNING: could not find end node"
#error "Could not find end node"
} else {
set end_node [get_nodes -of_objects]
lappend nodes [$end_node]
}
puts ""
puts "Routing net $net:"
foreach to_node $nodes {
if {$to_node eq ""} {
error "Empty node"
}
# Node string to object
set to_node [get_nodes -of_objects [get_wires $to_node]]
# Start at last routed position
set from_node [lindex $fixed_route end]
# Let vivado do heavy liftin in between
set route [find_routing_path -quiet -from $from_node -to $to_node]
if {$route == ""} {
# Some errors print a huge route
puts [concat [string range " $from_node -> $to_node" 0 1000] ": no route found - assuming direct PIP"]
lappend fixed_route $to_node
} {
puts [concat [string range " $from_node -> $to_node: $route" 0 1000] "routed"]
set fixed_route [concat $fixed_route [lrange $route 1 end]]
}
set_property -quiet FIXED_ROUTE $fixed_route $net
}
set_property -quiet FIXED_ROUTE $fixed_route $net
puts ""
}
# XXX: maybe add IOB?
set fp [open "design.txt" w]
puts $fp "name node"
if {1} {
set x $X_BASE
# Nothing needed for clk
# It will go to high level interconnect that goes everywhere
puts "Routing ROI inputs"
# Arbitrary offset as observed
set y [expr {$Y_DIN_BASE - 1}]
for {set i 0} {$i < $DIN_N} {incr i} {
#route_via2 "din_IBUF[$i]" "INT_R_X9Y${y}/NE2BEG3"
# needed to force routes away to avoid looping into ROI
#set x_EE2BEG3 [expr {$x - 2}]
set x_EE2BEG3 7
set x_NE2BEG3 9
set node "INT_R_X${x_NE2BEG3}Y${y}/NE2BEG3"
route_via2 "din_IBUF[$i]" "INT_R_X${x_EE2BEG3}Y${y}/EE2BEG3 $node"
puts $fp "din[$i] $node"
set y [expr {$y + 1}]
}
puts "Routing ROI outputs"
# Arbitrary offset as observed
set y [expr {$Y_DOUT_BASE + 0}]
for {set i 0} {$i < $DOUT_N} {incr i} {
# XXX: find a better solution if we need harness long term
# works on 50t but not 35t
if {$part eq "xc7a50tfgg484-1"} {
set node "INT_L_X10Y${y}/WW2BEG0"
route_via2 "roi/dout[$i]" "$node"
# works on 35t but not 50t
} elseif {$part eq "xc7a35tcsg324-1"} {
set node "INT_L_X10Y${y}/SW6BEG0"
route_via2 "roi/dout[$i]" "$node"
} else {
error "Unsupported part $part"
}
puts $fp "dout[$i] $node"
set y [expr {$y + 1}]
}
}
close $fp
puts "routing design"
route_design
write_checkpoint -force design.dcp
write_bitstream -force design.bit

View File

@ -0,0 +1,65 @@
//See README and tcl for more info
`ifndef DIN_N
`define DIN_N 4
`endif
`ifndef DOUT_N
`define DOUT_N 4
`endif
module top(input wire clk,
input [DIN_N-1:0] din, output [DOUT_N-1:0] dout);
parameter DIN_N = `DIN_N;
parameter DOUT_N = `DOUT_N;
roi #(.DIN_N(DIN_N), .DOUT_N(DOUT_N)) roi (
.clk(clk),
.din(din), .dout(dout));
endmodule
module roi(input clk,
input [DIN_N-1:0] din, output [DOUT_N-1:0] dout);
parameter DIN_N = 4;
parameter DOUT_N = 4;
genvar i;
generate
//CLK
(* KEEP, DONT_TOUCH *)
reg clk_reg;
always @(posedge clk) begin
clk_reg <= clk_reg;
end
//DIN
for (i = 0; i < DIN_N; i = i+1) begin:ins
(* KEEP, DONT_TOUCH *)
LUT6 #(
.INIT(64'b01)
) lut (
.I0(din[i]),
.I1(1'b0),
.I2(1'b0),
.I3(1'b0),
.I4(1'b0),
.I5(1'b0),
.O());
end
//DOUT
for (i = 0; i < DOUT_N; i = i+1) begin:outs
(* KEEP, DONT_TOUCH *)
LUT6 #(
.INIT(64'b01)
) lut (
.I0(1'b0),
.I1(1'b0),
.I2(1'b0),
.I3(1'b0),
.I4(1'b0),
.I5(1'b0),
.O(dout[i]));
end
endgenerate
endmodule

View File

@ -78,10 +78,8 @@ int main(int argc, char **argv) {
std::cout << "Bitstream size: " << in_file->size() << " bytes"
<< std::endl;
auto in_bytes = absl::Span<uint8_t>(
static_cast<uint8_t*>(in_file->data()),
in_file->size());
reader = xc7series::BitstreamReader::InitWithBytes(in_bytes);
reader = xc7series::BitstreamReader::InitWithBytes(
in_file->as_bytes());
} else {
std::vector<uint8_t> bitdata;
while (1) {

View File

@ -31,10 +31,8 @@ int ListConfigPackets(int argc, char *argv[]) {
return 1;
}
auto in_bytes = absl::Span<uint8_t>(
static_cast<uint8_t*>(in_file->data()),
in_file->size());
auto reader = xc7series::BitstreamReader::InitWithBytes(in_bytes);
auto reader = xc7series::BitstreamReader::InitWithBytes(
in_file->as_bytes());
if (!reader) {
std::cerr << "Input doesn't look like a bitstream"
<< std::endl;

View File

@ -2,7 +2,7 @@
#include <iomanip>
#include <iostream>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
namespace xc7series = prjxray::xilinx::xc7series;
@ -18,7 +18,7 @@ int main(int argc, char *argv[]) {
for (uint32_t frame_address_raw;
(*input_stream) >> std::setbase(0) >> frame_address_raw;
) {
xc7series::ConfigurationFrameAddress frame_address(frame_address_raw);
xc7series::FrameAddress frame_address(frame_address_raw);
std::cout << "["
<< std::hex << std::showbase << std::setw(10)
<< frame_address_raw

View File

@ -7,7 +7,6 @@
#include <absl/types/span.h>
#include <prjxray/memory_mapped_file.h>
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
#include <prjxray/xilinx/xc7series/part.h>
#include <yaml-cpp/yaml.h>
@ -31,10 +30,8 @@ int main(int argc, char *argv[]) {
return 1;
}
auto in_bytes = absl::Span<uint8_t>(
static_cast<uint8_t*>(in_file->data()),
in_file->size());
auto reader = xc7series::BitstreamReader::InitWithBytes(in_bytes);
auto reader = xc7series::BitstreamReader::InitWithBytes(
in_file->as_bytes());
if (!reader) {
std::cerr << "Input doesn't look like a bitstream"
<< std::endl;