/******************************************************************** * This file includes functions that output bitstream database * to files in different formats *******************************************************************/ #include #include #include /* Headers from vtrutil library */ #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" /* Headers from openfpgautil library */ #include "bitstream_manager_utils.h" #include "openfpga_digest.h" #include "openfpga_reserved_words.h" #include "openfpga_tokenizer.h" #include "write_xml_arch_bitstream.h" /* begin namespace openfpga */ namespace openfpga { /******************************************************************** * This function write header information to a bitstream file *******************************************************************/ static void write_bitstream_xml_file_head(std::fstream& fp, const bool& include_time_stamp) { valid_file_stream(fp); fp << "" << std::endl; fp << std::endl; } /******************************************************************** * Recursively write the bitstream of a block to a xml file * This function will use a Depth-First Search in outputting bitstream * for each block * 1. For block with bits as children, we will output the XML lines * 2. For block without bits/child blocks, we can return * 3. For block with child blocks, we visit each child recursively *******************************************************************/ static void rec_write_block_bitstream_to_xml_file( std::fstream& fp, const BitstreamManager& bitstream_manager, const ConfigBlockId& block, const size_t& hierarchy_level) { valid_file_stream(fp); /* Write the bits of this block */ write_tab_to_file(fp, hierarchy_level); fp << "" << std::endl; /* Dive to child blocks if this block has any */ for (const ConfigBlockId& child_block : bitstream_manager.block_children(block)) { rec_write_block_bitstream_to_xml_file(fp, bitstream_manager, child_block, hierarchy_level + 1); } if (0 == bitstream_manager.block_bits(block).size()) { write_tab_to_file(fp, hierarchy_level); fp << "" << std::endl; return; } std::vector block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, block); /* Output hierarchy of this parent*/ write_tab_to_file(fp, hierarchy_level + 1); fp << "" << std::endl; size_t hierarchy_counter = 0; for (const ConfigBlockId& temp_block : block_hierarchy) { write_tab_to_file(fp, hierarchy_level + 2); fp << "" << std::endl; hierarchy_counter++; } write_tab_to_file(fp, hierarchy_level + 1); fp << "" << std::endl; /* Output input/output nets if there are any */ if (false == bitstream_manager.block_input_net_ids(block).empty()) { write_tab_to_file(fp, hierarchy_level + 1); fp << "\n"; size_t path_counter = 0; /* Split with space */ StringToken input_net_tokenizer( bitstream_manager.block_input_net_ids(block)); for (const std::string& net : input_net_tokenizer.split(std::string(" "))) { write_tab_to_file(fp, hierarchy_level + 2); fp << ""; fp << "\n"; path_counter++; } write_tab_to_file(fp, hierarchy_level + 1); fp << "\n"; } if (false == bitstream_manager.block_output_net_ids(block).empty()) { write_tab_to_file(fp, hierarchy_level + 1); fp << "\n"; size_t path_counter = 0; /* Split with space */ StringToken output_net_tokenizer( bitstream_manager.block_output_net_ids(block)); for (const std::string& net : output_net_tokenizer.split(std::string(" "))) { write_tab_to_file(fp, hierarchy_level + 2); fp << ""; fp << "\n"; path_counter++; } write_tab_to_file(fp, hierarchy_level + 1); fp << "\n"; } /* Output child bits under this block */ size_t bit_counter = 0; write_tab_to_file(fp, hierarchy_level + 1); fp << "" << std::endl; for (const ConfigBitId& child_bit : bitstream_manager.block_bits(block)) { write_tab_to_file(fp, hierarchy_level + 2); fp << "" << std::endl; bit_counter++; } write_tab_to_file(fp, hierarchy_level + 1); fp << "" << std::endl; write_tab_to_file(fp, hierarchy_level); fp << "" << std::endl; } /******************************************************************** * Write the bitstream to a file without binding to the configuration * procotols of a given FPGA fabric in XML format * * Notes: * This is a very generic representation for bitstream that are implemented * by VPR engine. It shows the bitstream for each blocks in the FPGA * architecture that users are modeling. * This function can be used to: * 1. Debug the bitstream decoding to see if there is any bug * 2. Create an intermediate file to reorganize a bitstream for * specific FPGAs * 3. TODO: support FASM format *******************************************************************/ void write_xml_architecture_bitstream(const BitstreamManager& bitstream_manager, const std::string& fname, const bool& include_time_stamp) { /* Ensure that we have a valid file name */ if (true == fname.empty()) { VTR_LOG_ERROR( "Received empty file name to output bitstream!\n\tPlease specify a valid " "file name.\n"); } std::string timer_message = std::string("Write ") + std::to_string(bitstream_manager.bits().size()) + std::string(" architecture independent bitstream into XML file '") + fname + std::string("'"); vtr::ScopedStartFinishTimer timer(timer_message); /* Create the file stream */ std::fstream fp; fp.open(fname, std::fstream::out | std::fstream::trunc); check_file_stream(fname.c_str(), fp); /* Put down a brief introduction */ write_bitstream_xml_file_head(fp, include_time_stamp); /* Find the top block, which has not parents */ std::vector top_block = find_bitstream_manager_top_blocks(bitstream_manager); /* Make sure we have only 1 top block */ VTR_ASSERT(1 == top_block.size()); /* Write bitstream, block by block, in a recursive way */ rec_write_block_bitstream_to_xml_file(fp, bitstream_manager, top_block[0], 0); /* Close file handler */ fp.close(); } } /* end namespace openfpga */