mirror of https://github.com/openXC7/prjxray.git
356 lines
9.4 KiB
C++
356 lines
9.4 KiB
C++
/*
|
|
* 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);
|
|
}
|