diff --git a/docs/source/manual/file_formats/bitstream_setting.rst b/docs/source/manual/file_formats/bitstream_setting.rst index eb2af5ddf..d46751a14 100644 --- a/docs/source/manual/file_formats/bitstream_setting.rst +++ b/docs/source/manual/file_formats/bitstream_setting.rst @@ -16,6 +16,9 @@ This can define a hard-coded bitstream for a reconfigurable resource in FPGA fab <non_fabric name="<string>" file="<string>"> <pb name="<string>" type="<string>" content="<string>"/> </non_fabric> + <overwrite_bitstream> + <bit value="<0 or 1>" path="<string>"/> + </overwrite_bitstream> </openfpga_bitstream_setting> pb_type-related Settings @@ -75,7 +78,7 @@ The following syntax are applicable to the XML definition tagged by ``interconne The default path can be either ``iopad.inpad`` or ``ff.Q`` which corresponds to the first input and the second input respectively. non_fabric-related Settings -^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is special syntax to extract PB defined parameter or attribute and save the data into dedicated JSON file outside of fabric bitstream @@ -97,7 +100,7 @@ The following syntax are applicable to the XML definition tagged by ``non_fabric file="bram.json" -.. option:: ``pb`` child element name="<string: pb_type child name>" +.. option:: pb child element name="<string: pb_type child name>" Together with ``pb_type`` top level name, that is the source of the ``pb_type`` bitstream @@ -112,6 +115,33 @@ The following syntax are applicable to the XML definition tagged by ``non_fabric The final ``pb_type`` name is "bram.bram_lr[mem_36K_tdp].mem_36K" -.. option:: ``pb`` child element content="<string>" +.. option:: pb child element content="<string>" The content of the ``pb_type`` data to be extracted. For example, ``content=".param INIT_i"`` means that the data will be extracted from the ``.param INIT_i`` line defined under the ``.blif model``. + +overwrite_bitstream-related Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is to allow user to set value of a list of bits which is represented using full path in the hierarchy of FPGA fabric + +This ``overwrite_bitstream`` settings has the highest priority than loading any external bitstream file + +Each bit to overwrite is represented by one ``bit`` child node/tag + +The following syntax are applicable to the XML definition tagged by ``bit`` node under ``overwrite_bitstream`` setting. + +.. option:: value="<0 or 1>" + + The boolean ``0`` or ``1`` that will be set. For example, + + .. code-block:: xml + + value="0" + +.. option:: path="<string>" + + ``path`` represents the location of this block in FPGA fabric, i.e., the full path in the hierarchy of FPGA fabric. + + .. code-block:: xml + + path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5[0]" diff --git a/libs/libarchopenfpga/src/bitstream_setting.cpp b/libs/libarchopenfpga/src/bitstream_setting.cpp index 7d763f9d2..6092bd91e 100644 --- a/libs/libarchopenfpga/src/bitstream_setting.cpp +++ b/libs/libarchopenfpga/src/bitstream_setting.cpp @@ -24,6 +24,12 @@ BitstreamSetting::interconnect_settings() const { interconnect_setting_ids_.end()); } +BitstreamSetting::overwrite_bitstream_range +BitstreamSetting::overwrite_bitstreams() const { + return vtr::make_range(overwrite_bitstream_ids_.begin(), + overwrite_bitstream_ids_.end()); +} + /************************************************************************ * Constructors ***********************************************************************/ @@ -106,6 +112,18 @@ std::vector<NonFabricBitstreamSetting> BitstreamSetting::non_fabric() const { return non_fabric_; } +std::string BitstreamSetting::overwrite_bitstream_path( + const OverwriteBitstreamId& id) const { + VTR_ASSERT(true == valid_overwrite_bitstream_id(id)); + return overwrite_bitstream_paths_[id]; +} + +bool BitstreamSetting::overwrite_bitstream_value( + const OverwriteBitstreamId& id) const { + VTR_ASSERT(true == valid_overwrite_bitstream_id(id)); + return overwrite_bitstream_values_[id]; +} + /************************************************************************ * Public Mutators ***********************************************************************/ @@ -178,6 +196,21 @@ void BitstreamSetting::add_non_fabric_pb(const std::string& pb, } } +OverwriteBitstreamId BitstreamSetting::add_overwrite_bitstream( + const std::string& path, const bool& value) { + VTR_ASSERT(path.size()); + VTR_ASSERT(overwrite_bitstream_ids_.size() == + overwrite_bitstream_paths_.size()); + VTR_ASSERT(overwrite_bitstream_paths_.size() == + overwrite_bitstream_values_.size()); + OverwriteBitstreamId id = + OverwriteBitstreamId(overwrite_bitstream_ids_.size()); + overwrite_bitstream_ids_.push_back(id); + overwrite_bitstream_paths_.push_back(path); + overwrite_bitstream_values_.push_back(value); + return id; +} + /************************************************************************ * Public Validators ***********************************************************************/ @@ -194,4 +227,14 @@ bool BitstreamSetting::valid_bitstream_interconnect_setting_id( interconnect_setting_ids_[interconnect_setting_id]); } +bool BitstreamSetting::valid_overwrite_bitstream_id( + const OverwriteBitstreamId& id) const { + VTR_ASSERT(overwrite_bitstream_ids_.size() == + overwrite_bitstream_paths_.size()); + VTR_ASSERT(overwrite_bitstream_paths_.size() == + overwrite_bitstream_values_.size()); + return (size_t(id) < overwrite_bitstream_ids_.size()) && + (id == overwrite_bitstream_ids_[id]); +} + } // namespace openfpga diff --git a/libs/libarchopenfpga/src/bitstream_setting.h b/libs/libarchopenfpga/src/bitstream_setting.h index 1b12c8de3..042e850e0 100644 --- a/libs/libarchopenfpga/src/bitstream_setting.h +++ b/libs/libarchopenfpga/src/bitstream_setting.h @@ -61,11 +61,15 @@ class BitstreamSetting { typedef vtr::vector<BitstreamInterconnectSettingId, BitstreamInterconnectSettingId>::const_iterator bitstream_interconnect_setting_iterator; + typedef vtr::vector<OverwriteBitstreamId, + OverwriteBitstreamId>::const_iterator + overwrite_bitstream_iterator; /* Create range */ typedef vtr::Range<bitstream_pb_type_setting_iterator> bitstream_pb_type_setting_range; typedef vtr::Range<bitstream_interconnect_setting_iterator> bitstream_interconnect_setting_range; + typedef vtr::Range<overwrite_bitstream_iterator> overwrite_bitstream_range; public: /* Constructors */ BitstreamSetting(); @@ -73,6 +77,7 @@ class BitstreamSetting { public: /* Accessors: aggregates */ bitstream_pb_type_setting_range pb_type_settings() const; bitstream_interconnect_setting_range interconnect_settings() const; + overwrite_bitstream_range overwrite_bitstreams() const; public: /* Public Accessors */ std::string pb_type_name( @@ -98,6 +103,8 @@ class BitstreamSetting { std::string default_path( const BitstreamInterconnectSettingId& interconnect_setting_id) const; std::vector<NonFabricBitstreamSetting> non_fabric() const; + std::string overwrite_bitstream_path(const OverwriteBitstreamId& id) const; + bool overwrite_bitstream_value(const OverwriteBitstreamId& id) const; public: /* Public Mutators */ BitstreamPbTypeSettingId add_bitstream_pb_type_setting( @@ -120,11 +127,15 @@ class BitstreamSetting { void add_non_fabric(const std::string& name, const std::string& file); void add_non_fabric_pb(const std::string& pb, const std::string& content); + OverwriteBitstreamId add_overwrite_bitstream(const std::string& path, + const bool& value); + public: /* Public Validators */ bool valid_bitstream_pb_type_setting_id( const BitstreamPbTypeSettingId& pb_type_setting_id) const; bool valid_bitstream_interconnect_setting_id( const BitstreamInterconnectSettingId& interconnect_setting_id) const; + bool valid_overwrite_bitstream_id(const OverwriteBitstreamId& id) const; private: /* Internal data */ /* Pb type -related settings @@ -162,6 +173,10 @@ class BitstreamSetting { vtr::vector<BitstreamInterconnectSettingId, std::string> interconnect_default_paths_; std::vector<NonFabricBitstreamSetting> non_fabric_; + vtr::vector<OverwriteBitstreamId, OverwriteBitstreamId> + overwrite_bitstream_ids_; + vtr::vector<OverwriteBitstreamId, std::string> overwrite_bitstream_paths_; + vtr::vector<OverwriteBitstreamId, bool> overwrite_bitstream_values_; }; } // namespace openfpga diff --git a/libs/libarchopenfpga/src/bitstream_setting_fwd.h b/libs/libarchopenfpga/src/bitstream_setting_fwd.h index bc5c2ab88..dbcc70553 100644 --- a/libs/libarchopenfpga/src/bitstream_setting_fwd.h +++ b/libs/libarchopenfpga/src/bitstream_setting_fwd.h @@ -15,11 +15,13 @@ struct bitstream_pb_type_setting_id_tag; struct bitstream_interconnect_setting_id_tag; +struct overwrite_bitstream_id_tag; typedef vtr::StrongId<bitstream_pb_type_setting_id_tag> BitstreamPbTypeSettingId; typedef vtr::StrongId<bitstream_interconnect_setting_id_tag> BitstreamInterconnectSettingId; +typedef vtr::StrongId<overwrite_bitstream_id_tag> OverwriteBitstreamId; /* Short declaration of class */ class BitstreamSetting; diff --git a/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp b/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp index 7f6bd1237..244703fc1 100644 --- a/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp +++ b/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp @@ -102,6 +102,30 @@ static void read_xml_non_fabric_bitstream_setting( } } +/******************************************************************** + * Parse XML description for a bit setting under a <bit> XML node + *******************************************************************/ +static void read_xml_overwrite_bitstream_setting( + pugi::xml_node& xml_overwrite_bitstream, const pugiutil::loc_data& loc_data, + openfpga::BitstreamSetting& bitstream_setting) { + // Loopthrough bit + for (pugi::xml_node xml_bit : xml_overwrite_bitstream.children()) { + if (xml_bit.name() != std::string("bit")) { + bad_tag(xml_bit, loc_data, xml_overwrite_bitstream, {"bit"}); + } + const std::string& path_attr = + get_attribute(xml_bit, "path", loc_data).as_string(); + const std::string& value_attr = + get_attribute(xml_bit, "value", loc_data).as_string(); + if (value_attr != "0" && value_attr != "1") { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bit), + "Invalid value of overwrite_bitstream bit. Expect [0|1]"); + } + /* Add to bit */ + bitstream_setting.add_overwrite_bitstream(path_attr, value_attr == "1"); + } +} + /******************************************************************** * Parse XML codes about <openfpga_bitstream_setting> to an object *******************************************************************/ @@ -116,9 +140,10 @@ openfpga::BitstreamSetting read_xml_bitstream_setting( /* Error out if the XML child has an invalid name! */ if ((xml_child.name() != std::string("pb_type")) && (xml_child.name() != std::string("interconnect")) && - (xml_child.name() != std::string("non_fabric"))) { + (xml_child.name() != std::string("non_fabric")) && + (xml_child.name() != std::string("overwrite_bitstream"))) { bad_tag(xml_child, loc_data, Node, - {"pb_type | interconnect | non_fabric"}); + {"pb_type | interconnect | non_fabric | overwrite_bitstream"}); } if (xml_child.name() == std::string("pb_type")) { @@ -127,10 +152,13 @@ openfpga::BitstreamSetting read_xml_bitstream_setting( } else if (xml_child.name() == std::string("interconnect")) { read_xml_bitstream_interconnect_setting(xml_child, loc_data, bitstream_setting); - } else { - VTR_ASSERT_SAFE(xml_child.name() == std::string("non_fabric")); + } else if (xml_child.name() == std::string("non_fabric")) { read_xml_non_fabric_bitstream_setting(xml_child, loc_data, bitstream_setting); + } else { + VTR_ASSERT_SAFE(xml_child.name() == std::string("overwrite_bitstream")); + read_xml_overwrite_bitstream_setting(xml_child, loc_data, + bitstream_setting); } } diff --git a/libs/libfpgabitstream/src/bitstream_manager.cpp b/libs/libfpgabitstream/src/bitstream_manager.cpp index cf18a6e86..291befb60 100644 --- a/libs/libfpgabitstream/src/bitstream_manager.cpp +++ b/libs/libfpgabitstream/src/bitstream_manager.cpp @@ -5,6 +5,10 @@ #include <algorithm> +#include "arch_error.h" +#include "bitstream_manager_utils.h" +#include "openfpga_port_parser.h" +#include "openfpga_tokenizer.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -296,6 +300,69 @@ void BitstreamManager::add_output_net_id_to_block( block_output_net_ids_[block] = output_net_id; } +void BitstreamManager::overwrite_bitstream(const std::string& path, + const bool& value) { + PortParser port_parser(path, PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT); + if (!port_parser.valid()) { + archfpga_throw(__FILE__, __LINE__, + "overwrite_bitstream bit path '%s' does not match format " + "<full path in the hierarchy of FPGA fabric>[bit index]", + path.c_str()); + } else { + BasicPort port = port_parser.port(); + size_t bit = port.get_lsb(); + StringToken tokenizer(port.get_name()); + std::vector<std::string> blocks = tokenizer.split("."); + std::vector<ConfigBlockId> block_ids; + ConfigBlockId block_id = ConfigBlockId::INVALID(); + size_t found = 0; + for (size_t i = 0; i < blocks.size(); i++) { + if (i == 0) { + block_ids = find_bitstream_manager_top_blocks(*this); + } else { + block_ids = block_children(block_id); + } + // Reset + block_id = ConfigBlockId::INVALID(); + // Find the one from the list that match the name + for (auto id : block_ids) { + if (block_name(id) == blocks[i]) { + block_id = id; + break; + } + } + if (block_id != ConfigBlockId::INVALID()) { + // Found one that match the name + found++; + if (found == blocks.size()) { + // Last one, no more child must end here + if (block_children(block_id).size() == 0) { + std::vector<ConfigBitId> ids = block_bits(block_id); + if (bit < ids.size()) { + VTR_ASSERT(valid_bit_id(ids[bit])); + bit_values_[ids[bit]] = value ? '1' : '0'; + } else { + // No configuration bits at all or out of range, invalidate + found = 0; + } + } else { + // There are more child, hence the path still no end, invalidate + found = 0; + } + } + } else { + // Cannot match the name, just stop + break; + } + } + if (found != blocks.size()) { + archfpga_throw(__FILE__, __LINE__, + "Failed to find path '%s' to overwrite bitstream", + path.c_str()); + } + } +} + /****************************************************************************** * Public Validators ******************************************************************************/ diff --git a/libs/libfpgabitstream/src/bitstream_manager.h b/libs/libfpgabitstream/src/bitstream_manager.h index 4af15084d..3cba54fc4 100644 --- a/libs/libfpgabitstream/src/bitstream_manager.h +++ b/libs/libfpgabitstream/src/bitstream_manager.h @@ -213,6 +213,9 @@ class BitstreamManager { void add_output_net_id_to_block(const ConfigBlockId& block, const std::string& output_net_id); + /* Set bit to the bitstream at the given path */ + void overwrite_bitstream(const std::string& path, const bool& value); + public: /* Public Validators */ bool valid_bit_id(const ConfigBitId& bit_id) const; diff --git a/libs/libopenfpgautil/src/openfpga_port_parser.cpp b/libs/libopenfpgautil/src/openfpga_port_parser.cpp index 93b7e0899..da0a27368 100644 --- a/libs/libopenfpgautil/src/openfpga_port_parser.cpp +++ b/libs/libopenfpgautil/src/openfpga_port_parser.cpp @@ -5,6 +5,7 @@ #include <cstring> +#include "arch_error.h" #include "openfpga_tokenizer.h" #include "vtr_assert.h" #include "vtr_geometry.h" @@ -19,9 +20,10 @@ namespace openfpga { /************************************************************************ * Constructors ***********************************************************************/ -PortParser::PortParser(const std::string& data) { +PortParser::PortParser(const std::string& data, const int support_format) { set_default_bracket(); set_default_delim(); + set_support_format(support_format); set_data(data); } @@ -33,9 +35,18 @@ std::string PortParser::data() const { return data_; } BasicPort PortParser::port() const { return port_; } +bool PortParser::valid() const { return valid_; } + /************************************************************************ * Public Mutators ***********************************************************************/ +void PortParser::set_support_format(const int support_format) { + VTR_ASSERT((support_format & PORT_PARSER_SUPPORT_ALL_FORMAT) != 0); + VTR_ASSERT((support_format & ~PORT_PARSER_SUPPORT_ALL_FORMAT) == 0); + support_format_ = support_format; + return; +} + void PortParser::set_data(const std::string& data) { data_ = data; parse(); @@ -47,6 +58,8 @@ void PortParser::set_data(const std::string& data) { ***********************************************************************/ /* Parse the data */ void PortParser::parse() { + valid_ = true; + /* Create a tokenizer */ StringToken tokenizer(data_); @@ -54,11 +67,14 @@ void PortParser::parse() { std::vector<std::string> port_tokens = tokenizer.split(bracket_.x()); /* Make sure we have a port name! */ VTR_ASSERT_SAFE((1 == port_tokens.size()) || (2 == port_tokens.size())); + /* Store the port name! */ port_.set_name(port_tokens[0]); /* If we only have one token */ if (1 == port_tokens.size()) { + // there is no [ + valid_ = (support_format_ & PORT_PARSER_SUPPORT_NO_PORT_FORMAT) != 0; port_.set_width(1); return; /* We can finish here */ } @@ -72,19 +88,25 @@ void PortParser::parse() { /* Split the pin string now */ tokenizer.set_data(pin_tokens[0]); pin_tokens = tokenizer.split(delim_); + VTR_ASSERT_SAFE((1 == pin_tokens.size()) || (2 == pin_tokens.size())); /* Check if we have LSB and MSB or just one */ if (1 == pin_tokens.size()) { /* Single pin */ - port_.set_width(std::stoi(pin_tokens[0]), std::stoi(pin_tokens[0])); + valid_ = (support_format_ & PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT) != 0; + size_t temp = string_to_number(pin_tokens[0]); + port_.set_width(temp, temp); } else if (2 == pin_tokens.size()) { /* A number of pins. * Note that we always use the LSB for token[0] and MSB for token[1] */ - if (std::stoi(pin_tokens[1]) < std::stoi(pin_tokens[0])) { - port_.set_width(std::stoi(pin_tokens[1]), std::stoi(pin_tokens[0])); + valid_ = (support_format_ & PORT_PARSER_SUPPORT_RANGE_FORMAT) != 0; + size_t temp0 = string_to_number(pin_tokens[0]); + size_t temp1 = string_to_number(pin_tokens[1]); + if (temp1 < temp0) { + port_.set_width(temp1, temp0); } else { - port_.set_width(std::stoi(pin_tokens[0]), std::stoi(pin_tokens[1])); + port_.set_width(temp0, temp1); } } @@ -102,6 +124,24 @@ void PortParser::set_default_delim() { return; } +/* + Make sure string is not empty and is all digit before stoi +*/ +size_t PortParser::string_to_number(const std::string& str) { + bool bad_format = str.empty(); + for (auto& chr : str) { + if (!std::isdigit(chr)) { + bad_format = true; + break; + } + } + if (bad_format) { + archfpga_throw(__FILE__, __LINE__, + "Invalid string '%s' to call std::stoi()", str.c_str()); + } + return (size_t)(std::stoi(str)); +} + /************************************************************************ * Member functions for MultiPortParser class ***********************************************************************/ diff --git a/libs/libopenfpgautil/src/openfpga_port_parser.h b/libs/libopenfpgautil/src/openfpga_port_parser.h index f596fd309..ab61603e0 100644 --- a/libs/libopenfpgautil/src/openfpga_port_parser.h +++ b/libs/libopenfpgautil/src/openfpga_port_parser.h @@ -21,37 +21,51 @@ /* namespace openfpga begins */ namespace openfpga { +constexpr int PORT_PARSER_SUPPORT_NO_PORT_FORMAT = (1 << 0); // (5) below +constexpr int PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT = (1 << 1); // (3) below +constexpr int PORT_PARSER_SUPPORT_RANGE_FORMAT = (1 << 2); // (1) and (2) below +constexpr int PORT_PARSER_SUPPORT_ALL_FORMAT = ((1 << 3) - 1); + /************************************************************************ * Class PortParser: single port parser * Supported port definition: - * 1. <port_name>[<LSB>:<MSB>] - * 2. <port_name>[<MSB>:<LSB>] - * 3. <port_name>[<single_pin_index>] - * 4. <port_name>[] - * 5. <port_name> + * (1) <port_name>[<LSB>:<MSB>] + * (2) <port_name>[<MSB>:<LSB>] + * (3) <port_name>[<single_pin_index>] + * (4) <port_name>[] -- this is not currently supported. Two problems: + * * tokenizer will error out and + * * stoi cannot support empty string, and give + * std::invalid_argument error + * (5) <port_name> * In case 4 and 5, we will assign (-1,-1) for LSB and MSB ***********************************************************************/ class PortParser { public: /* Constructors*/ - PortParser(const std::string& data); + PortParser(const std::string& data, + const int support_format = PORT_PARSER_SUPPORT_ALL_FORMAT); public: /* Public Accessors */ std::string data() const; BasicPort port() const; + bool valid() const; public: /* Public Mutators */ + void set_support_format(const int support_format); void set_data(const std::string& data); private: /* Private Mutators */ void parse(); void set_default_bracket(); void set_default_delim(); + size_t string_to_number(const std::string& str); private: /* Internal data */ std::string data_; /* Lines to be splited */ + int support_format_; vtr::Point<char> bracket_; char delim_; BasicPort port_; + bool valid_; }; /************************************************************************ diff --git a/openfpga/src/base/openfpga_basic.cpp b/openfpga/src/base/openfpga_basic.cpp index 240caaa6f..6546ff909 100644 --- a/openfpga/src/base/openfpga_basic.cpp +++ b/openfpga/src/base/openfpga_basic.cpp @@ -69,7 +69,19 @@ int call_external_command(const Command& cmd, return CMD_EXEC_FATAL_ERROR; } - return system(cmd_ss.c_str()); + // Refer https://pubs.opengroup.org/onlinepubs/009695399/functions/system.html + // Refer + // https://pubs.opengroup.org/onlinepubs/009695399/functions/waitpid.html + int status = system(cmd_ss.c_str()); + if (WIFEXITED(status)) { + // This is normal program exit, WEXITSTATUS() will help you shift the status + // accordingly (status >> 8) + // Becareful if the final status is 2 or beyond, program will not error + // as it is treated as CMD_EXEC_MINOR_ERROR + return WEXITSTATUS(status); + } + // Program maybe terminated because of various killed or stopped signal + return CMD_EXEC_FATAL_ERROR; } } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_bitstream_template.h b/openfpga/src/base/openfpga_bitstream_template.h index c132e907f..af5bf1ae7 100644 --- a/openfpga/src/base/openfpga_bitstream_template.h +++ b/openfpga/src/base/openfpga_bitstream_template.h @@ -16,6 +16,7 @@ #include "openfpga_digest.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" +#include "overwrite_bitstream.h" #include "read_xml_arch_bitstream.h" #include "report_bitstream_distribution.h" #include "vtr_log.h" @@ -47,6 +48,10 @@ int fpga_bitstream_template(T& openfpga_ctx, const Command& cmd, g_vpr_ctx, openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose)); } + overwrite_bitstream(openfpga_ctx.mutable_bitstream_manager(), + openfpga_ctx.bitstream_setting(), + cmd_context.option_enable(cmd, opt_verbose)); + if (true == cmd_context.option_enable(cmd, opt_write_file)) { std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_write_file)); diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 9d7b89789..a3fb26432 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -230,6 +230,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_routing_annotation(), vpr_ctx.device().rr_graph, openfpga_ctx.device_rr_gsb(), openfpga_ctx.flow_manager().compress_routing(), verbose); + VTR_LOGV(verbose, "Done\n"); VTR_LOGV(verbose, "Decoded %lu configuration bits into %lu blocks\n", diff --git a/openfpga/src/fpga_bitstream/overwrite_bitstream.cpp b/openfpga/src/fpga_bitstream/overwrite_bitstream.cpp new file mode 100644 index 000000000..e25d63e94 --- /dev/null +++ b/openfpga/src/fpga_bitstream/overwrite_bitstream.cpp @@ -0,0 +1,38 @@ +/******************************************************************** + * This file includes functions to build bitstream from a mapped + * FPGA fabric. + * We decode the bitstream from configuration of routing multiplexers + * and Look-Up Tables (LUTs) which locate in CLBs and global routing + *architecture + *******************************************************************/ + +/* Headers from vtrutil library */ +#include "overwrite_bitstream.h" + +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Overwrite bitstream retrieve from bitstream annotation XML which stored in + *BitstreamSetting + *******************************************************************/ +void overwrite_bitstream(openfpga::BitstreamManager& bitstream_manager, + const openfpga::BitstreamSetting& bitstream_setting, + const bool& verbose) { + vtr::ScopedStartFinishTimer timer("\nOverwrite Bitstream\n"); + + /* Apply overwrite_bitstream bit's path and value */ + for (auto& id : bitstream_setting.overwrite_bitstreams()) { + std::string path = bitstream_setting.overwrite_bitstream_path(id); + bool value = bitstream_setting.overwrite_bitstream_value(id); + VTR_LOGV(verbose, "Overwrite bitstream path='%s' to value='%d'\n", + path.c_str(), value); + bitstream_manager.overwrite_bitstream(path, value); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/overwrite_bitstream.h b/openfpga/src/fpga_bitstream/overwrite_bitstream.h new file mode 100644 index 000000000..de142232b --- /dev/null +++ b/openfpga/src/fpga_bitstream/overwrite_bitstream.h @@ -0,0 +1,23 @@ +#ifndef OVERWRITE_BITSTREAM_H +#define OVERWRITE_BITSTREAM_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void overwrite_bitstream(openfpga::BitstreamManager& bitstream_manager, + const openfpga::BitstreamSetting& bitstream_setting, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh index 174b4cabe..8fc5e022c 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -15,6 +15,9 @@ echo -e "Testing bitstream generation for an 48x48 FPGA device"; run-task fpga_bitstream/generate_bitstream/configuration_chain/device_48x48 $@ run-task fpga_bitstream/generate_bitstream/ql_memory_bank_shift_register/device_48x48 $@ +echo -e "Testing bitstream generation for an 4x4 FPGA device (randomly overwrite fabric bits)"; +run-task fpga_bitstream/overwrite_bitstream/device_4x4 $@ + echo -e "Testing bitstream generation for an 96x96 FPGA device"; run-task fpga_bitstream/generate_bitstream/configuration_chain/device_96x96 $@ run-task fpga_bitstream/generate_bitstream/ql_memory_bank_shift_register/device_72x72 $@ diff --git a/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/task.conf new file mode 100644 index 000000000..67a1e028c --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/task.conf @@ -0,0 +1,37 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = false +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_bitstream_setting_file=bitstream_annotation.xml +openfpga_vpr_device_layout=4x4 +openfpga_ext_exec_python_script=${PATH:TASK_DIR}/config/test.py + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif + +[SYNTHESIS_PARAM] +bench0_top = and2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] diff --git a/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.openfpga b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.openfpga new file mode 100644 index 000000000..1e9e3f79c --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.openfpga @@ -0,0 +1,53 @@ +# Majority of the content refer to fix_device_example_script.openfpga + +ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} run_golden ${OPENFPGA_PATH}" + +ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} generate_testcase" + +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --device ${OPENFPGA_VPR_DEVICE_LAYOUT} + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE} + +# Read OpenFPGA bitstream settings +read_openfpga_bitstream_setting -f ${OPENFPGA_BITSTREAM_SETTING_FILE} + +# Annotate the OpenFPGA architecture to VPR data base +# to debug use --verbose options +link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges + +# Check and correct any naming conflicts in the BLIF netlist +check_netlist_naming_conflict --fix --report ./netlist_renaming.xml + +# Apply fix-up to Look-Up Table truth tables based on packing results +lut_truth_table_fixup + +# Build the module graph +# - Enabled compression on routing architecture modules +# - Enable pin duplication on grid modules +build_fabric --compress_routing #--verbose + +# Repack the netlist to physical pbs +# This must be done before bitstream generator and testbench generation +# Strongly recommend it is done after all the fix-up have been applied +repack #--verbose + +# Build the bitstream +# - Output the fabric-independent bitstream to a file +build_architecture_bitstream --verbose + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.xml --format xml + +ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} validate" + +# Finish and exit OpenFPGA +exit diff --git a/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.py b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.py new file mode 100644 index 000000000..2997bb5d6 --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.py @@ -0,0 +1,120 @@ +import xml.etree.ElementTree as ET +import sys +import shutil +import os +import random + +random.seed() +assert len(sys.argv) >= 2 +assert sys.argv[1] in ["run_golden", "generate_testcase", "validate"] +TEST_BIT_COUNT = 200 + +def read_fabric_bitstream_xml(file) : + + bit_count = 0 + tree = ET.parse(file) + root = tree.getroot() + assert root.tag == "fabric_bitstream", "Root tag is not 'fabric_bitstream', but '%s'" % root.tag + for region in root : + assert region.tag == "region", "fabric_bitstream child node tag is not 'region', but '%s'" % region.tag + for bit in region : + assert bit.tag == "bit", "region child node tag is not 'bit', but '%s'" % bit.tag + assert "path" in bit.attrib, "Attribute 'path' does not exist in bit node" + assert "value" in bit.attrib, "Attribute 'value' does not exist in bit node" + assert bit.attrib["value"] in ["0", "1"] + bit_count += 1 + return [tree, bit_count] + +def read_bitstream_annotation_xml(file) : + + xml = {} + tree = ET.parse(file) + root = tree.getroot() + assert root.tag == "openfpga_bitstream_setting", "Root tag is not 'openfpga_bitstream_setting', but '%s'" % root.tag + for overwrite_bitstream in root : + assert overwrite_bitstream.tag == "overwrite_bitstream", "openfpga_bitstream_setting child node tag is not 'overwrite_bitstream', but '%s'" % overwrite_bitstream.tag + for bit in overwrite_bitstream : + assert bit.tag == "bit", "overwrite_bitstream child node tag is not 'bit', but '%s'" % bit.tag + assert "path" in bit.attrib, "Attribute 'path' does not exist in bit node" + assert "value" in bit.attrib, "Attribute 'value' does not exist in bit node" + path = bit.attrib["path"] + assert path not in xml + index = path.rfind("[") + assert index != -1 + path = "%s.mem_out%s" % (path[:index], path[index:]) + assert path not in xml + assert bit.attrib["value"] in ["0", "1"] + xml[path] = bit.attrib["value"] + return xml + +if sys.argv[1] == "run_golden" : + + assert len(sys.argv) >= 3 + openfpga_exe = os.path.abspath("%s/build/openfpga/openfpga" % sys.argv[2]) + assert os.path.exists(openfpga_exe) + shutil.rmtree("golden", ignore_errors=True) + os.mkdir("golden") + original_openfpga = open("and2_run.openfpga") + golden_openfpga = open("golden/and2_run.openfpga", "w") + for line in original_openfpga : + if line.find("ext_exec") == 0 : + pass + else : + golden_openfpga.write(line) + golden_openfpga.close() + original_openfpga.close() + bitstream_annotation = open("golden/bitstream_annotation.xml", "w") + bitstream_annotation.write("<openfpga_bitstream_setting/>\n") + bitstream_annotation.close() + shutil.copyfile("and2.blif", "golden/and2.blif") + shutil.copyfile("and2_ace_out.act", "golden/and2_ace_out.act") + cmd = "cd golden && %s -batch -f and2_run.openfpga > golden.log" % (openfpga_exe) + assert os.system(cmd) == 0 + +elif sys.argv[1] == "generate_testcase" : + + (tree, bit_count) = read_fabric_bitstream_xml("golden/fabric_bitstream.xml") + random_bits = [] + while len(random_bits) != TEST_BIT_COUNT : + bit = random.randint(0, bit_count - 1) + if bit not in random_bits : + random_bits.append(bit) + bitstream_annotation = open("bitstream_annotation.xml", "w") + bitstream_annotation.write("<openfpga_bitstream_setting>\n") + bitstream_annotation.write(" <overwrite_bitstream>\n") + index = 0 + for region in tree.getroot() : + for bit in region : + if index in random_bits : + path = bit.attrib["path"] + value = bit.attrib["value"] + assert value in ["0", "1"] + path = path.replace(".mem_out[", "[") + bitstream_annotation.write(" <bit value=\"%s\" path=\"%s\"/>\n" % ("1" if value == "0" else "0", path)) + index += 1 + bitstream_annotation.write(" </overwrite_bitstream>\n") + bitstream_annotation.write("</openfpga_bitstream_setting>\n") + bitstream_annotation.close() + +else : + + gtree = ET.parse("golden/fabric_bitstream.xml") + tree = ET.parse("fabric_bitstream.xml") + bitstream_annotation = read_bitstream_annotation_xml("bitstream_annotation.xml") + checked_count = 0 + for gregion, region in zip(gtree.getroot(), tree.getroot()) : + for gbit, bit in zip(gregion, region) : + assert bit.attrib["path"] == gbit.attrib["path"] + path = bit.attrib["path"] + if path in bitstream_annotation : + # This is something we want to overwrite, hence the value should + # Same in the annotation file + # Not same in golden fabric + assert bit.attrib["value"] != gbit.attrib["value"] + assert bit.attrib["value"] == bitstream_annotation[path] + else : + # This is not what we want to overwrite + # Hence the value should same in golden fabric + assert bit.attrib["value"] == gbit.attrib["value"] + +exit(0)