Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial bitstream support #1492

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/include/prjxray/xilinx/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ class Configuration {
const PacketData& packet_data,
absl::optional<typename ArchType::Part>& part);

// Creates the configuration package for partial configuration
// which is later on used by the bitstream writer to generate
// the partial bitstream file.
static void createPartialConfigurationPackage(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is missing some important information. For example why is far_address even an argument if PacketData contains the far_address. Does this function work if PacketData spans multiple frames? If PacketData spans multiple frames, what is the right thing to do?

Copy link

@kowalewskijan kowalewskijan Nov 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some more changes I've pushed recently:

  • I removed the far_address from arguments because we should handle the FAR addreses for CLB_IO_CLK blocks and BRAMs separately, in the result we pass a map with data and address for them. This step is handled here and later we use this data to generate proper FAR/FDRI for each of these block types here.
  • I added a new definition of addMissingFrames function to handle address gaps in a partial reconfiguration region.
  • To handle the PR region correctly CLB_IO_CLK and BRAM address ranges should be defined as arguments to xc7frames2bit

typename ArchType::ConfigurationPackage& out_packets,
std::map<uint32_t, PacketData> cfg_packets,
absl::optional<typename ArchType::Part>& part);

// Returns the payload for a type 2 packet
// which allows for bigger payload compared to type 1.
static PacketData createType2ConfigurationPacketData(
Expand Down
45 changes: 45 additions & 0 deletions lib/include/prjxray/xilinx/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ class Frames {
void addMissingFrames(
const absl::optional<typename ArchType::Part>& part);

// Adds empty frames that are present in provided address ranges
// of a specific part. This version is used for filling missing frames
// in Partial Reconfiguration Region if there are address gaps.
// clb_*_addr - address range for CLB_IO_CLK block type
// bram_*_addr - address range for BRAM block type
void addMissingFrames(
const absl::optional<typename ArchType::Part>& part,
uint32_t clb_start_addr,
uint32_t clb_end_addr,
uint32_t bram_start_addr,
uint32_t bram_end_addr);

// Returns the map with frame addresses and corresponding data
Frames2Data& getFrames() { return frames_data_; }

Expand Down Expand Up @@ -128,6 +140,39 @@ void Frames<ArchType>::addMissingFrames(
} while (current_frame_address);
}

template <typename ArchType>
void Frames<ArchType>::addMissingFrames(
const absl::optional<typename ArchType::Part>& part,
uint32_t clb_start_addr,
uint32_t clb_end_addr,
uint32_t bram_start_addr,
uint32_t bram_end_addr) {
// CLB_IO_CLK block types are always on lower addresses,
// the region starts there and ends on BRAM addresses.
auto current_frame_address =
absl::optional<typename ArchType::FrameAddress>(
typename ArchType::FrameAddress((clb_start_addr)));
auto end_frame_address =
absl::optional<typename ArchType::FrameAddress>(
typename ArchType::FrameAddress(bram_end_addr));
do {
auto iter = frames_data_.find(*current_frame_address);
if (iter == frames_data_.end() &&
((*current_frame_address >= clb_start_addr &&
*current_frame_address <= clb_end_addr) ||
(*current_frame_address >= bram_start_addr &&
*current_frame_address <= bram_end_addr))) {
FrameData frame_data(ArchType::words_per_frame, 0);
frames_data_.insert(
std::pair<typename ArchType::FrameAddress,
FrameData>(*current_frame_address,
frame_data));
}
current_frame_address =
part->GetNextFrameAddress(*current_frame_address);
} while (*current_frame_address <= *end_frame_address);
}

} // namespace xilinx
} // namespace prjxray

Expand Down
132 changes: 132 additions & 0 deletions lib/xilinx/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ Configuration<Spartan6>::createType2ConfigurationPacketData(
return packet_data;
}

template <>
void Configuration<Spartan6>::createPartialConfigurationPackage(
Spartan6::ConfigurationPackage& out_packets,
std::map<uint32_t, PacketData> cfg_packets,
absl::optional<Spartan6::Part>& part) {
throw std::runtime_error(
"Partial bitstream generation for this architecture is not "
"supported!");
}

template <>
void Configuration<Spartan6>::createConfigurationPackage(
Spartan6::ConfigurationPackage& out_packets,
Expand Down Expand Up @@ -297,6 +307,108 @@ void Configuration<Spartan6>::createConfigurationPackage(
}
}

template <>
void Configuration<Series7>::createPartialConfigurationPackage(
Series7::ConfigurationPackage& out_packets,
std::map<uint32_t, PacketData> cfg_packets,
absl::optional<Series7::Part>& part) {
using ArchType = Series7;
using ConfigurationRegister = ArchType::ConfRegType;
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::RCRC)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());

out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::IDCODE, {part->idcode()}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::NOP)}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x100}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL0, {0x100}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x400}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL0, {0x400}));

out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::WCFG)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());

// Generate data packets for each address space
std::map<uint32_t, PacketData>::iterator it;
for (auto& it : cfg_packets) {
out_packets.emplace_back(new ConfigurationPacketWithPayload<
1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FAR, {it.first}));
out_packets.emplace_back(
new NopPacket<ConfigurationRegister>());

out_packets.emplace_back(new ConfigurationPacket<
ConfigurationRegister>(
TYPE1,
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FDRI, {}));
out_packets.emplace_back(new ConfigurationPacket<
ConfigurationRegister>(
TYPE2,
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FDRI, it.second));
}

// Finalization sequence
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::MASK, {0x100}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CTL0, {0x0}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::FAR, {0x3be0000}));
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::RCRC)}));
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
out_packets.emplace_back(
new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
ConfigurationRegister::CMD,
{static_cast<uint32_t>(xc7series::Command::DESYNC)}));
for (int ii = 0; ii < 400; ++ii) {
out_packets.emplace_back(
new NopPacket<ConfigurationRegister>());
}
}

template <>
void Configuration<Series7>::createConfigurationPackage(
Series7::ConfigurationPackage& out_packets,
Expand Down Expand Up @@ -469,6 +581,16 @@ void Configuration<Series7>::createConfigurationPackage(
}
}

template <>
void Configuration<UltraScale>::createPartialConfigurationPackage(
UltraScale::ConfigurationPackage& out_packets,
std::map<uint32_t, PacketData> cfg_packets,
absl::optional<UltraScale::Part>& part) {
throw std::runtime_error(
"Partial bitstream generation for this architecture is not "
"supported!");
}

template <>
void Configuration<UltraScale>::createConfigurationPackage(
UltraScale::ConfigurationPackage& out_packets,
Expand Down Expand Up @@ -630,6 +752,16 @@ void Configuration<UltraScale>::createConfigurationPackage(
}
}

template <>
void Configuration<UltraScalePlus>::createPartialConfigurationPackage(
UltraScalePlus::ConfigurationPackage& out_packets,
std::map<uint32_t, PacketData> cfg_packets,
absl::optional<UltraScalePlus::Part>& part) {
throw std::runtime_error(
"Partial bitstream generation for this architecture is not "
"supported!");
}

template <>
void Configuration<UltraScalePlus>::createConfigurationPackage(
UltraScalePlus::ConfigurationPackage& out_packets,
Expand Down
118 changes: 118 additions & 0 deletions lib/xilinx/tests/xc7series/configuration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,121 @@ TEST(ConfigurationTest, CheckForPaddingFrames) {
EXPECT_EQ(frame.second, frames.getFrames().at(frame.first));
}
}

TEST(ConfigurationTest, CreatePartialConfigurationPackage) {
namespace xilinx = prjxray::xilinx;
std::vector<xc7series::FrameAddress> test_part_addresses = {
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
0, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
1, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
2, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
3, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
4, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
5, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
6, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
7, 0),
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
8, 0),
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
0, 0),
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
1, 0),
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
2, 0),
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
3, 0),
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
4, 0)};

auto test_part = absl::optional<xc7series::Part>(
xc7series::Part(0x1234, test_part_addresses));

// There are CLB_IO_CLK blocks in address space:
// <0x100,0x200> plus <0x380,0x400> and BRAM blocks at:
// <0x800000> plus <0x800180>. We create address gaps which should be
// filled with zero frames and the final configuration package should
// have two data packets separated with two different FAR addresses: one
// for CLB_IO_CLK=<0x100> and second for BRAM=<0x800000>
const uint32_t clb_start_address = 0x100;
const uint32_t clb_end_address = 0x400;
const uint32_t bram_start_address = 0x800000;
const uint32_t bram_end_address = 0x800180;
Frames<Series7> frames;
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(2), std::vector<uint32_t>(101, 0xAA)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(3), std::vector<uint32_t>(101, 0xBB)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(4), std::vector<uint32_t>(101, 0xCC)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(7), std::vector<uint32_t>(101, 0xDD)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(8), std::vector<uint32_t>(101, 0xEE)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(9), std::vector<uint32_t>(101, 0xAB)));
frames.getFrames().emplace(std::make_pair(
test_part_addresses.at(12), std::vector<uint32_t>(101, 0xBC)));
ASSERT_EQ(frames.getFrames().size(), 7);

// Fill address gaps with zero frames
frames.addMissingFrames(test_part, clb_start_address, clb_end_address,
bram_start_address, bram_end_address);

// Split frames into separate CLB_IO_CLK and BRAM related
typename Frames<Series7>::Frames2Data clb_frames, bram_frames;
clb_frames.insert(frames.getFrames().find(clb_start_address),
frames.getFrames().find(clb_end_address));
bram_frames.insert(frames.getFrames().find(bram_start_address),
frames.getFrames().find(bram_end_address));
// Check if all columns are present. We expect to have <2,7> columns
// for CLB_IO_CLK blocks and <0,2> for BRAM blocks.
uint8_t clb_index = clb_frames.begin()->first.column();
for (auto frm : clb_frames) {
ASSERT_EQ(frm.first.column(), clb_index);
clb_index++;
}
uint8_t bram_index = bram_frames.begin()->first.column();
for (auto frm : bram_frames) {
ASSERT_EQ(frm.first.column(), bram_index);
bram_index++;
}

Configuration<Series7>::PacketData clb_packet_data =
Configuration<Series7>::createType2ConfigurationPacketData(
clb_frames, test_part);
Configuration<Series7>::PacketData bram_packet_data =
Configuration<Series7>::createType2ConfigurationPacketData(
bram_frames, test_part);

std::map<uint32_t, typename xilinx::Configuration<Series7>::PacketData>
cfg_packets = {{clb_start_address, clb_packet_data},
{bram_start_address, bram_packet_data}};

Series7::ConfigurationPackage partial_configuration_package;
xilinx::Configuration<Series7>::createPartialConfigurationPackage(
partial_configuration_package, cfg_packets, test_part);

// Partial bitstream should have a separate FAR address setup command
// for CLB_IO_CLK and BRAM.
bool clb_far_address = false;
for (auto& cfg : partial_configuration_package) {
if (cfg.get()->address() == Series7ConfigurationRegister::FAR) {
if (!clb_far_address) {
EXPECT_EQ(cfg.get()->data()[0],
clb_start_address);
clb_far_address = true;
} else {
EXPECT_EQ(cfg.get()->data()[0],
bram_start_address);
break;
}
}
}
}
Loading