/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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, "", ": 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 frames; uint32_t frame_range_begin = 0, frame_range_end = 0; std::vector zero_frame(101); struct BitReader { BitReader(const std::vector& bytes) : bytes_(bytes) {} const std::vector& bytes_; template int operator()(T& arg) { using ArchType = std::decay_t; auto reader = xilinx::BitstreamReader::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::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::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> pgmdata; std::vector 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(it.first), static_cast( 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()); 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(it.first)); for (size_t i = 0; i < it.second.size(); i++) if (std::is_same::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 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 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( static_cast(in_file->data()), static_cast(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); }