| /* | 
 |  * 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); | 
 | } |