| /* | |
| * Copyright (C) 2017-2020 The Project X-Ray Authors. | |
| * | |
| * Use of this source code is governed by a ISC-style | |
| * license that can be found in the LICENSE file or at | |
| * https://opensource.org/licenses/ISC | |
| * | |
| * SPDX-License-Identifier: ISC | |
| */ | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <iostream> | |
| #include <set> | |
| #include <string> | |
| #include <vector> | |
| #include <absl/strings/numbers.h> | |
| #include <absl/strings/str_cat.h> | |
| #include <absl/strings/str_split.h> | |
| #include <gflags/gflags.h> | |
| #include <prjxray/memory_mapped_file.h> | |
| #include <prjxray/xilinx/architectures.h> | |
| #include <prjxray/xilinx/bitstream_reader.h> | |
| #include <prjxray/xilinx/configuration.h> | |
| DEFINE_bool(c, false, "output '*' for repeating patterns"); | |
| DEFINE_bool(C, false, "do not ignore the checksum in each frame"); | |
| DEFINE_int32(f, | |
| -1, | |
| "only dump the specified frame (might be used more than once)"); | |
| DEFINE_string(F, | |
| "", | |
| "<first_frame_address>:<last_frame_address> only dump frame in " | |
| "the specified range"); | |
| DEFINE_string(o, "", "write machine-readable output file with config frames"); | |
| DEFINE_bool(p, false, "output a binary netpgm image"); | |
| DEFINE_bool(x, | |
| false, | |
| "use format 'bit_%%08x_%%03d_%%02d_t%%d_h%%d_r%%d_c%%d_m%%d'\n" | |
| "The fields have the following meaning:\n" | |
| " - complete 32 bit hex frame id\n" | |
| " - word index with that frame (decimal)\n" | |
| " - bit index with that word (decimal)\n" | |
| " - decoded frame type from frame id\n" | |
| " - decoded top/botttom from frame id (top=0)\n" | |
| " - decoded row address from frame id\n" | |
| " - decoded column address from frame id\n" | |
| " - decoded minor address from frame id\n"); | |
| DEFINE_bool(y, false, "use format 'bit_%%08x_%%03d_%%02d'"); | |
| DEFINE_bool(z, false, "skip zero frames (frames with all bits cleared) in o"); | |
| DEFINE_string(part_file, "", "YAML file describing a Xilinx part"); | |
| DEFINE_string(architecture, | |
| "Series7", | |
| "Architecture of the provided bitstream"); | |
| DEFINE_string( | |
| aux, | |
| "", | |
| "write machine-readable output file with auxiliary bitstream data"); | |
| namespace xilinx = prjxray::xilinx; | |
| std::set<uint32_t> frames; | |
| uint32_t frame_range_begin = 0, frame_range_end = 0; | |
| std::vector<uint32_t> zero_frame(101); | |
| struct BitReader { | |
| BitReader(const std::vector<uint8_t>& bytes) : bytes_(bytes) {} | |
| const std::vector<uint8_t>& bytes_; | |
| template <typename T> | |
| int operator()(T& arg) { | |
| using ArchType = std::decay_t<decltype(arg)>; | |
| auto reader = | |
| xilinx::BitstreamReader<ArchType>::InitWithBytes(bytes_); | |
| if (!reader) { | |
| std::cerr << "Input doesn't look like a bitstream" | |
| << std::endl; | |
| return 1; | |
| } | |
| std::cout << "Config size: " << reader->words().size() | |
| << " words" << std::endl; | |
| auto part = ArchType::Part::FromFile(FLAGS_part_file); | |
| if (!part) { | |
| std::cerr << "Part file not found or invalid" | |
| << std::endl; | |
| return 1; | |
| } | |
| auto config = xilinx::Configuration<ArchType>::InitWithPackets( | |
| *part, *reader); | |
| if (!config) { | |
| std::cerr | |
| << "Bitstream does not appear to be for this part" | |
| << std::endl; | |
| return 1; | |
| } | |
| std::cout << "Number of configuration frames: " | |
| << config->frames().size() << std::endl; | |
| FILE* f = stdout; | |
| if (!FLAGS_o.empty()) { | |
| f = fopen(FLAGS_o.c_str(), "w"); | |
| if (f == nullptr) { | |
| printf( | |
| "Can't open output file '%s' for " | |
| "writing!\n", | |
| FLAGS_o.c_str()); | |
| return 1; | |
| } | |
| } else { | |
| fprintf(f, "\n"); | |
| } | |
| if (!FLAGS_aux.empty()) { | |
| FILE* aux_file = fopen(FLAGS_aux.c_str(), "w"); | |
| if (aux_file == nullptr) { | |
| printf( | |
| "Can't open aux output file '%s' for " | |
| "writing!\n", | |
| FLAGS_aux.c_str()); | |
| return 1; | |
| } | |
| // Extract and decode header information as in RBT file | |
| xilinx::BitstreamReader<ArchType>::PrintHeader( | |
| bytes_, aux_file); | |
| // Extract FPGA configuration logic information | |
| reader->PrintFpgaConfigurationLogicData(aux_file); | |
| // Extract configuration frames' addresses | |
| config->PrintFrameAddresses(aux_file); | |
| fclose(aux_file); | |
| } | |
| std::vector<std::vector<bool>> pgmdata; | |
| std::vector<int> pgmsep; | |
| int word_length = sizeof(typename ArchType::WordType) * 8; | |
| for (auto& it : config->frames()) { | |
| if (FLAGS_z && it.second == zero_frame) | |
| continue; | |
| if (!frames.empty() && !frames.count(it.first)) | |
| continue; | |
| if (frame_range_begin != frame_range_end && | |
| (it.first < frame_range_begin || | |
| frame_range_end <= it.first)) | |
| continue; | |
| if (FLAGS_o.empty()) | |
| printf( | |
| "Frame 0x%08x (Type=%d Top=%d Row=%d " | |
| "Column=%d " | |
| "Minor=%d):\n", | |
| static_cast<uint32_t>(it.first), | |
| static_cast<unsigned int>( | |
| it.first.block_type()), | |
| it.first.is_bottom_half_rows() ? 1 : 0, | |
| it.first.row(), it.first.column(), | |
| it.first.minor()); | |
| if (FLAGS_p) { | |
| if (it.first.minor() == 0 && !pgmdata.empty()) | |
| pgmsep.push_back(pgmdata.size()); | |
| pgmdata.push_back(std::vector<bool>()); | |
| for (size_t i = 0; i < it.second.size(); i++) | |
| for (int k = 0; k < word_length; k++) | |
| pgmdata.back().push_back( | |
| (it.second.at(i) & | |
| (1 << k)) != 0); | |
| } else if (FLAGS_x || FLAGS_y) { | |
| for (int i = 0; i < (int)it.second.size(); | |
| i++) { | |
| for (int k = 0; k < word_length; k++) { | |
| if (((i != 50 || k > 12 || | |
| FLAGS_C) || | |
| std::is_same< | |
| ArchType, | |
| prjxray::xilinx:: | |
| Spartan6>:: | |
| value) && | |
| ((it.second.at(i) & | |
| (1 << k)) != 0)) { | |
| if (FLAGS_x) | |
| fprintf( | |
| f, | |
| "bit_%08x_%" | |
| "03d_%" | |
| "02d_t%d_h%" | |
| "d_r%d_c%" | |
| "d_m%d\n", | |
| static_cast< | |
| uint32_t>( | |
| it.first), | |
| i, k, | |
| static_cast< | |
| unsigned int>( | |
| it.first | |
| .block_type()), | |
| it.first.is_bottom_half_rows() | |
| ? 1 | |
| : 0, | |
| it.first | |
| .row(), | |
| it.first | |
| .column(), | |
| it.first | |
| .minor()); | |
| else | |
| fprintf( | |
| f, | |
| "bit_%08x_%" | |
| "03d_" | |
| "%02d\n", | |
| static_cast< | |
| uint32_t>( | |
| it.first), | |
| i, k); | |
| } | |
| } | |
| } | |
| if (FLAGS_o.empty()) | |
| fprintf(f, "\n"); | |
| } else { | |
| if (!FLAGS_o.empty()) | |
| fprintf( | |
| f, ".frame 0x%08x\n", | |
| static_cast<uint32_t>(it.first)); | |
| for (size_t i = 0; i < it.second.size(); i++) | |
| if (std::is_same<ArchType, | |
| prjxray::xilinx:: | |
| Spartan6>::value) { | |
| fprintf( | |
| f, "%08x%s", | |
| 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"); | |
| } | |
| } | |
| if (FLAGS_p) { | |
| int width = pgmdata.size() + pgmsep.size(); | |
| int height = ArchType::words_per_frame * word_length; | |
| fprintf(f, "P5 %d %d 15\n", width, height); | |
| for (int y = 0, | |
| bit = ArchType::words_per_frame * word_length - | |
| 1; | |
| y < height; y++, bit--) { | |
| for (int x = 0, frame = 0, sep = 0; x < width; | |
| x++, frame++) { | |
| if (sep < int(pgmsep.size()) && | |
| frame == pgmsep.at(sep)) { | |
| fputc(8, f); | |
| x++, sep++; | |
| } | |
| if (bit >= | |
| (int)pgmdata.at(frame).size()) { | |
| fputc(0, f); | |
| continue; | |
| } | |
| fputc( | |
| pgmdata.at(frame).at(bit) ? 15 : 0, | |
| f); | |
| } | |
| if (bit % word_length == 0 && y) { | |
| for (int x = 0; x < width; x++) | |
| fputc(8, f); | |
| y++; | |
| } | |
| } | |
| } | |
| if (!FLAGS_o.empty()) | |
| fclose(f); | |
| printf("DONE\n"); | |
| return 0; | |
| } | |
| }; | |
| int main(int argc, char** argv) { | |
| gflags::SetUsageMessage( | |
| absl::StrCat("Usage: ", argv[0], " [options] [bitfile]")); | |
| gflags::ParseCommandLineFlags(&argc, &argv, true); | |
| if (FLAGS_f >= 0) { | |
| frames.insert(FLAGS_f); | |
| } | |
| if (!FLAGS_F.empty()) { | |
| std::pair<std::string, std::string> p = | |
| absl::StrSplit(FLAGS_F, ":"); | |
| frame_range_begin = strtol(p.first.c_str(), nullptr, 0); | |
| frame_range_end = strtol(p.second.c_str(), nullptr, 0) + 1; | |
| } | |
| std::vector<uint8_t> in_bytes; | |
| if (argc == 2) { | |
| auto in_file_name = argv[1]; | |
| auto in_file = | |
| prjxray::MemoryMappedFile::InitWithFile(in_file_name); | |
| if (!in_file) { | |
| std::cerr << "Can't open input file '" << in_file_name | |
| << "' for reading!" << std::endl; | |
| return 1; | |
| } | |
| std::cout << "Bitstream size: " << in_file->size() << " bytes" | |
| << std::endl; | |
| in_bytes = std::vector<uint8_t>( | |
| static_cast<uint8_t*>(in_file->data()), | |
| static_cast<uint8_t*>(in_file->data()) + in_file->size()); | |
| } else { | |
| while (1) { | |
| int c = getchar(); | |
| if (c == EOF) | |
| break; | |
| in_bytes.push_back(c); | |
| } | |
| std::cout << "Bitstream size: " << in_bytes.size() << " bytes" | |
| << std::endl; | |
| } | |
| xilinx::Architecture::Container arch_container = | |
| xilinx::ArchitectureFactory::create_architecture( | |
| FLAGS_architecture); | |
| return absl::visit(BitReader(in_bytes), arch_container); | |
| } |