mirror of https://github.com/openXC7/prjxray.git
xc7frames2bit: implement tool for xilinx 7-series bitstream generation from frames
Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
parent
9a8ff7e360
commit
2aa3c30976
|
|
@ -14,9 +14,11 @@ add_library(libprjxray
|
||||||
xilinx/xc7series/global_clock_region.cc
|
xilinx/xc7series/global_clock_region.cc
|
||||||
xilinx/xc7series/part.cc
|
xilinx/xc7series/part.cc
|
||||||
xilinx/xc7series/row.cc
|
xilinx/xc7series/row.cc
|
||||||
|
xilinx/xc7series/frames.cc
|
||||||
|
xilinx/xc7series/utils.cc
|
||||||
)
|
)
|
||||||
target_include_directories(libprjxray PUBLIC "include")
|
target_include_directories(libprjxray PUBLIC "include")
|
||||||
target_link_libraries(libprjxray absl::optional absl::strings absl::span yaml-cpp)
|
target_link_libraries(libprjxray absl::optional absl::strings absl::span absl::time yaml-cpp)
|
||||||
|
|
||||||
if (PRJXRAY_BUILD_TESTING)
|
if (PRJXRAY_BUILD_TESTING)
|
||||||
add_executable(big_endian_span_test big_endian_span_test.cc)
|
add_executable(big_endian_span_test big_endian_span_test.cc)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_FRAMES_H
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_FRAMES_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <absl/strings/str_split.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/part.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
class Frames {
|
||||||
|
public:
|
||||||
|
typedef std::vector<uint32_t> FrameData;
|
||||||
|
typedef std::map<FrameAddress, FrameData> Frames2Data;
|
||||||
|
|
||||||
|
int readFrames(const std::string& frm_file_str);
|
||||||
|
void addMissingFrames(const absl::optional<Part>& part);
|
||||||
|
Frames2Data& getFrames();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Frames2Data frames_data_;
|
||||||
|
|
||||||
|
void updateECC(FrameData& data);
|
||||||
|
uint32_t calculateECC(const FrameData& data);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_FRAMES_H
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_UTILS_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_UTILS_H_
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_packet.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frames.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/part.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
using PacketData = std::vector<uint32_t>;
|
||||||
|
using BitstreamHeader = std::vector<uint8_t>;
|
||||||
|
using ConfigurationPackage = std::vector<std::unique_ptr<ConfigurationPacket>>;
|
||||||
|
|
||||||
|
PacketData createType2ConfigurationPacketData(const Frames::Frames2Data& frames,
|
||||||
|
absl::optional<Part>& part);
|
||||||
|
void createConfigurationPackage(ConfigurationPackage& out_packets,
|
||||||
|
const PacketData& packet_data,
|
||||||
|
absl::optional<Part>& part);
|
||||||
|
BitstreamHeader createBitistreamHeader(const std::string& part_name,
|
||||||
|
const std::string& frames_file_name,
|
||||||
|
const std::string& generator_name);
|
||||||
|
int writeBitstream(const ConfigurationPackage& packets,
|
||||||
|
const std::string& part_name,
|
||||||
|
const std::string& frames_file,
|
||||||
|
const std::string& generator_name,
|
||||||
|
const std::string& output_file);
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_UTILS_H_
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/xc7series/ecc.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frames.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
Frames::Frames2Data& Frames::getFrames() {
|
||||||
|
return frames_data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Frames::readFrames(const std::string& frm_file_str) {
|
||||||
|
assert(!frm_file_str.empty());
|
||||||
|
|
||||||
|
std::ifstream frm_file(frm_file_str);
|
||||||
|
if (!frm_file) {
|
||||||
|
std::cerr << "Unable to open frm file: " << frm_file_str
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string frm_line;
|
||||||
|
|
||||||
|
while (std::getline(frm_file, frm_line)) {
|
||||||
|
if (frm_line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> frame_delta =
|
||||||
|
absl::StrSplit(frm_line, ' ');
|
||||||
|
|
||||||
|
uint32_t frame_address =
|
||||||
|
std::stoul(frame_delta.first, nullptr, 16);
|
||||||
|
|
||||||
|
std::vector<std::string> frame_data_strings =
|
||||||
|
absl::StrSplit(frame_delta.second, ',');
|
||||||
|
if (frame_data_strings.size() != 101) {
|
||||||
|
std::cerr << "Frame " << std::hex << frame_address
|
||||||
|
<< ": found " << std::dec
|
||||||
|
<< frame_data_strings.size()
|
||||||
|
<< "words instead of 101";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameData frame_data(101, 0);
|
||||||
|
std::transform(frame_data_strings.begin(),
|
||||||
|
frame_data_strings.end(), frame_data.begin(),
|
||||||
|
[](const std::string& val) -> uint32_t {
|
||||||
|
return std::stoul(val, nullptr, 16);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO make sure if this is needed
|
||||||
|
updateECC(frame_data);
|
||||||
|
|
||||||
|
// Insert the frame address and corresponding frame data to the
|
||||||
|
// map
|
||||||
|
FrameAddress frm_addr(frame_address);
|
||||||
|
frames_data_.insert(
|
||||||
|
std::pair<FrameAddress, FrameData>(frm_addr, frame_data));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frames::addMissingFrames(const absl::optional<Part>& part) {
|
||||||
|
auto current_frame_address = frames_data_.begin()->first;
|
||||||
|
auto next_frame_address =
|
||||||
|
part->GetNextFrameAddress(current_frame_address);
|
||||||
|
while (next_frame_address) {
|
||||||
|
current_frame_address = *next_frame_address;
|
||||||
|
auto iter = frames_data_.find(current_frame_address);
|
||||||
|
if (iter == frames_data_.end()) {
|
||||||
|
FrameData frame_data(101, 0);
|
||||||
|
// TODO make sure if this is needed
|
||||||
|
updateECC(frame_data);
|
||||||
|
frames_data_.insert(std::pair<FrameAddress, FrameData>(
|
||||||
|
current_frame_address, frame_data));
|
||||||
|
}
|
||||||
|
next_frame_address =
|
||||||
|
part->GetNextFrameAddress(current_frame_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frames::updateECC(FrameData& data) {
|
||||||
|
assert(data.size() != 0);
|
||||||
|
// Replace the old ECC with the new.
|
||||||
|
data[0x32] &= 0xFFFFE000;
|
||||||
|
data[0x32] |= (calculateECC(data) & 0x1FFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Frames::calculateECC(const FrameData& data) {
|
||||||
|
uint32_t ecc = 0;
|
||||||
|
for (size_t ii = 0; ii < data.size(); ++ii) {
|
||||||
|
ecc = icap_ecc(ii, data[ii], ecc);
|
||||||
|
}
|
||||||
|
return ecc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <absl/strings/str_cat.h>
|
||||||
|
#include <absl/strings/str_split.h>
|
||||||
|
#include <absl/time/clock.h>
|
||||||
|
#include <absl/time/time.h>
|
||||||
|
//#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
#include <prjxray/xilinx/xc7series/bitstream_writer.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/command.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_options_0_value.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_packet_with_payload.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/nop_packet.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/utils.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
PacketData createType2ConfigurationPacketData(const Frames::Frames2Data& frames,
|
||||||
|
absl::optional<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));
|
||||||
|
|
||||||
|
auto next_address = part->GetNextFrameAddress(frame.first);
|
||||||
|
if (next_address &&
|
||||||
|
(next_address->block_type() != frame.first.block_type() ||
|
||||||
|
next_address->is_bottom_half_rows() !=
|
||||||
|
frame.first.is_bottom_half_rows() ||
|
||||||
|
next_address->row() != frame.first.row())) {
|
||||||
|
packet_data.insert(packet_data.end(), 202, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packet_data.insert(packet_data.end(), 202, 0);
|
||||||
|
return packet_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createConfigurationPackage(ConfigurationPackage& out_packets,
|
||||||
|
const PacketData& packet_data,
|
||||||
|
absl::optional<Part>& part) {
|
||||||
|
// The programming sequence is taken from
|
||||||
|
// https://www.kc8apf.net/2018/05/unpacking-xilinx-7-series-bitstreams-part-2/
|
||||||
|
// Initialization sequence
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::TIMER,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::WBSTAR,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::NOP)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::RCRC)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::UNKNOWN,
|
||||||
|
{0x0}));
|
||||||
|
|
||||||
|
// Configuration Options 0
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::COR0,
|
||||||
|
{ConfigurationOptions0Value()
|
||||||
|
.SetAddPipelineStageForDoneIn(true)
|
||||||
|
.SetReleaseDonePinAtStartupCycle(
|
||||||
|
ConfigurationOptions0Value::SignalReleaseCycle::Phase4)
|
||||||
|
.SetStallAtStartupCycleUntilDciMatch(
|
||||||
|
ConfigurationOptions0Value::StallCycle::NoWait)
|
||||||
|
.SetStallAtStartupCycleUntilMmcmLock(
|
||||||
|
ConfigurationOptions0Value::StallCycle::NoWait)
|
||||||
|
.SetReleaseGtsSignalAtStartupCycle(
|
||||||
|
ConfigurationOptions0Value::SignalReleaseCycle::Phase5)
|
||||||
|
.SetReleaseGweSignalAtStartupCycle(
|
||||||
|
ConfigurationOptions0Value::SignalReleaseCycle::Phase6)}));
|
||||||
|
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::COR1,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::IDCODE,
|
||||||
|
{part->idcode()}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::SWITCH)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::MASK,
|
||||||
|
{0x401}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CTL0,
|
||||||
|
{0x501}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::MASK,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CTL1,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::FAR,
|
||||||
|
{0x0}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::WCFG)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
|
||||||
|
// Frame data write
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacket(1, ConfigurationPacket::Opcode::Write,
|
||||||
|
ConfigurationRegister::FDRI, {}));
|
||||||
|
out_packets.emplace_back(
|
||||||
|
new ConfigurationPacket(2, ConfigurationPacket::Opcode::Write,
|
||||||
|
ConfigurationRegister::FDRI, packet_data));
|
||||||
|
|
||||||
|
// Finalization sequence
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::RCRC)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::GRESTORE)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::LFRM)}));
|
||||||
|
for (int ii = 0; ii < 100; ++ii) {
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
}
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::START)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::FAR,
|
||||||
|
{0x3be0000}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::MASK,
|
||||||
|
{0x501}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CTL0,
|
||||||
|
{0x501}));
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::RCRC)}));
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
out_packets.emplace_back(new ConfigurationPacketWithPayload<1>(
|
||||||
|
ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD,
|
||||||
|
{static_cast<uint32_t>(Command::DESYNC)}));
|
||||||
|
for (int ii = 0; ii < 400; ++ii) {
|
||||||
|
out_packets.emplace_back(new NopPacket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xilinx BIT header
|
||||||
|
BitstreamHeader createBitstreamHeader(const std::string& part_name,
|
||||||
|
const std::string& frames_file_name,
|
||||||
|
const std::string& generator_name) {
|
||||||
|
// Sync header
|
||||||
|
BitstreamHeader bit_header{0x0, 0x9, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
|
||||||
|
0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 'a'};
|
||||||
|
auto build_source =
|
||||||
|
absl::StrCat(frames_file_name, ";Generator=" + generator_name);
|
||||||
|
bit_header.push_back(
|
||||||
|
static_cast<uint8_t>((build_source.size() + 1) >> 8));
|
||||||
|
bit_header.push_back(static_cast<uint8_t>(build_source.size() + 1));
|
||||||
|
bit_header.insert(bit_header.end(), build_source.begin(),
|
||||||
|
build_source.end());
|
||||||
|
bit_header.push_back(0x0);
|
||||||
|
|
||||||
|
// Source file.
|
||||||
|
bit_header.push_back('b');
|
||||||
|
bit_header.push_back(static_cast<uint8_t>((part_name.size() + 1) >> 8));
|
||||||
|
bit_header.push_back(static_cast<uint8_t>(part_name.size() + 1));
|
||||||
|
bit_header.insert(bit_header.end(), part_name.begin(), part_name.end());
|
||||||
|
bit_header.push_back(0x0);
|
||||||
|
|
||||||
|
// Build timestamp.
|
||||||
|
auto build_time = absl::Now();
|
||||||
|
auto build_date_string =
|
||||||
|
absl::FormatTime("%E4Y/%m/%d", build_time, absl::UTCTimeZone());
|
||||||
|
auto build_time_string =
|
||||||
|
absl::FormatTime("%H:%M:%S", build_time, absl::UTCTimeZone());
|
||||||
|
|
||||||
|
bit_header.push_back('c');
|
||||||
|
bit_header.push_back(
|
||||||
|
static_cast<uint8_t>((build_date_string.size() + 1) >> 8));
|
||||||
|
bit_header.push_back(
|
||||||
|
static_cast<uint8_t>(build_date_string.size() + 1));
|
||||||
|
bit_header.insert(bit_header.end(), build_date_string.begin(),
|
||||||
|
build_date_string.end());
|
||||||
|
bit_header.push_back(0x0);
|
||||||
|
|
||||||
|
bit_header.push_back('d');
|
||||||
|
bit_header.push_back(
|
||||||
|
static_cast<uint8_t>((build_time_string.size() + 1) >> 8));
|
||||||
|
bit_header.push_back(
|
||||||
|
static_cast<uint8_t>(build_time_string.size() + 1));
|
||||||
|
bit_header.insert(bit_header.end(), build_time_string.begin(),
|
||||||
|
build_time_string.end());
|
||||||
|
bit_header.push_back(0x0);
|
||||||
|
|
||||||
|
bit_header.insert(bit_header.end(), {'e', 0x0, 0x0, 0x0, 0x0});
|
||||||
|
|
||||||
|
return bit_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
int writeBitstream(const ConfigurationPackage& packets,
|
||||||
|
const std::string& part_name,
|
||||||
|
const std::string& frames_file,
|
||||||
|
const std::string& generator_name,
|
||||||
|
const std::string& output_file) {
|
||||||
|
std::ofstream out_file(output_file, std::ofstream::binary);
|
||||||
|
if (!out_file) {
|
||||||
|
std::cerr << "Unable to open file for writting: " << output_file
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitstreamHeader bit_header(
|
||||||
|
createBitstreamHeader(part_name, frames_file, generator_name));
|
||||||
|
out_file.write(reinterpret_cast<const char*>(bit_header.data()),
|
||||||
|
bit_header.size());
|
||||||
|
|
||||||
|
auto end_of_header_pos = out_file.tellp();
|
||||||
|
auto header_data_length_pos =
|
||||||
|
end_of_header_pos - static_cast<std::ofstream::off_type>(4);
|
||||||
|
|
||||||
|
BitstreamWriter out_bitstream_writer(packets);
|
||||||
|
for (uint32_t word : out_bitstream_writer) {
|
||||||
|
out_file.put((word >> 24) & 0xFF);
|
||||||
|
out_file.put((word >> 16) & 0xFF);
|
||||||
|
out_file.put((word >> 8) & 0xFF);
|
||||||
|
out_file.put((word)&0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t length_of_data = out_file.tellp() - end_of_header_pos;
|
||||||
|
|
||||||
|
out_file.seekp(header_data_length_pos);
|
||||||
|
out_file.put((length_of_data >> 24) & 0xFF);
|
||||||
|
out_file.put((length_of_data >> 16) & 0xFF);
|
||||||
|
out_file.put((length_of_data >> 8) & 0xFF);
|
||||||
|
out_file.put((length_of_data)&0xFF);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
@ -25,3 +25,10 @@ target_link_libraries(xc7patch
|
||||||
gflags
|
gflags
|
||||||
libprjxray
|
libprjxray
|
||||||
)
|
)
|
||||||
|
add_executable(xc7frames2bit xc7frames2bit.cc)
|
||||||
|
target_link_libraries(xc7frames2bit
|
||||||
|
absl::strings
|
||||||
|
absl::time
|
||||||
|
gflags
|
||||||
|
libprjxray
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/utils.h>
|
||||||
|
|
||||||
|
DEFINE_string(part_name, "", "Name of the 7-series part");
|
||||||
|
DEFINE_string(part_file, "", "Definition file for target 7-series part");
|
||||||
|
DEFINE_string(
|
||||||
|
frm_file,
|
||||||
|
"",
|
||||||
|
"File containing a list of frame deltas to be applied to the base "
|
||||||
|
"bitstream. Each line in the file is of the form: "
|
||||||
|
"<frame_address> <word1>,...,<word101>.");
|
||||||
|
DEFINE_string(output_file, "", "Write bitsteam to file");
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
gflags::SetUsageMessage(argv[0]);
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
|
||||||
|
auto part = xc7series::Part::FromFile(FLAGS_part_file);
|
||||||
|
if (!part) {
|
||||||
|
std::cerr << "Part file " << FLAGS_part_file
|
||||||
|
<< " not found or invalid" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the frames from the input file
|
||||||
|
xc7series::Frames frames;
|
||||||
|
if (frames.readFrames(FLAGS_frm_file)) {
|
||||||
|
std::cerr << "Frames file " << FLAGS_frm_file
|
||||||
|
<< " not found or invalid" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the frames input file is missing some frames that are in the
|
||||||
|
// tilegrid
|
||||||
|
frames.addMissingFrames(part);
|
||||||
|
|
||||||
|
// Create data for the type 2 configuration packet with information
|
||||||
|
// about all frames
|
||||||
|
xc7series::PacketData configuration_packet_data(
|
||||||
|
xc7series::createType2ConfigurationPacketData(frames.getFrames(),
|
||||||
|
part));
|
||||||
|
|
||||||
|
// Put together a configuration package
|
||||||
|
xc7series::ConfigurationPackage configuration_package;
|
||||||
|
xc7series::createConfigurationPackage(configuration_package,
|
||||||
|
configuration_packet_data, part);
|
||||||
|
|
||||||
|
// Write bitstream
|
||||||
|
if (xc7series::writeBitstream(configuration_package, FLAGS_part_name,
|
||||||
|
FLAGS_frm_file, "xc7frames2bit",
|
||||||
|
FLAGS_output_file)) {
|
||||||
|
std::cerr << "Failed to write bitstream" << std::endl
|
||||||
|
<< "Exitting" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue