From 2aa3c309762a1ac2e3c5989e6419f4825a24ac0e Mon Sep 17 00:00:00 2001 From: Tomasz Michalak Date: Fri, 8 Mar 2019 15:50:01 +0100 Subject: [PATCH] xc7frames2bit: implement tool for xilinx 7-series bitstream generation from frames Signed-off-by: Tomasz Michalak --- lib/CMakeLists.txt | 4 +- lib/include/prjxray/xilinx/xc7series/frames.h | 35 +++ lib/include/prjxray/xilinx/xc7series/utils.h | 34 +++ lib/xilinx/xc7series/frames.cc | 101 +++++++ lib/xilinx/xc7series/utils.cc | 266 ++++++++++++++++++ tools/CMakeLists.txt | 7 + tools/xc7frames2bit.cc | 61 ++++ 7 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 lib/include/prjxray/xilinx/xc7series/frames.h create mode 100644 lib/include/prjxray/xilinx/xc7series/utils.h create mode 100644 lib/xilinx/xc7series/frames.cc create mode 100644 lib/xilinx/xc7series/utils.cc create mode 100644 tools/xc7frames2bit.cc diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bc53f61c..331798d8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -14,9 +14,11 @@ add_library(libprjxray xilinx/xc7series/global_clock_region.cc xilinx/xc7series/part.cc xilinx/xc7series/row.cc + xilinx/xc7series/frames.cc + xilinx/xc7series/utils.cc ) 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) add_executable(big_endian_span_test big_endian_span_test.cc) diff --git a/lib/include/prjxray/xilinx/xc7series/frames.h b/lib/include/prjxray/xilinx/xc7series/frames.h new file mode 100644 index 00000000..63070c6c --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/frames.h @@ -0,0 +1,35 @@ +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_FRAMES_H +#define PRJXRAY_LIB_XILINX_XC7SERIES_FRAMES_H + +#include +#include + +#include +#include +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { +class Frames { + public: + typedef std::vector FrameData; + typedef std::map Frames2Data; + + int readFrames(const std::string& frm_file_str); + void addMissingFrames(const absl::optional& 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 diff --git a/lib/include/prjxray/xilinx/xc7series/utils.h b/lib/include/prjxray/xilinx/xc7series/utils.h new file mode 100644 index 00000000..9b79fd4a --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/utils.h @@ -0,0 +1,34 @@ + +#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_UTILS_H_ +#define PRJXRAY_LIB_XILINX_XC7SERIES_UTILS_H_ + +#include +#include +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { +using PacketData = std::vector; +using BitstreamHeader = std::vector; +using ConfigurationPackage = std::vector>; + +PacketData createType2ConfigurationPacketData(const Frames::Frames2Data& frames, + absl::optional& part); +void createConfigurationPackage(ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& 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_ diff --git a/lib/xilinx/xc7series/frames.cc b/lib/xilinx/xc7series/frames.cc new file mode 100644 index 00000000..f6accecb --- /dev/null +++ b/lib/xilinx/xc7series/frames.cc @@ -0,0 +1,101 @@ +#include +#include + +#include +#include + +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 frame_delta = + absl::StrSplit(frm_line, ' '); + + uint32_t frame_address = + std::stoul(frame_delta.first, nullptr, 16); + + std::vector 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(frm_addr, frame_data)); + } + return 0; +} + +void Frames::addMissingFrames(const absl::optional& 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( + 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 diff --git a/lib/xilinx/xc7series/utils.cc b/lib/xilinx/xc7series/utils.cc new file mode 100644 index 00000000..f45921c8 --- /dev/null +++ b/lib/xilinx/xc7series/utils.cc @@ -0,0 +1,266 @@ +#include +#include + +#include +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +PacketData createType2ConfigurationPacketData(const Frames::Frames2Data& frames, + absl::optional& 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) { + // 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(Command::NOP)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new ConfigurationPacketWithPayload<1>( + ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD, + {static_cast(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(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(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(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(Command::GRESTORE)})); + out_packets.emplace_back(new NopPacket()); + out_packets.emplace_back(new ConfigurationPacketWithPayload<1>( + ConfigurationPacket::Opcode::Write, ConfigurationRegister::CMD, + {static_cast(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(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(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(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((build_source.size() + 1) >> 8)); + bit_header.push_back(static_cast(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((part_name.size() + 1) >> 8)); + bit_header.push_back(static_cast(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((build_date_string.size() + 1) >> 8)); + bit_header.push_back( + static_cast(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((build_time_string.size() + 1) >> 8)); + bit_header.push_back( + static_cast(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(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(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 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1a0bec1b..2fc35558 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -25,3 +25,10 @@ target_link_libraries(xc7patch gflags libprjxray ) +add_executable(xc7frames2bit xc7frames2bit.cc) +target_link_libraries(xc7frames2bit + absl::strings + absl::time + gflags + libprjxray +) diff --git a/tools/xc7frames2bit.cc b/tools/xc7frames2bit.cc new file mode 100644 index 00000000..554ca197 --- /dev/null +++ b/tools/xc7frames2bit.cc @@ -0,0 +1,61 @@ +#include + +#include +#include + +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: " + " ,...,."); +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; +}