From 5364b94cf8dca11f3e56e464140b628fd549c4ec Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 13:42:12 -0600 Subject: [PATCH 001/164] [Tool] Update bitstream setting parser/writer to support interconnect-related syntax --- .../libarchopenfpga/src/bitstream_setting.cpp | 41 ++++++++++++++ .../libarchopenfpga/src/bitstream_setting.h | 31 +++++++++++ .../src/bitstream_setting_fwd.h | 2 + .../src/read_xml_bitstream_setting.cpp | 35 ++++++++++-- .../src/write_xml_bitstream_setting.cpp | 53 ++++++++++++++++++- 5 files changed, 157 insertions(+), 5 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/bitstream_setting.cpp b/libopenfpga/libarchopenfpga/src/bitstream_setting.cpp index f7a773dad..f239a78b3 100644 --- a/libopenfpga/libarchopenfpga/src/bitstream_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/bitstream_setting.cpp @@ -16,6 +16,10 @@ BitstreamSetting::bitstream_pb_type_setting_range BitstreamSetting::pb_type_sett return vtr::make_range(pb_type_setting_ids_.begin(), pb_type_setting_ids_.end()); } +BitstreamSetting::bitstream_interconnect_setting_range BitstreamSetting::interconnect_settings() const { + return vtr::make_range(interconnect_setting_ids_.begin(), interconnect_setting_ids_.end()); +} + /************************************************************************ * Constructors ***********************************************************************/ @@ -61,6 +65,25 @@ size_t BitstreamSetting::bitstream_offset(const BitstreamPbTypeSettingId& pb_typ return bitstream_offsets_[pb_type_setting_id]; } +std::string BitstreamSetting::interconnect_name(const BitstreamInterconnectSettingId& interconnect_setting_id) const { + VTR_ASSERT(true == valid_bitstream_interconnect_setting_id(interconnect_setting_id)); + return interconnect_names_[interconnect_setting_id]; +} + +std::vector BitstreamSetting::parent_pb_type_names(const BitstreamInterconnectSettingId& interconnect_setting_id) const { + VTR_ASSERT(true == valid_bitstream_interconnect_setting_id(interconnect_setting_id)); + return interconnect_parent_pb_type_names_[interconnect_setting_id]; +} + +std::vector BitstreamSetting::parent_mode_names(const BitstreamInterconnectSettingId& interconnect_setting_id) const { + VTR_ASSERT(true == valid_bitstream_interconnect_setting_id(interconnect_setting_id)); + return interconnect_parent_mode_names_[interconnect_setting_id]; +} + +std::string BitstreamSetting::default_path(const BitstreamInterconnectSettingId& interconnect_setting_id) const { + VTR_ASSERT(true == valid_bitstream_interconnect_setting_id(interconnect_setting_id)); + return interconnect_default_paths_[interconnect_setting_id]; +} /************************************************************************ * Public Mutators @@ -95,6 +118,20 @@ void BitstreamSetting::set_bitstream_offset(const BitstreamPbTypeSettingId& pb_t bitstream_offsets_[pb_type_setting_id] = offset; } +BitstreamInterconnectSettingId BitstreamSetting::add_bitstream_interconnect_setting(const std::string& interconnect_name, + const std::vector& parent_pb_type_names, + const std::vector& parent_mode_names, + const std::string& default_path) { + BitstreamInterconnectSettingId interc_setting_id = BitstreamInterconnectSettingId(interconnect_setting_ids_.size()); + interconnect_setting_ids_.push_back(interc_setting_id); + interconnect_names_.push_back(interconnect_name); + interconnect_parent_pb_type_names_.push_back(parent_pb_type_names); + interconnect_parent_mode_names_.push_back(parent_mode_names); + interconnect_default_paths_.push_back(default_path); + + return interc_setting_id; +} + /************************************************************************ * Public Validators ***********************************************************************/ @@ -102,4 +139,8 @@ bool BitstreamSetting::valid_bitstream_pb_type_setting_id(const BitstreamPbTypeS return ( size_t(pb_type_setting_id) < pb_type_setting_ids_.size() ) && ( pb_type_setting_id == pb_type_setting_ids_[pb_type_setting_id] ); } +bool BitstreamSetting::valid_bitstream_interconnect_setting_id(const BitstreamInterconnectSettingId& interconnect_setting_id) const { + return ( size_t(interconnect_setting_id) < interconnect_setting_ids_.size() ) && ( interconnect_setting_id == interconnect_setting_ids_[interconnect_setting_id] ); +} + } /* namespace openfpga ends */ diff --git a/libopenfpga/libarchopenfpga/src/bitstream_setting.h b/libopenfpga/libarchopenfpga/src/bitstream_setting.h index a972bb6c4..63cb9092b 100644 --- a/libopenfpga/libarchopenfpga/src/bitstream_setting.h +++ b/libopenfpga/libarchopenfpga/src/bitstream_setting.h @@ -16,6 +16,10 @@ namespace openfpga { /******************************************************************** * A data structure to describe bitstream settings + * + * This data structure includes following types of settings: + * - Pb type: include definiting hard coded bitstream for pb_types (LUT or configurable pb_type for mode selection) + * - Interconnect: include defining default paths for routing multiplexers in pb_types * * Typical usage: * -------------- @@ -27,12 +31,15 @@ namespace openfpga { class BitstreamSetting { public: /* Types */ typedef vtr::vector::const_iterator bitstream_pb_type_setting_iterator; + typedef vtr::vector::const_iterator bitstream_interconnect_setting_iterator; /* Create range */ typedef vtr::Range bitstream_pb_type_setting_range; + typedef vtr::Range bitstream_interconnect_setting_range; public: /* Constructors */ BitstreamSetting(); public: /* Accessors: aggregates */ bitstream_pb_type_setting_range pb_type_settings() const; + bitstream_interconnect_setting_range interconnect_settings() const; public: /* Public Accessors */ std::string pb_type_name(const BitstreamPbTypeSettingId& pb_type_setting_id) const; std::vector parent_pb_type_names(const BitstreamPbTypeSettingId& pb_type_setting_id) const; @@ -41,6 +48,10 @@ class BitstreamSetting { std::string pb_type_bitstream_content(const BitstreamPbTypeSettingId& pb_type_setting_id) const; bool is_mode_select_bitstream(const BitstreamPbTypeSettingId& pb_type_setting_id) const; size_t bitstream_offset(const BitstreamPbTypeSettingId& pb_type_setting_id) const; + std::string interconnect_name(const BitstreamInterconnectSettingId& interconnect_setting_id) const; + std::vector parent_pb_type_names(const BitstreamInterconnectSettingId& interconnect_setting_id) const; + std::vector parent_mode_names(const BitstreamInterconnectSettingId& interconnect_setting_id) const; + std::string default_path(const BitstreamInterconnectSettingId& interconnect_setting_id) const; public: /* Public Mutators */ BitstreamPbTypeSettingId add_bitstream_pb_type_setting(const std::string& pb_type_name, const std::vector& parent_pb_type_names, @@ -51,9 +62,19 @@ class BitstreamSetting { const bool& is_mode_select_bitstream); void set_bitstream_offset(const BitstreamPbTypeSettingId& pb_type_setting_id, const size_t& offset); + + BitstreamInterconnectSettingId add_bitstream_interconnect_setting(const std::string& interconnect_name, + const std::vector& parent_pb_type_names, + const std::vector& parent_mode_names, + const std::string& default_path); 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; private: /* Internal data */ + /* Pb type -related settings + * - Paths to a pb_type in the pb_graph + * - Bitstream source, data_type, offsets etc. + */ vtr::vector pb_type_setting_ids_; vtr::vector pb_type_names_; vtr::vector> parent_pb_type_names_; @@ -64,6 +85,16 @@ class BitstreamSetting { vtr::vector is_mode_select_bitstreams_; /* The offset that the bitstream is applied to the original bitstream of a pb_type */ vtr::vector bitstream_offsets_; + + /* Interconnect-related settings: + * - Name of interconnect under a given pb_type + * - The default path to be considered for a given interconnect during bitstream generation + */ + vtr::vector interconnect_setting_ids_; + vtr::vector interconnect_names_; + vtr::vector> interconnect_parent_pb_type_names_; + vtr::vector> interconnect_parent_mode_names_; + vtr::vector interconnect_default_paths_; }; } /* namespace openfpga ends */ diff --git a/libopenfpga/libarchopenfpga/src/bitstream_setting_fwd.h b/libopenfpga/libarchopenfpga/src/bitstream_setting_fwd.h index 009bcbfed..d5bb618e1 100644 --- a/libopenfpga/libarchopenfpga/src/bitstream_setting_fwd.h +++ b/libopenfpga/libarchopenfpga/src/bitstream_setting_fwd.h @@ -13,8 +13,10 @@ #include "vtr_strong_id.h" struct bitstream_pb_type_setting_id_tag; +struct bitstream_interconnect_setting_id_tag; typedef vtr::StrongId BitstreamPbTypeSettingId; +typedef vtr::StrongId BitstreamInterconnectSettingId; /* Short declaration of class */ class BitstreamSetting; diff --git a/libopenfpga/libarchopenfpga/src/read_xml_bitstream_setting.cpp b/libopenfpga/libarchopenfpga/src/read_xml_bitstream_setting.cpp index 181ee531c..05ffc492d 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_bitstream_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_bitstream_setting.cpp @@ -50,6 +50,26 @@ void read_xml_bitstream_pb_type_setting(pugi::xml_node& xml_pb_type, bitstream_setting.set_bitstream_offset(bitstream_pb_type_id, offset); } +/******************************************************************** + * Parse XML description for a pb_type annotation under a XML node + *******************************************************************/ +static +void read_xml_bitstream_interconnect_setting(pugi::xml_node& xml_pb_type, + const pugiutil::loc_data& loc_data, + openfpga::BitstreamSetting& bitstream_setting) { + const std::string& name_attr = get_attribute(xml_pb_type, "name", loc_data).as_string(); + const std::string& default_path_attr = get_attribute(xml_pb_type, "default_path", loc_data).as_string(); + + /* Parse the attributes for operating pb_type */ + openfpga::PbParser operating_pb_parser(name_attr); + + /* Add to bitstream setting */ + bitstream_setting.add_bitstream_interconnect_setting(operating_pb_parser.leaf(), + operating_pb_parser.parents(), + operating_pb_parser.modes(), + default_path_attr); +} + /******************************************************************** * Parse XML codes about to an object *******************************************************************/ @@ -60,12 +80,19 @@ openfpga::BitstreamSetting read_xml_bitstream_setting(pugi::xml_node& Node, /* Iterate over the children under this node, * each child should be named after */ - for (pugi::xml_node xml_pb_type : Node.children()) { + for (pugi::xml_node xml_child : Node.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_pb_type.name() != std::string("pb_type")) { - bad_tag(xml_pb_type, loc_data, Node, {"pb_type"}); + if ( (xml_child.name() != std::string("pb_type")) + && (xml_child.name() != std::string("interconnect")) ) { + bad_tag(xml_child, loc_data, Node, {"pb_type | interconnect"}); + } + + if (xml_child.name() == std::string("pb_type")) { + read_xml_bitstream_pb_type_setting(xml_child, loc_data, bitstream_setting); + } else { + VTR_ASSERT_SAFE(xml_child.name() == std::string("interconnect")); + read_xml_bitstream_interconnect_setting(xml_child, loc_data, bitstream_setting); } - read_xml_bitstream_pb_type_setting(xml_pb_type, loc_data, bitstream_setting); } return bitstream_setting; diff --git a/libopenfpga/libarchopenfpga/src/write_xml_bitstream_setting.cpp b/libopenfpga/libarchopenfpga/src/write_xml_bitstream_setting.cpp index 51600cf4f..1754fbc4b 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_bitstream_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_bitstream_setting.cpp @@ -39,6 +39,31 @@ std::string generate_bitstream_setting_pb_type_hierarchy_name(const openfpga::Bi return hie_name; } +/******************************************************************** + * Generate the full hierarchy name for an interconnect in bitstream setting + *******************************************************************/ +static +std::string generate_bitstream_setting_interconnect_hierarchy_name(const openfpga::BitstreamSetting& bitstream_setting, + const BitstreamInterconnectSettingId& bitstream_interc_setting_id) { + /* Iterate over the parent_pb_type and modes names, they should well match */ + VTR_ASSERT_SAFE(bitstream_setting.parent_pb_type_names(bitstream_interc_setting_id).size() == bitstream_setting.parent_mode_names(bitstream_interc_setting_id).size()); + + std::string hie_name; + + for (size_t i = 0 ; i < bitstream_setting.parent_pb_type_names(bitstream_interc_setting_id).size(); ++i) { + hie_name += bitstream_setting.parent_pb_type_names(bitstream_interc_setting_id)[i]; + hie_name += std::string("["); + hie_name += bitstream_setting.parent_mode_names(bitstream_interc_setting_id)[i]; + hie_name += std::string("]"); + hie_name += std::string("."); + } + + /* Add the leaf pb_type */ + hie_name += bitstream_setting.interconnect_name(bitstream_interc_setting_id); + + return hie_name; +} + /******************************************************************** * A writer to output a bitstream pb_type setting to XML format *******************************************************************/ @@ -63,6 +88,27 @@ void write_xml_bitstream_pb_type_setting(std::fstream& fp, fp << "/>" << "\n"; } +/******************************************************************** + * A writer to output a bitstream interconnect setting to XML format + *******************************************************************/ +static +void write_xml_bitstream_interconnect_setting(std::fstream& fp, + const char* fname, + const openfpga::BitstreamSetting& bitstream_setting, + const BitstreamInterconnectSettingId& bitstream_interc_setting_id) { + /* Validate the file stream */ + openfpga::check_file_stream(fname, fp); + + fp << "\t" << "" << "\n"; +} + /******************************************************************** * A writer to output a bitstream setting to XML format *******************************************************************/ @@ -76,11 +122,16 @@ void write_xml_bitstream_setting(std::fstream& fp, */ fp << "" << "\n"; - /* Write clock settings */ + /* Write pb_type -related settings */ for (const auto& bitstream_pb_type_setting_id : bitstream_setting.pb_type_settings()) { write_xml_bitstream_pb_type_setting(fp, fname, bitstream_setting, bitstream_pb_type_setting_id); } + /* Write interconnect -related settings */ + for (const auto& bitstream_interc_setting_id : bitstream_setting.interconnect_settings()) { + write_xml_bitstream_interconnect_setting(fp, fname, bitstream_setting, bitstream_interc_setting_id); + } + /* Write the root node */ fp << "" << "\n"; } From 0aec30bac6a33e5d14abbdb550be6c9a1f9699c0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 15:53:33 -0600 Subject: [PATCH 002/164] [Tool] Update FPGA core engine to support mux default path overloading through bitstream setting file --- .../annotation/annotate_bitstream_setting.cpp | 141 +++++++++++++++++- .../annotation/annotate_bitstream_setting.h | 1 + .../annotation/vpr_bitstream_annotation.cpp | 16 ++ .../src/annotation/vpr_bitstream_annotation.h | 9 ++ openfpga/src/base/openfpga_link_arch.cpp | 2 + .../fpga_bitstream/build_device_bitstream.cpp | 1 + .../fpga_bitstream/build_grid_bitstream.cpp | 38 +++-- .../src/fpga_bitstream/build_grid_bitstream.h | 2 + 8 files changed, 195 insertions(+), 15 deletions(-) diff --git a/openfpga/src/annotation/annotate_bitstream_setting.cpp b/openfpga/src/annotation/annotate_bitstream_setting.cpp index 48002a08b..a51e73fde 100644 --- a/openfpga/src/annotation/annotate_bitstream_setting.cpp +++ b/openfpga/src/annotation/annotate_bitstream_setting.cpp @@ -12,6 +12,9 @@ #include "vtr_assert.h" #include "vtr_log.h" +/* Headers from openfpgautil library */ +#include "openfpga_tokenizer.h" + #include "pb_type_utils.h" #include "annotate_bitstream_setting.h" @@ -23,9 +26,10 @@ namespace openfpga { * - Find the pb_type and link to the bitstream source * - Find the pb_type and link to the bitstream content *******************************************************************/ -int annotate_bitstream_setting(const BitstreamSetting& bitstream_setting, - const DeviceContext& vpr_device_ctx, - VprBitstreamAnnotation& vpr_bitstream_annotation) { +static +int annotate_bitstream_pb_type_setting(const BitstreamSetting& bitstream_setting, + const DeviceContext& vpr_device_ctx, + VprBitstreamAnnotation& vpr_bitstream_annotation) { for (const auto& bitstream_pb_type_setting_id : bitstream_setting.pb_type_settings()) { /* Get the full name of pb_type */ @@ -95,4 +99,135 @@ int annotate_bitstream_setting(const BitstreamSetting& bitstream_setting, return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Annotate bitstream setting based on VPR device information + * - Find the interconnect and link to the default path id + *******************************************************************/ +static +int annotate_bitstream_interconnect_setting(const BitstreamSetting& bitstream_setting, + const DeviceContext& vpr_device_ctx, + const VprDeviceAnnotation& vpr_device_annotation, + VprBitstreamAnnotation& vpr_bitstream_annotation) { + + for (const auto& bitstream_interc_setting_id : bitstream_setting.interconnect_settings()) { + /* Get the full name of pb_type */ + std::vector target_pb_type_names; + std::vector target_pb_mode_names; + + target_pb_type_names = bitstream_setting.parent_pb_type_names(bitstream_interc_setting_id); + target_pb_mode_names = bitstream_setting.parent_mode_names(bitstream_interc_setting_id); + /* Kick out the last mode so that we can use an existing function search the pb_type in graph */ + std::string expected_physical_mode_name = target_pb_mode_names.back(); + target_pb_mode_names.pop_back(); + + std::string interconnect_name = bitstream_setting.interconnect_name(bitstream_interc_setting_id); + std::string expected_input_path = bitstream_setting.default_path(bitstream_interc_setting_id); + + /* Pb type information are located at the logic_block_types in the device context of VPR + * We iterate over the vectors and find the pb_type matches the parent_pb_type_name + */ + bool link_success = false; + + for (const t_logical_block_type& lb_type : vpr_device_ctx.logical_block_types) { + /* By pass nullptr for pb_type head */ + if (nullptr == lb_type.pb_type) { + continue; + } + /* Check the name of the top-level pb_type, if it does not match, we can bypass */ + if (target_pb_type_names[0] != std::string(lb_type.pb_type->name)) { + continue; + } + /* Match the name in the top-level, we go further to search the pb_type in the graph */ + t_pb_type* target_pb_type = try_find_pb_type_with_given_path(lb_type.pb_type, target_pb_type_names, + target_pb_mode_names); + if (nullptr == target_pb_type) { + continue; + } + + /* Found one, build annotation */ + t_mode* physical_mode = vpr_device_annotation.physical_mode(target_pb_type); + + VTR_ASSERT(nullptr != physical_mode); + /* Ensure that the annotation is only applicable to physical mode */ + if (std::string(physical_mode->name) != expected_physical_mode_name) { + VTR_LOG_ERROR("The physical mode '%s' under pb_type '%s' does not match in the bitstream setting '%s'!\n", + physical_mode->name, + target_pb_type->name, + expected_physical_mode_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the interconnect name under the physical mode of a physical pb_type */ + t_interconnect* pb_interc = find_pb_mode_interconnect(physical_mode, interconnect_name.c_str()); + + if (nullptr == pb_interc) { + VTR_LOG_ERROR("Unable to find interconnect '%s' under physical mode '%s' of pb_type '%s'!\n", + interconnect_name.c_str(), + physical_mode->name, + target_pb_type->name); + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the default path and spot the path id from the input string recorded */ + StringToken input_string_tokenizer(std::string(pb_interc->input_string)); + std::vector input_paths = input_string_tokenizer.split(' '); + size_t input_path_id = input_paths.size(); + for (size_t ipath = 0; ipath < input_paths.size(); ++ipath) { + if (expected_input_path == input_paths[ipath]) { + input_path_id = ipath; + break; + } + } + /* If the input_path id is invalid, error out! */ + if (input_path_id == input_paths.size()) { + VTR_LOG_ERROR("Invalid default path '%s' for interconnect '%s' which inputs are '%s'\n", + expected_input_path.c_str(), + interconnect_name.c_str(), + pb_interc->input_string); + return CMD_EXEC_FATAL_ERROR; + } + + vpr_bitstream_annotation.set_interconnect_default_path_id(pb_interc, input_path_id); + + link_success = true; + } + + /* If fail to link bitstream setting to architecture, error out immediately */ + if (false == link_success) { + VTR_LOG_ERROR("Fail to find an interconnect '%s' with default path '%s', which is defined in bitstream setting from VPR architecture description\n", + interconnect_name.c_str(), + expected_input_path.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Annotate bitstream setting based on VPR device information + * - Fill pb_type -related mapping + * - Fill interconnect -related mapping + *******************************************************************/ +int annotate_bitstream_setting(const BitstreamSetting& bitstream_setting, + const DeviceContext& vpr_device_ctx, + const VprDeviceAnnotation& vpr_device_annotation, + VprBitstreamAnnotation& vpr_bitstream_annotation) { + + int status = CMD_EXEC_SUCCESS; + + status = annotate_bitstream_pb_type_setting(bitstream_setting, + vpr_device_ctx, + vpr_bitstream_annotation); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } + + status = annotate_bitstream_interconnect_setting(bitstream_setting, + vpr_device_ctx, vpr_device_annotation, + vpr_bitstream_annotation); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/annotation/annotate_bitstream_setting.h b/openfpga/src/annotation/annotate_bitstream_setting.h index 053ca2fbe..45d6316dd 100644 --- a/openfpga/src/annotation/annotate_bitstream_setting.h +++ b/openfpga/src/annotation/annotate_bitstream_setting.h @@ -16,6 +16,7 @@ namespace openfpga { int annotate_bitstream_setting(const BitstreamSetting& bitstream_setting, const DeviceContext& vpr_device_ctx, + const VprDeviceAnnotation& vpr_device_annotation, VprBitstreamAnnotation& vpr_bitstream_annotation); } /* end namespace openfpga */ diff --git a/openfpga/src/annotation/vpr_bitstream_annotation.cpp b/openfpga/src/annotation/vpr_bitstream_annotation.cpp index 3361d598c..38842cbff 100644 --- a/openfpga/src/annotation/vpr_bitstream_annotation.cpp +++ b/openfpga/src/annotation/vpr_bitstream_annotation.cpp @@ -4,6 +4,7 @@ #include "vtr_log.h" #include "vtr_assert.h" #include "vpr_bitstream_annotation.h" +#include "mux_bitstream_constants.h" /* namespace openfpga begins */ namespace openfpga { @@ -78,6 +79,16 @@ size_t VprBitstreamAnnotation::pb_type_mode_select_bitstream_offset(t_pb_type* p return 0; } +size_t VprBitstreamAnnotation::interconnect_default_path_id(t_interconnect* interconnect) const { + auto result = interconnect_default_path_ids_.find(interconnect); + if (result != interconnect_default_path_ids_.end()) { + return result->second; + } + + /* Not found, return an invalid input id */ + return DEFAULT_PATH_ID; +} + /************************************************************************ * Public mutators ***********************************************************************/ @@ -111,4 +122,9 @@ void VprBitstreamAnnotation::set_pb_type_mode_select_bitstream_offset(t_pb_type* mode_select_bitstream_offsets_[pb_type] = offset; } +void VprBitstreamAnnotation::set_interconnect_default_path_id(t_interconnect* interconnect, + const size_t& default_path_id) { + interconnect_default_path_ids_[interconnect] = default_path_id; +} + } /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/vpr_bitstream_annotation.h b/openfpga/src/annotation/vpr_bitstream_annotation.h index c562e4aa2..134530f25 100644 --- a/openfpga/src/annotation/vpr_bitstream_annotation.h +++ b/openfpga/src/annotation/vpr_bitstream_annotation.h @@ -38,6 +38,7 @@ class VprBitstreamAnnotation { e_bitstream_source_type pb_type_mode_select_bitstream_source(t_pb_type* pb_type) const; std::string pb_type_mode_select_bitstream_content(t_pb_type* pb_type) const; size_t pb_type_mode_select_bitstream_offset(t_pb_type* pb_type) const; + size_t interconnect_default_path_id(t_interconnect* interconnect) const; public: /* Public mutators */ void set_pb_type_bitstream_source(t_pb_type* pb_type, const e_bitstream_source_type& bitstream_source); @@ -52,6 +53,8 @@ class VprBitstreamAnnotation { const std::string& bitstream_content); void set_pb_type_mode_select_bitstream_offset(t_pb_type* pb_type, const size_t& offset); + void set_interconnect_default_path_id(t_interconnect* interconnect, + const size_t& default_path_id); private: /* Internal data */ /* For regular bitstreams */ /* A look up for pb type to find bitstream source type */ @@ -68,6 +71,12 @@ class VprBitstreamAnnotation { std::map mode_select_bitstream_contents_; /* Offset to be applied to mode-select bitstream */ std::map mode_select_bitstream_offsets_; + + /* A look up for interconnect to find default path indices + * Note: this is different from the default path in bitstream setting which is the index + * of inputs in the context of the interconnect input string + */ + std::map interconnect_default_path_ids_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/base/openfpga_link_arch.cpp b/openfpga/src/base/openfpga_link_arch.cpp index 63a0b0430..3da3f3ae1 100644 --- a/openfpga/src/base/openfpga_link_arch.cpp +++ b/openfpga/src/base/openfpga_link_arch.cpp @@ -177,7 +177,9 @@ int link_arch(OpenfpgaContext& openfpga_ctx, /* Build bitstream annotation based on bitstream settings */ if (CMD_EXEC_FATAL_ERROR == annotate_bitstream_setting(openfpga_ctx.bitstream_setting(), g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.mutable_vpr_bitstream_annotation())) { + return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 1fb2360e9..e90d73321 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -189,6 +189,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_clustering_annotation(), openfpga_ctx.vpr_placement_annotation(), + openfpga_ctx.vpr_bitstream_annotation(), verbose); VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index f0cfab7dd..d5241a451 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -129,6 +129,7 @@ void build_physical_block_pin_interc_bitstream(BitstreamManager& bitstream_manag const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const VprBitstreamAnnotation& bitstream_annotation, const PhysicalPb& physical_pb, t_pb_graph_pin* des_pb_graph_pin, t_mode* physical_mode) { @@ -197,6 +198,12 @@ void build_physical_block_pin_interc_bitstream(BitstreamManager& bitstream_manag } } + /* Overwrite the default path if defined in bitstream annotation */ + if ( (size_t(DEFAULT_PATH_ID) == mux_input_pin_id) + && (mux_input_pin_id != bitstream_annotation.interconnect_default_path_id(cur_interc)) ) { + mux_input_pin_id = bitstream_annotation.interconnect_default_path_id(cur_interc); + } + /* Generate bitstream depend on both technology and structure of this MUX */ std::vector mux_bitstream = build_mux_bitstream(circuit_lib, mux_model, mux_lib, datapath_mux_size, mux_input_pin_id); @@ -266,6 +273,7 @@ void build_physical_block_interc_port_bitstream(BitstreamManager& bitstream_mana const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const VprBitstreamAnnotation& bitstream_annotation, t_pb_graph_node* physical_pb_graph_node, const PhysicalPb& physical_pb, const e_circuit_pb_port_type& pb_port_type, @@ -276,7 +284,7 @@ void build_physical_block_interc_port_bitstream(BitstreamManager& bitstream_mana for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->input_pins[iport][ipin]), physical_mode); @@ -288,7 +296,7 @@ void build_physical_block_interc_port_bitstream(BitstreamManager& bitstream_mana for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->output_pins[iport][ipin]), physical_mode); @@ -300,7 +308,7 @@ void build_physical_block_interc_port_bitstream(BitstreamManager& bitstream_mana for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) { build_physical_block_pin_interc_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, physical_pb, &(physical_pb_graph_node->clock_pins[iport][ipin]), physical_mode); @@ -327,6 +335,7 @@ void build_physical_block_interc_bitstream(BitstreamManager& bitstream_manager, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const VprBitstreamAnnotation& bitstream_annotation, t_pb_graph_node* physical_pb_graph_node, const PhysicalPb& physical_pb, t_mode* physical_mode) { @@ -348,7 +357,7 @@ void build_physical_block_interc_bitstream(BitstreamManager& bitstream_manager, */ build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, physical_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_OUTPUT, physical_mode); @@ -367,13 +376,13 @@ void build_physical_block_interc_bitstream(BitstreamManager& bitstream_manager, /* For each child_pb_graph_node input pins*/ build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_INPUT, physical_mode); /* For clock pins, we should do the same work */ build_physical_block_interc_port_bitstream(bitstream_manager, parent_configurable_block, module_manager, circuit_lib, mux_lib, - atom_ctx, device_annotation, + atom_ctx, device_annotation, bitstream_annotation, child_pb_graph_node, physical_pb, CIRCUIT_PB_PORT_CLOCK, physical_mode); } @@ -534,6 +543,7 @@ void rec_build_physical_block_bitstream(BitstreamManager& bitstream_manager, const MuxLibrary& mux_lib, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const VprBitstreamAnnotation& bitstream_annotation, const e_side& border_side, const PhysicalPb& physical_pb, const PhysicalPbId& pb_id, @@ -578,7 +588,7 @@ void rec_build_physical_block_bitstream(BitstreamManager& bitstream_manager, rec_build_physical_block_bitstream(bitstream_manager, pb_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, - device_annotation, + device_annotation, bitstream_annotation, border_side, physical_pb, child_pb, &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]), @@ -623,7 +633,7 @@ void rec_build_physical_block_bitstream(BitstreamManager& bitstream_manager, build_physical_block_interc_bitstream(bitstream_manager, pb_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, - device_annotation, + device_annotation, bitstream_annotation, physical_pb_graph_node, physical_pb, physical_mode); } @@ -644,6 +654,7 @@ void build_physical_block_bitstream(BitstreamManager& bitstream_manager, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, + const VprBitstreamAnnotation& bitstream_annotation, const DeviceGrid& grids, const vtr::Point& grid_coord, const e_side& border_side) { @@ -692,7 +703,8 @@ void build_physical_block_bitstream(BitstreamManager& bitstream_manager, rec_build_physical_block_bitstream(bitstream_manager, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, - device_annotation, border_side, + device_annotation, bitstream_annotation, + border_side, PhysicalPb(), PhysicalPbId::INVALID(), lb_type->pb_graph_head, z); } else { @@ -707,7 +719,8 @@ void build_physical_block_bitstream(BitstreamManager& bitstream_manager, rec_build_physical_block_bitstream(bitstream_manager, grid_configurable_block, module_manager, circuit_lib, mux_lib, atom_ctx, - device_annotation, border_side, + device_annotation, bitstream_annotation, + border_side, phy_pb, top_pb_id, pb_graph_head, z); } } @@ -731,6 +744,7 @@ void build_grid_bitstream(BitstreamManager& bitstream_manager, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, + const VprBitstreamAnnotation& bitstream_annotation, const bool& verbose) { VTR_LOGV(verbose, "Generating bitstream for core grids..."); @@ -753,7 +767,7 @@ void build_grid_bitstream(BitstreamManager& bitstream_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, - place_annotation, + place_annotation, bitstream_annotation, grids, grid_coord, NUM_SIDES); } } @@ -780,7 +794,7 @@ void build_grid_bitstream(BitstreamManager& bitstream_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, - place_annotation, + place_annotation, bitstream_annotation, grids, io_coordinate, io_side); } } diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.h b/openfpga/src/fpga_bitstream/build_grid_bitstream.h index be2425c5a..9d2c3d010 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.h +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.h @@ -14,6 +14,7 @@ #include "vpr_device_annotation.h" #include "vpr_clustering_annotation.h" #include "vpr_placement_annotation.h" +#include "vpr_bitstream_annotation.h" /******************************************************************** * Function declaration @@ -32,6 +33,7 @@ void build_grid_bitstream(BitstreamManager& bitstream_manager, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, + const VprBitstreamAnnotation& bitstream_annotation, const bool& verbose); } /* end namespace openfpga */ From 5976cc0a1ce3e1ae9f47d525239fbabbf5cfd01c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 15:54:18 -0600 Subject: [PATCH 003/164] [Test] Add test case for using bitstream setting to overload default paths for pb_type interconnection --- .../config/bitstream_annotation.xml | 3 ++ .../config/task.conf | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml create mode 100644 openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf diff --git a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml new file mode 100644 index 000000000..2bb8ec56d --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml @@ -0,0 +1,3 @@ + + + diff --git a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf new file mode 100644 index 000000000..769a2ed4a --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf @@ -0,0 +1,36 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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 = 1*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_bitstream_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_pipelined/and2_pipelined.v + +[SYNTHESIS_PARAM] +bench0_top = and2_pipelined +bench0_chan_width = 300 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 18eb5c9de9ff4c853c0275bf9ab43d694389b1ba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 15:56:41 -0600 Subject: [PATCH 004/164] [Test] Deploy new test to CI --- .../regression_test_scripts/fpga_bitstream_reg_test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 e5b0c2f42..f85fc8d59 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -11,7 +11,6 @@ echo -e "FPGA-Bitstream regression tests"; echo -e "Testing bitstream generation for an auto-sized device"; run-task fpga_bitstream/generate_bitstream/device_auto --debug --show_thread_logs - echo -e "Testing bitstream generation for an 48x48 FPGA device"; run-task fpga_bitstream/generate_bitstream/device_48x48 --debug --show_thread_logs @@ -23,3 +22,6 @@ run-task fpga_bitstream/load_external_architecture_bitstream --debug --show_thre echo -e "Testing repacker capability in identifying wire LUTs"; run-task fpga_bitstream/repack_wire_lut --debug --show_thread_logs + +echo -e "Testing overloading default paths for programmable interconnect when generating bitstream"; +run-task fpga_bitstream/overload_mux_default_path--debug --show_thread_logs From 578d81b67a518acefa091e0d759a5bfbeebe319d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 16:15:00 -0600 Subject: [PATCH 005/164] [Test] Patch task configuration file --- .../overload_mux_default_path/config/bitstream_annotation.xml | 2 +- .../fpga_bitstream/overload_mux_default_path/config/task.conf | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml index 2bb8ec56d..519a837ef 100644 --- a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml +++ b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml @@ -1,3 +1,3 @@ - + diff --git a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf index 769a2ed4a..70fe861c2 100644 --- a/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf +++ b/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/task.conf @@ -16,10 +16,11 @@ timeout_each_job = 1*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml openfpga_bitstream_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/fpga_bitstream/overload_mux_default_path/config/bitstream_annotation.xml +openfpga_vpr_device_layout=2x2 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml From 64163edbe671dc1dc1252530ae2c4416d5fa0ff6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 16:15:25 -0600 Subject: [PATCH 006/164] [Script] Add a custom script to run OpenFPGA in a fixed device size using global tile clock and bitstream setting --- ..._bitstream_setting_example_script.openfpga | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga new file mode 100644 index 000000000..dabb0c6c6 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga @@ -0,0 +1,77 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --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 --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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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 + +# Write the fabric hierarchy of module graph to a file +# This is used by hierarchical PnR flows +write_fabric_hierarchy --file ./fabric_hierarchy.txt + +# 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 --write_file fabric_independent_bitstream.xml + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.xml --format xml + +# Write the Verilog netlist for FPGA fabric +# - Enable the use of explicit port mapping in Verilog netlist +write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose + +# Write the Verilog testbench for FPGA fabric +# - We suggest the use of same output directory as fabric Verilog netlists +# - Must specify the reference benchmark file if you want to output any testbenches +# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA +# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase +# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts +write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator #--explicit_port_mapping + +# Write the SDC files for PnR backend +# - Turn on every options here +write_pnr_sdc --file ./SDC + +# Write SDC to disable timing for configure ports +write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc + +# Write the SDC to run timing analysis for a mapped FPGA fabric +write_analysis_sdc --file ./SDC_analysis + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory From 9b3dcc65bd6f42914bd4f5733cbe0abc059e1b45 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 16:37:21 -0600 Subject: [PATCH 007/164] [Doc] Add new bitstream setting syntex 'interconnect' to documentation --- .../manual/file_formats/bitstream_setting.rst | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/source/manual/file_formats/bitstream_setting.rst b/docs/source/manual/file_formats/bitstream_setting.rst index d5d4649b4..764bf0c87 100644 --- a/docs/source/manual/file_formats/bitstream_setting.rst +++ b/docs/source/manual/file_formats/bitstream_setting.rst @@ -6,19 +6,35 @@ Bitstream Setting (.xml) An example of bitstream settings is shown as follows. This can define a hard-coded bitstream for a reconfigurable resource in FPGA fabrics. +.. warning:: Bitstream setting is a feature for power-users. It may cause wrong bitstream to be generated. For example, the hard-coded bitstream is not compatible with LUTs whose nets may be swapped during routing stage (cause a change on the truth table as well as bitstream). It is users's responsibility to ensure correct bitstream. + .. code-block:: xml + -.. option:: pb_type="" +pb_type-related Settings +^^^^^^^^^^^^^^^^^^^^^^^^ - The ``pb_type`` name to be constrained, which should be the full path of a ``pb_type`` consistent with VPR's architecture description. For example, ``pb_type="clb.fle[arithmetic].soft_adder.adder_lut4"`` +The following syntax are applicable to the XML definition tagged by ``pb_type`` in bitstream setting files. + +.. option:: name="" + + The ``pb_type`` name to be constrained, which should be the full path of a ``pb_type`` consistent with VPR's architecture description. For example, + + .. code-block:: xml + + pb_type="clb.fle[arithmetic].soft_adder.adder_lut4" .. option:: source="" - The source of the ``pb_type`` bitstream, which could be from a ``.eblif`` file. For example, ``source="eblif"``. + The source of the ``pb_type`` bitstream, which could be from a ``.eblif`` file. For example, + + .. code-block:: xml + + source="eblif" .. option:: content="" @@ -33,5 +49,25 @@ This can define a hard-coded bitstream for a reconfigurable resource in FPGA fab Specify the offset to be applied when overloading the bitstream to a target. For example, a LUT may have a 16-bit bitstream. When ``offset=1``, bitstream overloading will skip the first bit and start from the second bit of the 16-bit bitstream. -.. warning:: Bitstream setting is a feature for power-users. It may cause wrong bitstream to be generated. For example, the hard-coded bitstream is not compatible with LUTs whose nets may be swapped during routing stage (cause a change on the truth table as well as bitstream). It is users's responsibility to ensure correct bitstream. +Interconnection-related Settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following syntax are applicable to the XML definition tagged by ``interconnect`` in bitstream setting files. + +.. option:: name="" + + The ``interconnect`` name to be constrained, which should be the full path of a ``pb_type`` consistent with VPR's architecture description. For example, + + .. code-block:: xml + + pb_type="clb.fle[arithmetic].mux1" + +.. option:: default_path="" + + The default path denotes an input name that is consistent with VPR's architecture description. For example, in VPR architecture, there is a mux defined as + + .. code-block:: xml + + + + The default path can be either ``iopad.inpad`` or ``ff.Q`` which corresponds to the first input and the second input respectively. From 2fa370d7d5ddb09b51e2eb2d8f07d316ae087135 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Apr 2021 17:15:14 -0600 Subject: [PATCH 008/164] [Test] Patch regression tests for fpga bitstream --- .../regression_test_scripts/fpga_bitstream_reg_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f85fc8d59..db692c002 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -24,4 +24,4 @@ echo -e "Testing repacker capability in identifying wire LUTs"; run-task fpga_bitstream/repack_wire_lut --debug --show_thread_logs echo -e "Testing overloading default paths for programmable interconnect when generating bitstream"; -run-task fpga_bitstream/overload_mux_default_path--debug --show_thread_logs +run-task fpga_bitstream/overload_mux_default_path --debug --show_thread_logs From 96ce6b545f8c3404af3f19d5603af8fe0fd6c674 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 13:53:08 -0600 Subject: [PATCH 009/164] [Tool] Patch repack to consider design constraints for pins that are not equivalent --- .../src/repack_design_constraints.cpp | 22 +++++ .../src/repack_design_constraints.h | 18 ++++ openfpga/src/repack/repack.cpp | 90 +++++++++++++------ 3 files changed, 105 insertions(+), 25 deletions(-) diff --git a/libopenfpga/librepackdc/src/repack_design_constraints.cpp b/libopenfpga/librepackdc/src/repack_design_constraints.cpp index d19c1ba71..c32ad6bb3 100644 --- a/libopenfpga/librepackdc/src/repack_design_constraints.cpp +++ b/libopenfpga/librepackdc/src/repack_design_constraints.cpp @@ -50,6 +50,20 @@ std::string RepackDesignConstraints::net(const RepackDesignConstraintId& repack_ return repack_design_constraint_nets_[repack_design_constraint_id]; } +std::string RepackDesignConstraints::find_constrained_pin_net(const std::string& pb_type, + const openfpga::BasicPort& pin) const { + std::string constrained_net_name; + for (const RepackDesignConstraintId& design_constraint : design_constraints()) { + /* If found a constraint, record the net name */ + if ( (pb_type == repack_design_constraint_pb_types_[design_constraint]) + && (pin == repack_design_constraint_pins_[design_constraint])) { + constrained_net_name = repack_design_constraint_nets_[design_constraint]; + break; + } + } + return constrained_net_name; +} + bool RepackDesignConstraints::empty() const { return 0 == repack_design_constraint_ids_.size(); } @@ -106,3 +120,11 @@ void RepackDesignConstraints::set_net(const RepackDesignConstraintId& repack_des bool RepackDesignConstraints::valid_design_constraint_id(const RepackDesignConstraintId& design_constraint_id) const { return ( size_t(design_constraint_id) < repack_design_constraint_ids_.size() ) && ( design_constraint_id == repack_design_constraint_ids_[design_constraint_id] ); } + +bool RepackDesignConstraints::unconstrained_net(const std::string& net) const { + return net.empty(); +} + +bool RepackDesignConstraints::unmapped_net(const std::string& net) const { + return std::string(REPACK_DESIGN_CONSTRAINT_OPEN_NET) == net; +} diff --git a/libopenfpga/librepackdc/src/repack_design_constraints.h b/libopenfpga/librepackdc/src/repack_design_constraints.h index c32a0aca8..8c1ca6415 100644 --- a/libopenfpga/librepackdc/src/repack_design_constraints.h +++ b/libopenfpga/librepackdc/src/repack_design_constraints.h @@ -61,6 +61,10 @@ class RepackDesignConstraints { /* Get the net to be constrained */ std::string net(const RepackDesignConstraintId& repack_design_constraint_id) const; + /* Find a constrained net */ + std::string find_constrained_pin_net(const std::string& pb_type, + const openfpga::BasicPort& pin) const; + /* Check if there are any design constraints */ bool empty() const; @@ -86,6 +90,20 @@ class RepackDesignConstraints { public: /* Public invalidators/validators */ bool valid_design_constraint_id(const RepackDesignConstraintId& repack_design_constraint_id) const; + /* Show if the net has no constraints (free to map to any pin) + * This function is used to identify the net name returned by APIs: + * - find_constrained_pin_net() + * - net() + */ + bool unconstrained_net(const std::string& net) const; + + /* Show if the net is defined specifically not to map to any pin + * This function is used to identify the net name returned by APIs: + * - find_constrained_pin_net() + * - net() + */ + bool unmapped_net(const std::string& net) const; + private: /* Internal data */ /* Unique ids for each design constraint */ vtr::vector repack_design_constraint_ids_; diff --git a/openfpga/src/repack/repack.cpp b/openfpga/src/repack/repack.cpp index 3611909bb..5e25179a5 100644 --- a/openfpga/src/repack/repack.cpp +++ b/openfpga/src/repack/repack.cpp @@ -230,6 +230,38 @@ std::vector find_routed_pb_graph_pins_atom_net(const t_pb* pb, return sink_pb_pins; } +/*************************************************************************************** + * This function will find the actual routing traces of the demanded net + * There is a specific search space applied when searching the routing traces: + * - ONLY applicable to the pb_pin of top-level pb_graph_node + * - candidate can be limited to a set of pb pins + ***************************************************************************************/ +static +std::vector find_pb_route_by_atom_net(const t_pb* pb, + const t_pb_graph_pin* source_pb_pin, + const AtomNetId& atom_net_id) { + VTR_ASSERT(true == source_pb_pin->parent_node->is_root()); + + std::vector pb_route_indices; + + for (int pin = 0; pin < pb->pb_graph_node->total_pb_pins; ++pin) { + /* Bypass unused pins */ + if ((0 == pb->pb_route.count(pin)) || (AtomNetId::INVALID() == pb->pb_route.at(pin).atom_net_id)) { + continue; + } + /* Get the driver pb pin id, it must be valid */ + if (atom_net_id != pb->pb_route.at(pin).atom_net_id) { + continue; + } + + if (source_pb_pin->port == pb->pb_route.at(pin).pb_graph_pin->port) { + pb_route_indices.push_back(pin); + } + } + + return pb_route_indices; +} + /*************************************************************************************** * This function will find the actual source_pb_pin that is mapped by packed in the pb route * As the inputs of clustered block may be renamed during routing, @@ -422,18 +454,7 @@ void add_lb_router_nets(LbRouter& lb_router, AtomNetId atom_net_id = pb_pin_mapped_nets[source_pb_pin]; /* Check if the net information is constrained or not */ - std::string constrained_net_name; - for (const RepackDesignConstraintId& design_constraint : design_constraints.design_constraints()) { - /* All the pin must have only 1 bit */ - VTR_ASSERT_SAFE(1 == design_constraints.pin(design_constraint).get_width()); - /* If found a constraint, record the net name */ - if ( (std::string(lb_type->pb_type->name) == design_constraints.pb_type(design_constraint)) - && (std::string(source_pb_pin->port->name) == design_constraints.pin(design_constraint).get_name()) - && (size_t(source_pb_pin->pin_number) == design_constraints.pin(design_constraint).get_lsb())) { - constrained_net_name = design_constraints.net(design_constraint); - break; - } - } + std::string constrained_net_name = design_constraints.find_constrained_pin_net(std::string(lb_type->pb_type->name), BasicPort(std::string(source_pb_pin->port->name), source_pb_pin->pin_number, source_pb_pin->pin_number)); /* Find the constrained net mapped to this pin in clustering results */ AtomNetId constrained_atom_net_id = AtomNetId::INVALID(); @@ -443,16 +464,21 @@ void add_lb_router_nets(LbRouter& lb_router, * - if this is valid net name, find the net id from atom_netlist * and overwrite the atom net id to mapped */ - if (!constrained_net_name.empty()) { - if (std::string(REPACK_DESIGN_CONSTRAINT_OPEN_NET) != constrained_net_name) { - constrained_atom_net_id = atom_ctx.nlist.find_net(constrained_net_name); - if (false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)) { - VTR_LOG_WARN("Invalid net '%s' to be constrained! Will drop the constraint in repacking\n", - constrained_net_name.c_str()); - } + if ( (!design_constraints.unconstrained_net(constrained_net_name)) + && (!design_constraints.unmapped_net(constrained_net_name))) { + constrained_atom_net_id = atom_ctx.nlist.find_net(constrained_net_name); + if (false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)) { + VTR_LOG_WARN("Invalid net '%s' to be constrained! Will drop the constraint in repacking\n", + constrained_net_name.c_str()); + } else { + VTR_ASSERT_SAFE(false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)); + VTR_LOGV(verbose, + "Accept net '%s' to be constrained on pin '%s[%d]' during repacking\n", + constrained_net_name.c_str(), + source_pb_pin->port->name, + source_pb_pin->pin_number); } - } else { - VTR_ASSERT_SAFE(constrained_net_name.empty()); + } else if (design_constraints.unconstrained_net(constrained_net_name)) { constrained_atom_net_id = atom_net_id; } @@ -486,18 +512,35 @@ void add_lb_router_nets(LbRouter& lb_router, LbRRNodeId source_lb_rr_node = lb_rr_graph.find_node(LB_INTERMEDIATE, source_pb_pin); VTR_ASSERT(true == lb_rr_graph.valid_node_id(source_lb_rr_node)); + /* Output verbose messages for debugging only */ + VTR_LOGV(verbose, + "Pb route for Net %s:\n", + atom_ctx.nlist.net_name(atom_net_id_to_route).c_str()); + /* As the pin remapping is allowed during routing, we should * - Find the routing traces from packing results which is mapped to the net * from the same port (as remapping is allowed for pins in the same port only) * - Find the source pb_graph_pin that drives the routing traces during packing * - Then we can find the sink nodes + * + * When there is a pin constraint applied. The routing trace + * - Find the routing traces from packing results which is mapped to the net + * with the same port constraints */ - std::vector pb_route_indices = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id_to_route); + std::vector pb_route_indices; + if (design_constraints.unconstrained_net(constrained_net_name)) { + pb_route_indices = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id_to_route); + } else { + VTR_ASSERT_SAFE(!design_constraints.unconstrained_net(constrained_net_name)); + pb_route_indices = find_pb_route_by_atom_net(pb, source_pb_pin, atom_net_id_to_route); + } /* It could happen that the constrained net is NOT used in this clb, we just skip it for routing * For example, a clkB net is never mapped to any ports in the pb that is clocked by clkA net * */ int pb_route_index; if (0 == pb_route_indices.size()) { + VTR_LOGV(verbose, + "Bypass routing due to no routing traces found\n"); continue; } else { VTR_ASSERT(1 == pb_route_indices.size()); @@ -512,9 +555,6 @@ void add_lb_router_nets(LbRouter& lb_router, VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); /* Output verbose messages for debugging only */ - VTR_LOGV(verbose, - "Pb route for Net %s:\n", - atom_ctx.nlist.net_name(atom_net_id_to_route).c_str()); VTR_LOGV(verbose, "Source node:\n\t%s -> %s\n", source_pb_pin->to_string().c_str(), From b203ef7bc27e3a92d8c5f4dbda87c39639fcaf0f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 14:03:51 -0600 Subject: [PATCH 010/164] [Benchmark] Add new benchmark 2-clock version of and2_latch as an essential test for multi-clock FPGAs --- .../and2_latch_2clock/and2_latch_2clock.v | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v b/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v new file mode 100644 index 000000000..a34cfdc91 --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v @@ -0,0 +1,49 @@ +///////////////////////////////////////// +// Functionality: Two 2-input AND with clocked +// and combinational outputs +// Each of which are controlled by different clocks +// Author: Xifan Tang +//////////////////////////////////////// + +`timescale 1ns / 1ps + +module and2_latch_2clock( + a0, + b0, + clk0, + a1, + b1, + clk1, + c0, + d0, + c1, + d1); + +input wire clk0; + +input wire a0; +input wire b0; +output wire c0; +output reg d0; + +input wire clk1; + +input wire a1; +input wire b1; +output wire c1; +output reg d1; + + +assign c0 = a0 & b0; + +always @(posedge clk0) begin + d0 <= c0; +end + +assign c1 = a1 & b1; + +always @(posedge clk1) begin + d1 <= c1; +end + +endmodule From 8046b16c151cba318a6da4ec867e8003e0dd9194 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 14:04:34 -0600 Subject: [PATCH 011/164] [Test] Remove restrictions in the multi-clock test case and deploy new microbenchmarks for testing --- .../global_tile_4clock/config/task.conf | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf index 275a32b57..e7cbcb5ab 100644 --- a/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf @@ -9,17 +9,11 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 -# Due to the limitation in ACE2 which cannot output .blif files -# with correct multi-clock assignments to .latch lines -# We have to use the vpr_blif flow where the .blif is modified -# based on yosys outputs with correct clock assignment! -# TODO: This limitation should be removed and we should use yosys_vpr flow!!! -fpga_flow=vpr_blif -#fpga_flow=yosys_vpr +fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga @@ -32,14 +26,12 @@ openfpga_pin_constraints_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_te arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTile4Clk_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.blif -#bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v [SYNTHESIS_PARAM] bench0_top = counter4bit_2clock -bench0_act=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.act -bench0_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock_post_yosys.v -bench0_chan_width = 300 +bench1_top = and2_latch_2clock [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 2e1cc5499d53719d6b899835175c2265ba7879e3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 14:14:54 -0600 Subject: [PATCH 012/164] [Doc] Add disclaimer for limitations when using repack pin constraints --- .../manual/file_formats/repack_design_constraints.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/file_formats/repack_design_constraints.rst b/docs/source/manual/file_formats/repack_design_constraints.rst index 5771123c2..56f40c310 100644 --- a/docs/source/manual/file_formats/repack_design_constraints.rst +++ b/docs/source/manual/file_formats/repack_design_constraints.rst @@ -3,6 +3,11 @@ Repack Design Constraints (.xml) -------------------------------- +.. warning:: For the best practice, current repack design constraints only support the net remapping between pins in the same port. Pin constraints are **NOT** allowed for two separated ports. + + - A legal pin constraint example: when there are two clock nets, ``clk0`` and ``clk1``, pin constraints are forced on two pins in a clock port ``clk[0:2]`` (e.g., ``clk[0] = clk0`` and ``clk[1] == clk1``). + - An **illegal** pin constraint example: when there are two clock nets, ``clk0`` and ``clk1``, pin constraints are forced on two clock ports ``clkA[0]`` and ``clkB[0]`` (e.g., ``clkA[0] = clk0`` and ``clkB[0] == clk1``). + An example of design constraints is shown as follows. .. code-block:: xml @@ -25,5 +30,5 @@ An example of design constraints is shown as follows. .. option:: net="" The net name of the pin to be mapped, which should be consistent with net definition in your ``.blif`` file. The reserved word ``OPEN`` means that no net should be mapped to a given pin. Please ensure that it is not conflicted with any net names in your ``.blif`` file. - + .. warning:: Design constraints is a feature for power-users. It may cause repack to fail. It is users's responsibility to ensure proper design constraints From 9d9840d9b7862a5aff22cbbd8e20a6e80ccf51b7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 19:49:48 -0600 Subject: [PATCH 013/164] [Arch] Add architecture using multi-mode DFFs --- ...ain_dpram8K_dsp36_fracff_40nm_openfpga.xml | 308 +++++ ..._adder_chain_dpram8K_dsp36_fracff_40nm.xml | 1007 +++++++++++++++++ 2 files changed, 1315 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml create mode 100644 openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml new file mode 100644 index 000000000..d72b9176c --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 + + + 10e-12 5e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml new file mode 100644 index 000000000..002d11154 --- /dev/null +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml @@ -0,0 +1,1007 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + clb.clk clb.reset + clb.cin + clb.O[9:0] clb.I[19:0] + clb.cout clb.O[19:10] clb.I[39:20] + + + + + + + + + + + + + + + + + + + memory.clk + + memory.waddr[4:0] memory.raddr[4:0] memory.data_in[3:0] memory.wen memory.data_out[3:0] + memory.waddr[9:5] memory.raddr[9:5] memory.data_in[7:4] memory.ren memory.data_out[7:4] + + + + + + + + + + + + + + mult_36.b[0:9] mult_36.b[10:35] mult_36.out[36:71] + + mult_36.a[0:9] mult_36.a[10:35] mult_36.out[0:35] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3d615e1516ca52d7d79051fbbbb3f5d146254d5d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 19:50:07 -0600 Subject: [PATCH 014/164] [Script] Add yosys script supporting customize DFF/BRAM/DSP mapping --- .../ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys diff --git a/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys b/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys new file mode 100644 index 000000000..7244532e5 --- /dev/null +++ b/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys @@ -0,0 +1,105 @@ +# Yosys synthesis script for ${TOP_MODULE} + +######################### +# Parse input files +######################### +# Read verilog files +${READ_VERILOG_FILE} +# Read technology library +read_verilog -lib -specify ${YOSYS_CELL_SIM_VERILOG} + +######################### +# Prepare for synthesis +######################### +# Identify top module from hierarchy +hierarchy -check -top ${TOP_MODULE} +# - Convert process blocks to AST +proc +# Flatten all the gates/primitives +flatten +# Identify tri-state buffers from 'z' signal in AST +# with follow-up optimizations to clean up AST +tribuf -logic +opt_expr +opt_clean +# demote inout ports to input or output port +# with follow-up optimizations to clean up AST +deminout +opt + +opt_expr +opt_clean +check +opt +wreduce -keepdc +peepopt +pmuxtree +opt_clean + +######################## +# Map multipliers +# Inspired from synth_xilinx.cc +######################### +# Avoid merging any registers into DSP, reserve memory port registers first +memory_dff +wreduce t:$mul +techmap -map +/mul2dsp.v -map ${YOSYS_DSP_MAP_VERILOG} ${YOSYS_DSP_MAP_PARAMETERS} +select a:mul2dsp +setattr -unset mul2dsp +opt_expr -fine +wreduce +select -clear +chtype -set $mul t:$__soft_mul# Extract arithmetic functions + +######################### +# Run coarse synthesis +######################### +# Run a tech map with default library +techmap +alumacc +share +opt +fsm +# Run a quick follow-up optimization to sweep out unused nets/signals +opt -fast +# Optimize any memory cells by merging share-able ports and collecting all the ports belonging to memorcy cells +memory -nomap +opt_clean + +######################### +# Map logics to BRAMs +######################### +memory_bram -rules ${YOSYS_BRAM_MAP_RULES} +techmap -map ${YOSYS_BRAM_MAP_VERILOG} +opt -fast -mux_undef -undriven -fine +memory_map +opt -undriven -fine + +######################### +# Map flip-flops +######################### +techmap -map ${YOSYS_DFF_MAP_VERILOG} +opt_expr -mux_undef +simplemap +opt_expr +opt_merge +opt_rmdff +opt_clean +opt + +######################### +# Map LUTs +######################### +abc -lut ${LUT_SIZE} + +######################### +# Check and show statisitics +######################### +hierarchy -check +stat + +######################### +# Output netlists +######################### +opt_clean -purge +write_blif ${OUTPUT_BLIF} From 8cbea6a2686899f226de0f53ccceb1e917571f14 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 19:50:51 -0600 Subject: [PATCH 015/164] [HDL] Add technology library for customizable DFF synthesis --- ...chain_dpram8K_dsp36_fracff_40nm_cell_sim.v | 127 ++++++++++++++++++ ..._chain_dpram8K_dsp36_fracff_40nm_dff_map.v | 18 +++ .../openfpga_yosys_techlib/openfpga_dff_sim.v | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v create mode 100644 openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v new file mode 100644 index 000000000..f075e8781 --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v @@ -0,0 +1,127 @@ +//----------------------------- +// Dual-port RAM 1024x8 bit (8Kbit) +// Core logic +//----------------------------- +module dpram_1024x8_core ( + input wclk, + input wen, + input [0:9] waddr, + input [0:7] data_in, + input rclk, + input ren, + input [0:9] raddr, + output [0:7] data_out ); + + reg [0:7] ram[0:1023]; + reg [0:7] internal; + + assign data_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule + +//----------------------------- +// Dual-port RAM 1024x8 bit (8Kbit) wrapper +// where the read clock and write clock +// are combined to a unified clock +//----------------------------- +module dpram_1024x8 ( + input clk, + input wen, + input ren, + input [0:9] waddr, + input [0:9] raddr, + input [0:7] data_in, + output [0:7] data_out ); + + dpram_1024x8_core memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (data_in), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .data_out (data_out) ); + +endmodule + +//----------------------------- +// 36-bit multiplier +//----------------------------- +module mult_36( + input [0:35] A, + input [0:35] B, + output [0:71] Y +); + +assign Y = A * B; + +endmodule + +//----------------------------- +// Native D-type flip-flop +//----------------------------- +(* abc9_flop, lib_whitebox *) +module dff( + output reg Q, + input D, + (* clkbuf_sink *) + (* invertible_pin = "IS_C_INVERTED" *) + input C +); + parameter [0:0] INIT = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + initial Q = INIT; + case(|IS_C_INVERTED) + 1'b0: + always @(posedge C) + Q <= D; + 1'b1: + always @(negedge C) + Q <= D; + endcase +endmodule + +//----------------------------- +// D-type flip-flop with asynchronous reset +//----------------------------- +(* abc9_flop, lib_whitebox *) +module dffr( + output reg Q, + input D, + input R, + (* clkbuf_sink *) + (* invertible_pin = "IS_C_INVERTED" *) + input C +); + parameter [0:0] INIT = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + initial Q = INIT; + case(|IS_C_INVERTED) + 1'b0: + always @(posedge C or posedge R) + if (R == 1'b1) + Q <= 1'b0; + else + Q <= D; + 1'b1: + always @(negedge C or posedge R) + if (R == 1'b1) + Q <= 1'b0; + else + Q <= D; + endcase +endmodule + diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v new file mode 100644 index 000000000..822706642 --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v @@ -0,0 +1,18 @@ +// Basic DFF +module \$_DFF_P_ (D, C, Q); + input D; + input C; + output Q; + parameter _TECHMAP_WIREINIT_Q_ = 1'bx; + dff _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C)); +endmodule + +// Async reset +module \$_DFF_PP0_ (D, C, R, Q); + input D; + input C; + input R; + output Q; + parameter _TECHMAP_WIREINIT_Q_ = 1'bx; + dffr _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .R(R)); +endmodule diff --git a/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_sim.v b/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_sim.v index 90926cef6..d4798a906 100644 --- a/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_sim.v +++ b/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_sim.v @@ -93,4 +93,4 @@ module latchre ( if (S) Q <= 1'b1; else if (E && G) Q <= D; end -endmodule \ No newline at end of file +endmodule From 3a5c26c6a16bd14a040518222ac3099d50706d9d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 19:51:25 -0600 Subject: [PATCH 016/164] [Test] Update IWLS test by using new architecture and customize DFF techmap --- .../benchmark_sweep/iwls2005/config/task.conf | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/openfpga_flow/tasks/benchmark_sweep/iwls2005/config/task.conf b/openfpga_flow/tasks/benchmark_sweep/iwls2005/config/task.conf index 60eaeec4a..6db54bc7d 100644 --- a/openfpga_flow/tasks/benchmark_sweep/iwls2005/config/task.conf +++ b/openfpga_flow/tasks/benchmark_sweep/iwls2005/config/task.conf @@ -17,10 +17,11 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/iwls_benchmark_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_40nm_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml # Yosys script parameters -yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_cell_sim.v +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v +yosys_dff_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram.txt yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram_map.v yosys_dsp_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_dsp_map.v @@ -30,48 +31,54 @@ yosys_dsp_map_parameters=-D DSP_A_MAXWIDTH=36 -D DSP_B_MAXWIDTH=36 -D DSP_A_MINW vpr_route_chan_width=300 [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml [BENCHMARKS] # RTL netlists from IWLS 2005 benchmark release -# Comment out due to DFF synthesis problems +# Comment out it requires falling edge latches which are not supported yet #bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/ac97_ctrl/rtl/*.v +# Comment out it requires synchronous reset which has not been supported yet #bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/ethernet/rtl/*.v +# Comment out it requires set which has not been supported yet #bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/mem_ctrl/rtl/*.v +# Comment out it requires a active-low reset which has not been supported yet #bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/simple_spi/rtl/*.v # Comment out due to VHDL is not supported by Yosys without Verific #bench4=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/steppermotordrive/rtl/*.vhd bench5=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/tv80/rtl/*.v -# Comment out due to DFF synthesis problems +# Comment out it requires a active-low reset which has not been supported yet #bench6=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/vga_lcd/rtl/*.v # AES core has two top modules that can be tested: encryption and decryption # Synthesis is too long; skip it #bench7=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/aes_core/rtl/*.v #bench8=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/aes_core/rtl/*.v #bench9=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/fpu/rtl/*.v -# Comment out due to DFF synthesis problems +# Comment out it requires set which has not been supported yet #bench10=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/pci/rtl/*.v +# Comment out it requires set which has not been supported yet #bench11=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/spi/rtl/*.v +# Comment out it requires a active-low reset which has not been supported yet #bench12=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/systemcaes/rtl/*.v bench13=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/usb_funct/rtl/*.v -# Comment out due to DFF synthesis problems -#bench14=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/wb_conmax/rtl/*.v +bench14=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/wb_conmax/rtl/*.v ## DES has two versions: area-optimized and performance optimized -bench15=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/des/area_opt/rtl/*.v -bench16=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/des/perf_opt/rtl/*.v -# Comment out due to DFF synthesis problems +# The DES has same top-level module name as systemcdes +# Currently openfpga flow has a bug which does not allow same top-level module name +#bench15=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/des/area_opt/rtl/*.v +#bench16=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/des/perf_opt/rtl/*.v +# Comment out it requires a active-low reset which has not been supported yet #bench17=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/i2c/rtl/*.v -# Comment out due to DFF synthesis problems +# Comment out it requires a active-low reset which has not been supported yet #bench18=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/sasc/rtl/*.v bench19=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/ss_pcm/rtl/*.v -# Comment out due to DFF synthesis problems +# Comment out it requires a active-low reset which has not been supported yet #bench20=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/systemcdes/rtl/*.v bench21=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/usb_phy/rtl/*.v -# Comment out due to DFF synthesis problems +# Comment out it requires a active-low reset which has not been supported yet #bench22=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/iwls2005/wb_dma/rtl/*.v [SYNTHESIS_PARAM] -bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_flow.ys +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_dsp_dff_flow.ys bench0_top = ac97_top bench1_top = eth_top bench2_top = mc_top From 62497549b6c0ebd35bda56decfccd847a2ac076a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 20:04:40 -0600 Subject: [PATCH 017/164] [HDL] Add multi-mode DFF module --- .../openfpga_cell_library/verilog/dff.v | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/openfpga_flow/openfpga_cell_library/verilog/dff.v b/openfpga_flow/openfpga_cell_library/verilog/dff.v index a0548c54c..a82b88fae 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/dff.v +++ b/openfpga_flow/openfpga_cell_library/verilog/dff.v @@ -289,6 +289,34 @@ assign Q = q_reg; endmodule //End Of Module +//----------------------------------------------------- +// Function : A multi-functional D-type flip-flop with +// - asynchronous reset +// which can be switched between active-low and active hight +// - asynchronous set which can be switched +// which can be switched between active-low and active hight +//----------------------------------------------------- +module MULTIMODE_DFFSRQ ( + input SET, // Set input + input RST, // Reset input + input CK, // Clock Input + input D, // Data Input + output Q, // Q output + input [0:1] mode // mode-selection bits: bit0 for reset polarity; bit1 for set polarity +); + +wire post_set = mode ? ~SET : SET; +wire post_reset = mode ? ~RST : RST; + +DFFSRQ FF_CORE (.SET(post_set), + .RST(post_rst), + .CK(CK), + .D(D), + .Q(Q) + ); + +endmodule //End Of Module + //----------------------------------------------------- // Function : D-type flip-flop with // - asynchronous active high reset From adfea88be21b79613540feb31504c6f4337ac825 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 20:06:03 -0600 Subject: [PATCH 018/164] [HDL] Rename multi-mode DFF module --- openfpga_flow/openfpga_cell_library/verilog/dff.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_cell_library/verilog/dff.v b/openfpga_flow/openfpga_cell_library/verilog/dff.v index a82b88fae..a1d3e2407 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/dff.v +++ b/openfpga_flow/openfpga_cell_library/verilog/dff.v @@ -296,7 +296,7 @@ endmodule //End Of Module // - asynchronous set which can be switched // which can be switched between active-low and active hight //----------------------------------------------------- -module MULTIMODE_DFFSRQ ( +module MULTI_MODE_DFFSRQ ( input SET, // Set input input RST, // Reset input input CK, // Clock Input From ce6018e1235fcb29aebc0276b0d5f15e80d80778 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Apr 2021 22:48:31 -0600 Subject: [PATCH 019/164] [Arch] Enriched DFF model to support active-low/high FFs --- ...ain_dpram8K_dsp36_fracff_40nm_openfpga.xml | 49 +++- ..._adder_chain_dpram8K_dsp36_fracff_40nm.xml | 226 +++++++++++++++++- 2 files changed, 258 insertions(+), 17 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml index d72b9176c..d1cf24921 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml @@ -139,15 +139,16 @@ - + - + + @@ -240,6 +241,9 @@ + + + @@ -257,7 +261,7 @@ - + @@ -266,11 +270,18 @@ - + - - + + + + + + + + + @@ -278,22 +289,36 @@ - + - - + + + + + + + + + - + - - + + + + + + + + + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml index 002d11154..db1b5bef4 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml @@ -147,7 +147,7 @@ - + @@ -157,7 +157,17 @@ - + + + + + + + + + + + @@ -168,6 +178,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -190,6 +230,7 @@ + @@ -198,12 +239,13 @@ + - clb.clk clb.reset + clb.clk clb.reset clb.set clb.cin clb.O[9:0] clb.I[19:0] clb.cout clb.O[19:10] clb.I[39:20] @@ -412,6 +454,7 @@ + @@ -423,6 +466,7 @@ + @@ -432,6 +476,7 @@ + @@ -456,13 +501,15 @@ - + + + @@ -491,6 +538,7 @@ + @@ -510,6 +558,7 @@ + @@ -517,6 +566,7 @@ + @@ -545,6 +595,7 @@ + @@ -592,6 +643,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -600,6 +702,7 @@ + @@ -612,6 +715,7 @@ + @@ -620,6 +724,7 @@ + @@ -660,6 +765,7 @@ + @@ -707,9 +813,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -746,13 +904,15 @@ - + + + @@ -782,6 +942,7 @@ + @@ -829,6 +990,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -837,6 +1049,7 @@ + @@ -848,6 +1061,7 @@ + @@ -871,6 +1085,8 @@ + + - From 272d1fffb70fb6a02c4eb4d27f28b9b6b2b4a338 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 13:30:46 -0600 Subject: [PATCH 036/164] [HDL] Add tech library for architecture using multi-mode 16-bit DSP blocks --- ..._nonLR_caravel_io_skywater130nm_cell_sim.v | 25 +++++++++++ ...6_nonLR_caravel_io_skywater130nm_dsp_map.v | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_cell_sim.v create mode 100644 openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v diff --git a/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_cell_sim.v b/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_cell_sim.v new file mode 100644 index 000000000..d455c79d0 --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_cell_sim.v @@ -0,0 +1,25 @@ +//----------------------------- +// 8-bit multiplier +//----------------------------- +module mult_8( + input [0:7] A, + input [0:7] B, + output [0:15] Y +); + +assign Y = A * B; + +endmodule + +//----------------------------- +// 16-bit multiplier +//----------------------------- +module mult_16( + input [0:15] A, + input [0:15] B, + output [0:31] Y +); + +assign Y = A * B; + +endmodule diff --git a/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v b/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v new file mode 100644 index 000000000..035e6f7eb --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v @@ -0,0 +1,41 @@ +//----------------------------- +// 8-bit multiplier +//----------------------------- +module mult_8x8 ( + input [0:7] A, + input [0:7] B, + output [0:15] Y +); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + mult_8 #() _TECHMAP_REPLACE_ ( + .A (A), + .B (B), + .Y (Y) ); + +endmodule + +//----------------------------- +// 16-bit multiplier +//----------------------------- +module mult_16x16 ( + input [0:15] A, + input [0:15] B, + output [0:31] Y +); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + mult_16 #() _TECHMAP_REPLACE_ ( + .A (A), + .B (B), + .Y (Y) ); + +endmodule From 4f454abfde2ad6693be9dc422dee48a40fe9ed6a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 14:01:42 -0600 Subject: [PATCH 037/164] [Arch] Add a new architecture using fracturable 16-bit DSP blocks --- ...avel_io_skywater130nm_fdhd_cc_openfpga.xml | 295 ++++++ ...c_dsp16_nonLR_caravel_io_skywater130nm.xml | 962 ++++++++++++++++++ 2 files changed, 1257 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml create mode 100644 openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml new file mode 100644 index 000000000..7ce8c5c28 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 + + + 10e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml new file mode 100644 index 000000000..98bae4fa2 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml @@ -0,0 +1,962 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io_top.outpad io_top.inpad + + + + + + + + + + + + io_right.outpad io_right.inpad + + + + + + + + + + + + io_bottom.outpad io_bottom.inpad + + + + + + + + + + + + io_left.outpad io_left.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clb.clk clb.reset + clb.reg_in clb.sc_in clb.cin clb.O[7:0] clb.I0 clb.I0i clb.I1 clb.I1i clb.I2 clb.I2i clb.I3 clb.I3i + clb.O[15:8] clb.I4 clb.I4i clb.I5 clb.I5i clb.I6 clb.I6i clb.I7 clb.I7i + clb.reg_out clb.sc_out clb.cout + + + + + + + + + + + + + + mult_16.a[0:5] mult_16.b[0:5] mult_16.out[0:10] + mult_16.a[6:7] mult_16.b[6:7] mult_16.out[11:15] + mult_16.a[8:13] mult_16.b[8:13] mult_16.out[16:26] + mult_16.a[14:15] mult_16.b[14:15] mult_16.out[27:31] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 148da80869b020aba0c73ccd6dbbc3ab982be15c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 14:53:29 -0600 Subject: [PATCH 038/164] [Tool] Add new syntax about physical_pb_port_rotate_offset to support fracturable heterogeneous block mapping between operating modes and physical modes --- .../src/pb_type_annotation.cpp | 40 +++++++++++--- .../libarchopenfpga/src/pb_type_annotation.h | 24 ++++++--- .../src/read_xml_pb_type_annotation.cpp | 25 +++++++++ .../src/write_xml_pb_type_annotation.cpp | 9 +++- openfpga/src/annotation/annotate_pb_graph.cpp | 26 +++++++++- openfpga/src/annotation/annotate_pb_types.cpp | 3 +- .../src/annotation/vpr_device_annotation.cpp | 52 +++++++++++++++++++ .../src/annotation/vpr_device_annotation.h | 15 +++++- openfpga/src/repack/repack.cpp | 6 +-- 9 files changed, 178 insertions(+), 22 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp index 9a369e742..1a1a7ea31 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp @@ -86,11 +86,11 @@ std::vector PbTypeAnnotation::port_names() const { return keys; } -std::map> PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { - std::map>>::const_iterator it = operating_pb_type_ports_.find(port_name); +std::map> PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { + std::map>>::const_iterator it = operating_pb_type_ports_.find(port_name); if (it == operating_pb_type_ports_.end()) { /* Return an empty port */ - return std::map>(); + return std::map>(); } return operating_pb_type_ports_.at(port_name); } @@ -169,25 +169,25 @@ void PbTypeAnnotation::set_physical_pb_type_index_offset(const int& value) { void PbTypeAnnotation::add_pb_type_port_pair(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port) { /* Give a warning if the operating_pb_port_name already exist */ - std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); /* If not exist, initialize and set a default value */ if (it == operating_pb_type_ports_.end()) { - operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0}; + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0, 0}; /* We can return early */ return; } /* If the physical port is not in the list, we create one and set a default value */ if (0 == operating_pb_type_ports_[operating_pb_port_name].count(physical_pb_port)) { - operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0}; + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0, 0}; } } void PbTypeAnnotation::set_physical_pin_initial_offset(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port, const int& physical_pin_initial_offset) { - std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); if (it == operating_pb_type_ports_.end()) { VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", @@ -210,7 +210,7 @@ void PbTypeAnnotation::set_physical_pin_initial_offset(const std::string& operat void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port, const int& physical_pin_rotate_offset) { - std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); if (it == operating_pb_type_ports_.end()) { VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", @@ -230,6 +230,30 @@ void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operati operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][1] = physical_pin_rotate_offset; } +void PbTypeAnnotation::set_physical_port_rotate_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_port_rotate_offset) { + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + + if (it == operating_pb_type_ports_.end()) { + VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", + operating_pb_port_name.c_str()); + exit(1); + } + + if (operating_pb_type_ports_[operating_pb_port_name].end() == operating_pb_type_ports_[operating_pb_port_name].find(physical_pb_port)) { + VTR_LOG_ERROR("The physical pb_type port '%s[%lu:%lu]' definition for operating pb_type port '%s' is not valid!\n", + physical_pb_port.get_name().c_str(), + physical_pb_port.get_lsb(), + physical_pb_port.get_msb(), + operating_pb_port_name.c_str()); + exit(1); + } + + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][2] = physical_port_rotate_offset; +} + + void PbTypeAnnotation::add_interconnect_circuit_model_pair(const std::string& interc_name, const std::string& circuit_model_name) { std::map::const_iterator it = interconnect_circuit_model_names_.find(interc_name); diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h index 96383679d..2a0b8804e 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h @@ -49,7 +49,7 @@ class PbTypeAnnotation { float physical_pb_type_index_factor() const; int physical_pb_type_index_offset() const; std::vector port_names() const; - std::map> physical_pb_type_port(const std::string& port_name) const; + std::map> physical_pb_type_port(const std::string& port_name) const; std::vector interconnect_names() const; std::string interconnect_circuit_model_name(const std::string& interc_name) const; public: /* Public mutators */ @@ -73,6 +73,9 @@ class PbTypeAnnotation { void set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port, const int& physical_pin_rotate_offset); + void set_physical_port_rotate_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_port_rotate_offset); void add_interconnect_circuit_model_pair(const std::string& interc_name, const std::string& circuit_model_name); private: /* Internal data */ @@ -138,10 +141,10 @@ class PbTypeAnnotation { int physical_pb_type_index_offset_; /* Link from the pins under an operating pb_type to pairs of - * its physical pb_type and its pin initial & rotating offset - * - * Note that initial offset is the first element of the std::array - * Note that rotating offset is the second element of the std::array + * its physical pb_type and + * - its pin initial offset: the first element of the std::array + * - pin-level rotating offset: the second element of the std::array + * - port-level rotating offset: the third element of the std::array * * The offsets aim to align the pin indices for port of pb_type * between operating and physical modes, especially when an operating @@ -158,14 +161,21 @@ class PbTypeAnnotation { * physical pb_type bram[0].dout_a[0] with a full path memory[physical].bram[0] * physical pb_type bram[0].dout_a[1] with a full path memory[physical].bram[0] * - * For example, a rotating offset of 9 is used to map + * For example, a pin-level rotating offset of 9 is used to map + * operating pb_type mult_9x9[0].a[0] with a full path mult[frac].mult_9x9[0] + * operating pb_type mult_9x9[0].a[1] with a full path mult[frac].mult_9x9[1] + * to + * physical pb_type mult_36x36.a[0] with a full path mult[physical].mult_36x36[0] + * physical pb_type mult_36x36.a[9] with a full path mult[physical].mult_36x36[0] + * + * For example, a port-level rotating offset of 9 is used to map * operating pb_type mult_9x9[0].a[0:8] with a full path mult[frac].mult_9x9[0] * operating pb_type mult_9x9[1].a[0:8] with a full path mult[frac].mult_9x9[1] * to * physical pb_type mult_36x36.a[0:8] with a full path mult[physical].mult_36x36[0] * physical pb_type mult_36x36.a[9:17] with a full path mult[physical].mult_36x36[0] */ - std::map>> operating_pb_type_ports_; + std::map>> operating_pb_type_ports_; /* Link between the interconnects under this pb_type and circuit model names */ std::map interconnect_circuit_model_names_; diff --git a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp index 9831f6e23..f91dc99a8 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp @@ -114,6 +114,31 @@ void read_xml_pb_port_annotation(pugi::xml_node& xml_port, std::stoi(rotate_offsets[iport])); } } + + /* We have an optional attribute: physical_mode_port_rotate_offset + * Split based on the number of physical pb_type ports that have been defined + */ + const std::string& physical_port_rotate_offset_attr = get_attribute(xml_port, "physical_mode_port_rotate_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(); + + if (false == physical_port_rotate_offset_attr.empty()) { + /* Split the physical mode port attributes with space */ + openfpga::StringToken offset_tokenizer(physical_port_rotate_offset_attr); + const std::vector rotate_offsets = offset_tokenizer.split(); + + /* Error out if the offset does not match the port definition */ + if (physical_mode_ports.size() != rotate_offsets.size()) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port), + "Defined %lu physical mode ports but only %lu physical port rotate offset are defined! Expect size matching.\n", + physical_mode_ports.size(), rotate_offsets.size()); + } + + for (size_t iport = 0; iport < physical_mode_ports.size(); ++iport) { + openfpga::PortParser port_parser(physical_mode_ports[iport]); + pb_type_annotation.set_physical_port_rotate_offset(name_attr, + port_parser.port(), + std::stoi(rotate_offsets[iport])); + } + } } /******************************************************************** diff --git a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp index e9b36522c..b401f6ca8 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp @@ -144,7 +144,14 @@ void write_xml_pb_port_annotation(std::fstream& fp, physical_mode_pin_rotate_offset_attr += std::to_string(physical_pb_port_pair.second[1]); } write_xml_attribute(fp, "physical_mode_pin_rotate_offset", physical_mode_pin_rotate_offset_attr.c_str()); - + std::string physical_mode_port_rotate_offset_attr; + for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) { + if (false == physical_mode_port_rotate_offset_attr.empty()) { + physical_mode_port_rotate_offset_attr += " "; + } + physical_mode_port_rotate_offset_attr += std::to_string(physical_pb_port_pair.second[2]); + } + write_xml_attribute(fp, "physical_mode_port_rotate_offset", physical_mode_port_rotate_offset_attr.c_str()); fp << "/>" << "\n"; } diff --git a/openfpga/src/annotation/annotate_pb_graph.cpp b/openfpga/src/annotation/annotate_pb_graph.cpp index 67a1baba8..bed7997cc 100644 --- a/openfpga/src/annotation/annotate_pb_graph.cpp +++ b/openfpga/src/annotation/annotate_pb_graph.cpp @@ -333,7 +333,7 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, * by the pin rotate offset value * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port */ - int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port, candidate_port); + int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port, candidate_port) + vpr_device_annotation.physical_pb_port_offset(operating_pb_graph_pin->port, candidate_port); int init_offset = vpr_device_annotation.physical_pb_pin_initial_offset(operating_pb_graph_pin->port, candidate_port); const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port, candidate_port); if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number @@ -463,6 +463,14 @@ void annotate_physical_pb_graph_node_pins(t_pb_graph_node* operating_pb_graph_no physical_pb_graph_node, vpr_device_annotation, verbose_output); } + /* Finish a port, accumulate the port-level offset affiliated to the port */ + if (0 == operating_pb_graph_node->num_input_pins[iport]) { + continue; + } + t_pb_graph_pin* operating_pb_graph_pin = &(operating_pb_graph_node->input_pins[iport][0]); + for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { + vpr_device_annotation.accumulate_physical_pb_port_rotate_offset(operating_pb_graph_pin->port, candidate_port); + } } for (int iport = 0; iport < operating_pb_graph_node->num_output_ports; ++iport) { @@ -471,6 +479,14 @@ void annotate_physical_pb_graph_node_pins(t_pb_graph_node* operating_pb_graph_no physical_pb_graph_node, vpr_device_annotation, verbose_output); } + /* Finish a port, accumulate the port-level offset affiliated to the port */ + if (0 == operating_pb_graph_node->num_output_pins[iport]) { + continue; + } + t_pb_graph_pin* operating_pb_graph_pin = &(operating_pb_graph_node->output_pins[iport][0]); + for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { + vpr_device_annotation.accumulate_physical_pb_port_rotate_offset(operating_pb_graph_pin->port, candidate_port); + } } for (int iport = 0; iport < operating_pb_graph_node->num_clock_ports; ++iport) { @@ -479,6 +495,14 @@ void annotate_physical_pb_graph_node_pins(t_pb_graph_node* operating_pb_graph_no physical_pb_graph_node, vpr_device_annotation, verbose_output); } + /* Finish a port, accumulate the port-level offset affiliated to the port */ + if (0 == operating_pb_graph_node->num_clock_pins[iport]) { + continue; + } + t_pb_graph_pin* operating_pb_graph_pin = &(operating_pb_graph_node->clock_pins[iport][0]); + for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { + vpr_device_annotation.accumulate_physical_pb_port_rotate_offset(operating_pb_graph_pin->port, candidate_port); + } } } diff --git a/openfpga/src/annotation/annotate_pb_types.cpp b/openfpga/src/annotation/annotate_pb_types.cpp index 92a6f6560..e4b5f972c 100644 --- a/openfpga/src/annotation/annotate_pb_types.cpp +++ b/openfpga/src/annotation/annotate_pb_types.cpp @@ -211,7 +211,7 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, * if not found, we assume that the physical port is the same as the operating pb_port */ for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) { - std::map> expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); + std::map> expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); /* If not defined in the annotation, set the default pair: * rotate_offset is 0 by default! @@ -243,6 +243,7 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, physical_pb_port, expected_physical_pb_port.first); vpr_device_annotation.add_physical_pb_pin_initial_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[0]); vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[1]); + vpr_device_annotation.add_physical_pb_port_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[2]); } } diff --git a/openfpga/src/annotation/vpr_device_annotation.cpp b/openfpga/src/annotation/vpr_device_annotation.cpp index c2eed00ab..eef4fafbb 100644 --- a/openfpga/src/annotation/vpr_device_annotation.cpp +++ b/openfpga/src/annotation/vpr_device_annotation.cpp @@ -220,6 +220,21 @@ int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* operating_pb_port return physical_pb_pin_rotate_offsets_.at(operating_pb_port).at(physical_pb_port); } +int VprDeviceAnnotation::physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const { + /* Ensure that the pb_type is in the list */ + std::map>::const_iterator it = physical_pb_port_rotate_offsets_.find(operating_pb_port); + if (it == physical_pb_port_rotate_offsets_.end()) { + /* Default value is 0 */ + return 0; + } + if (0 == physical_pb_port_rotate_offsets_.at(operating_pb_port).count(physical_pb_port)) { + /* Default value is 0 */ + return 0; + } + return physical_pb_port_rotate_offsets_.at(operating_pb_port).at(physical_pb_port); +} + int VprDeviceAnnotation::physical_pb_pin_offset(t_port* operating_pb_port, t_port* physical_pb_port) const { /* Ensure that the pb_type is in the list */ @@ -235,6 +250,21 @@ int VprDeviceAnnotation::physical_pb_pin_offset(t_port* operating_pb_port, return physical_pb_pin_offsets_.at(operating_pb_port).at(physical_pb_port); } +int VprDeviceAnnotation::physical_pb_port_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const { + /* Ensure that the pb_type is in the list */ + std::map>::const_iterator it = physical_pb_port_offsets_.find(operating_pb_port); + if (it == physical_pb_port_offsets_.end()) { + /* Default value is 0 */ + return 0; + } + if (0 == physical_pb_port_offsets_.at(operating_pb_port).count(physical_pb_port)) { + /* Default value is 0 */ + return 0; + } + return physical_pb_port_offsets_.at(operating_pb_port).at(physical_pb_port); +} + t_pb_graph_pin* VprDeviceAnnotation::physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const { /* Ensure that the pb_type is in the list */ std::map::const_iterator it = physical_pb_graph_pins_.find(pb_graph_pin); @@ -478,6 +508,28 @@ void VprDeviceAnnotation::add_physical_pb_pin_initial_offset(t_port* operating_p physical_pb_pin_initial_offsets_[operating_pb_port][physical_pb_port] = offset; } +void VprDeviceAnnotation::add_physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset) { + /* Warn any override attempt */ + std::map>::const_iterator it = physical_pb_port_rotate_offsets_.find(operating_pb_port); + if ( (it != physical_pb_port_rotate_offsets_.end()) + && (0 < physical_pb_port_rotate_offsets_[operating_pb_port].count(physical_pb_port)) ) { + VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s' port rotate offset '%d'!\n", + operating_pb_port->name, offset); + } + + physical_pb_port_rotate_offsets_[operating_pb_port][physical_pb_port] = offset; + /* We initialize the accumulated offset to 0 */ + physical_pb_port_offsets_[operating_pb_port][physical_pb_port] = 0; +} + + +void VprDeviceAnnotation::accumulate_physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port) { + physical_pb_port_offsets_[operating_pb_port][physical_pb_port] += physical_pb_port_rotate_offsets_[operating_pb_port][physical_pb_port]; +} + void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port, const int& offset) { diff --git a/openfpga/src/annotation/vpr_device_annotation.h b/openfpga/src/annotation/vpr_device_annotation.h index b5afbda00..d1a0f8e99 100644 --- a/openfpga/src/annotation/vpr_device_annotation.h +++ b/openfpga/src/annotation/vpr_device_annotation.h @@ -67,6 +67,9 @@ class VprDeviceAnnotation { int physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port) const; + int physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const; + /**This function returns an accumulated offset. Note that the * accumulated offset is NOT the pin rotate offset specified by users * It is an aggregation of the offset during pin pairing @@ -76,6 +79,8 @@ class VprDeviceAnnotation { */ int physical_pb_pin_offset(t_port* operating_pb_port, t_port* physical_pb_port) const; + int physical_pb_port_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const; t_pb_graph_pin* physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const; CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const; CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const; @@ -106,6 +111,11 @@ class VprDeviceAnnotation { void add_physical_pb_pin_initial_offset(t_port* operating_pb_port, t_port* physical_pb_port, const int& offset); + void add_physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset); + void accumulate_physical_pb_port_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port); void add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port, const int& offset); @@ -166,8 +176,11 @@ class VprDeviceAnnotation { std::map> physical_pb_ports_; std::map> physical_pb_pin_initial_offsets_; std::map> physical_pb_pin_rotate_offsets_; + std::map> physical_pb_port_rotate_offsets_; - /* Accumulated offsets for a physical pb_type port, just for internal usage */ + /* Accumulated offsets for a physical pb port, just for internal usage */ + std::map> physical_pb_port_offsets_; + /* Accumulated offsets for a physical pb_graph_pin, just for internal usage */ std::map> physical_pb_pin_offsets_; /* Pair a pb_port to its LSB and MSB of a physical pb_port diff --git a/openfpga/src/repack/repack.cpp b/openfpga/src/repack/repack.cpp index d6cb669bc..7538c675a 100644 --- a/openfpga/src/repack/repack.cpp +++ b/openfpga/src/repack/repack.cpp @@ -61,9 +61,9 @@ bool rec_direct_search_sink_pb_graph_pins(const t_pb_graph_pin* source_pb_pin, std::vector sink_pb_pins_to_search; /* Only support single-mode pb_type!!! */ - if (1 != source_pb_pin->parent_node->pb_type->num_modes) { - return false; - } + //if (1 != source_pb_pin->parent_node->pb_type->num_modes) { + // return false; + //} for (int iedge = 0; iedge < source_pb_pin->num_output_edges; ++iedge) { if (DIRECT_INTERC != source_pb_pin->output_edges[iedge]->interconnect->type) { From a3a98fa21d6365a67f57db0c1ef2ae11a9237df8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 14:56:10 -0600 Subject: [PATCH 039/164] [Arch] Bug fix for port name mismatching between openfpga cell library and architecture definition --- ...frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml index 7ce8c5c28..78978c2c7 100644 --- a/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml @@ -210,9 +210,9 @@ - - - + + + From 8b8096f3a860e655ebebd644f8673a3b5c2e11cc Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 14:57:09 -0600 Subject: [PATCH 040/164] [HDL] Bug fix in HDL modeling of multi-mode 16-bit DSP block --- openfpga_flow/openfpga_cell_library/verilog/frac_mult_16x16.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_cell_library/verilog/frac_mult_16x16.v b/openfpga_flow/openfpga_cell_library/verilog/frac_mult_16x16.v index 7fedc711c..d73a1c7e1 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/frac_mult_16x16.v +++ b/openfpga_flow/openfpga_cell_library/verilog/frac_mult_16x16.v @@ -13,7 +13,7 @@ module frac_mult_16x16 ( output [0:31] out, input [0:0] mode); - reg [0:63] out_reg; + reg [0:31] out_reg; always @(mode, a, b) begin if (1'b1 == mode) begin From 80f98328df34411a6baaaf6b4ac57ee59a79c762 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 15:16:50 -0600 Subject: [PATCH 041/164] [Test] Update test settings for architecture with fracturable DSP blocks --- .../dsp/multi_mode_mult_16x16/config/task.conf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/dsp/multi_mode_mult_16x16/config/task.conf b/openfpga_flow/tasks/fpga_verilog/dsp/multi_mode_mult_16x16/config/task.conf index 0db63ef8b..b9e2c957b 100644 --- a/openfpga_flow/tasks/fpga_verilog/dsp/multi_mode_mult_16x16/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/dsp/multi_mode_mult_16x16/config/task.conf @@ -24,7 +24,7 @@ yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techli yosys_dsp_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v yosys_dsp_map_parameters=-D DSP_A_MAXWIDTH=8 -D DSP_B_MAXWIDTH=8 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_8x8 # VPR parameter -openfpga_vpr_device_layout=3x2 +openfpga_vpr_device_layout=3x4 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml @@ -32,7 +32,8 @@ arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_sof [BENCHMARKS] bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_4/mac_4.v bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_8/mac_8.v -bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_16/mac_16.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_12/mac_12.v +bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_16/mac_16.v [SYNTHESIS_PARAM] bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dsp_flow.ys @@ -40,7 +41,8 @@ bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosy bench0_top = mac_4 bench1_top = mac_8 -bench2_top = mac_16 +bench2_top = mac_12 +bench3_top = mac_16 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 5adffad6023b8dad5bbbc75a9ab2de21b794e836 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 15:49:53 -0600 Subject: [PATCH 042/164] [Arch] Changes to the arch to avoid a bug where the rr_nodes at top side of a heterogenenous block have no fan-in!!! --- ...ac_dsp16_nonLR_caravel_io_skywater130nm.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml index 98bae4fa2..2d817e7cd 100644 --- a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml @@ -111,7 +111,7 @@ These clocks can be handled in back-end --> - + @@ -123,7 +123,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -217,10 +217,12 @@ - mult_16.a[0:5] mult_16.b[0:5] mult_16.out[0:10] - mult_16.a[6:7] mult_16.b[6:7] mult_16.out[11:15] - mult_16.a[8:13] mult_16.b[8:13] mult_16.out[16:26] - mult_16.a[14:15] mult_16.b[14:15] mult_16.out[27:31] + mult_16.a[0:2] mult_16.b[0:2] mult_16.out[0:5] + mult_16.a[3:5] mult_16.b[3:5] mult_16.out[6:10] + + mult_16.a[8:10] mult_16.b[8:10] mult_16.out[16:21] + mult_16.a[11:13] mult_16.b[11:13] mult_16.out[22:26] + mult_16.a[6:7] mult_16.b[6:7] mult_16.out[11:15] mult_16.a[14:15] mult_16.b[14:15] mult_16.out[27:31] @@ -239,7 +241,7 @@ - + From b7da22501c4ff35e56b6af623aceadaad967331f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 15:55:05 -0600 Subject: [PATCH 043/164] [Test] Deply new test to regression test --- openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh index 0033ad8ac..9168cbd1e 100755 --- a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh @@ -47,6 +47,9 @@ run-task fpga_verilog/bram/wide_dpram16k --debug --show_thread_logs echo -e "Testing Verilog generation with heterogeneous fabric using 8-bit single-mode multipliers "; run-task fpga_verilog/dsp/single_mode_mult_8x8 --debug --show_thread_logs +echo -e "Testing Verilog generation with heterogeneous fabric using 16-bit multi-mode multipliers "; +run-task fpga_verilog/dsp/multi_mode_mult_16x16 --debug --show_thread_logs + echo -e "Testing Verilog generation with different I/O capacities on each side of an FPGA "; run-task fpga_verilog/io/multi_io_capacity --debug --show_thread_logs From 62dc5a3856035a009363f3862a0053b66908b72d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 24 Apr 2021 16:02:24 -0600 Subject: [PATCH 044/164] [Doc] Update documentation about the new syntax introduced for pin binding between operating modes and physical modes --- .../manual/arch_lang/annotate_vpr_arch.rst | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/source/manual/arch_lang/annotate_vpr_arch.rst b/docs/source/manual/arch_lang/annotate_vpr_arch.rst index 239d10dfb..f4767c938 100644 --- a/docs/source/manual/arch_lang/annotate_vpr_arch.rst +++ b/docs/source/manual/arch_lang/annotate_vpr_arch.rst @@ -223,7 +223,9 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. note:: A ```` parent XML node is required for the interconnect-to-circuit bindings whose interconnects are defined under the ``pb_type`` in VPR architecture description. .. option:: + physical_mode_pin_initial_offset="" + physical_mode_pin_rotate_offset=""/> + physical_mode_port_rotate_offset=""/> Link a port of an operating ``pb_type`` to a port of a physical ``pb_type`` @@ -233,7 +235,6 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. note:: Users can define multiple ports. For example: ``physical_mode_pin="a[0:1] b[2:2]"``. When multiple ports are used, the ``physical_mode_pin_initial_offset`` and ``physical_mode_pin_rotate_offset`` should also be adapt. For example: ``physical_mode_pin_rotate_offset="1 0"``) - - ``physical_mode_pin_initial_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when part of port of operating mode is mapped to a port in physical ``pb_type``. When ``physical_mode_pin_initial_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. .. note:: A quick example to understand the initial offset @@ -249,7 +250,24 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. note:: If not defined, the default value of ``physical_mode_pin_initial_offset`` is set to ``0``. - - ``physical_mode_pin_rotate_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. + - ``physical_mode_pin_rotate_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset, **each time a pin in the operating mode is binded to a pin in the physical mode**. + + .. note:: A quick example to understand the rotate offset + For example, a rotating offset of 9 is used to map + + - operating pb_type ``mult_9x9[0].a[0]`` with a full path ``mult[frac].mult_9x9[0]`` + - operating pb_type ``mult_9x9[1].a[1]`` with a full path ``mult[frac].mult_9x9[1]`` + + to + + - physical pb_type ``mult_36x36.a[0]`` with a full path ``mult[physical].mult_36x36[0]`` + - physical pb_type ``mult_36x36.a[9]`` with a full path ``mult[physical].mult_36x36[0]`` + + .. note:: If not defined, the default value of ``physical_mode_pin_rotate_offset`` is set to ``0``. + + .. warning:: The result of using ``physical_mode_pin_rotate_offset`` is fundementally different than ``physical_mode_port_rotate_offset``!!! Please read the examples carefully and pick the one fitting your needs. + + - ``physical_mode_port_rotate_offset=""`` aims to align the port indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_port_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset, **only when all the pins of a port in the operating mode is binded to all the pins of a port in the physical mode**. .. note:: A quick example to understand the rotate offset For example, a rotating offset of 9 is used to map @@ -262,7 +280,8 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de - physical pb_type ``mult_36x36.a[0:8]`` with a full path ``mult[physical].mult_36x36[0]`` - physical pb_type ``mult_36x36.a[9:17]`` with a full path ``mult[physical].mult_36x36[0]`` - .. note:: If not defined, the default value of ``physical_mode_pin_rotate_offset`` is set to ``0``. + .. note:: If not defined, the default value of ``physical_mode_port_rotate_offset`` is set to ``0``. + .. note:: It is highly recommended that only one physical mode is defined for a multi-mode configurable block. Try not to use nested physical mode definition. This will ease the debugging and lead to clean XML description. From 94c575fa7406aec832788b8c315551714eb912c1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 18:12:12 -0600 Subject: [PATCH 045/164] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b465fdee4..dc13788d2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,12 +10,21 @@ assignees: '' **Describe the bug** A clear and concise description of what the bug is. +Which part of OpenFPGA is buggy +- [ ] Documentation +- [ ] OpenFPGA flow +- [ ] FPGA-Verilog +- [ ] FPGA-Bitstream +- [ ] FPGA-SDC +- [ ] FPGA-SPICE +- [ ] VPR +- [ ] Yosys + **To Reproduce** Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +1. Clone OpenFPGA repository and checkout commit id: +2. Execute OpenFPGA task or your own example: +3. See error **Expected behavior** A clear and concise description of what you expected to happen. @@ -24,9 +33,22 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Enviornment (please complete the following information):** - - OS: [e.g. CentOs, Ubuntu] - - Compiler [e.g. gcc, clang] - - Version [e.g. Github commit id] + - OS: + - [ ] CentOS 7 + - [ ] Ubuntu 18.04 + - [ ] Others. If so, please specify: + - Compiler + - [ ] gcc-5 + - [ ] gcc-6 + - [ ] gcc-7 + - [ ] gcc-8 + - [ ] gcc-9 + - [ ] clang-6 + - [ ] clang-8 + - [ ] Others. If so, please specify: + - Version + - [ ] Current master + - [ ] Others. If so, please specify Github commit id: **Additional context** Add any other context about the problem here. From 386dbf8c1a66ddd4d1e6f7b978ce59d2c17d4cc4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 18:30:48 -0600 Subject: [PATCH 046/164] Update pull_request_template.md --- .../pull_request_template.md | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md index d1c458c04..8bec074e4 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -1,33 +1,35 @@ ---- -name: Pull request -about: Push a change to this project ---- +> ### Motivate of the pull request +> - [ ] To address an existing issue. If so, please provide a link to the issue: +> - [ ] Breaking new feature. If so, please describe details in the description part. -### Motivate of the pull request -- [ ] To address an existing issue. If so, please provide a link to the issue. -- [ ] Breaking new feature. If so, please decribe details in the description part. +> ### Describe the technical details +> #### What is currently done? (Provide issue link if applicable) +> ** Please provide a list of limitations if not specified in any issue ** +> - [ ] +> - [ ] +> +> #### What does this pull request change? +> ** Please provide a list of highlights of your changes. ** +> - [ ]
+> - [ ] -### Describe the technical details -- What is currently done? (Provide issue link if applicable) -- What does this pull request change? +> ### Which part of the code base require a change +> **In general, modification on existing submodules are not acceptable. You should push changes to upstream.** +> - [ ] VPR +> - [ ] Tileable routing architecture generator +> - [ ] OpenFPGA libraries +> - [ ] FPGA-Verilog +> - [ ] FPGA-Bitstream +> - [ ] FPGA-SDC +> - [ ] FPGA-SPICE +> - [ ] Flow scripts +> - [ ] Architecture library +> - [ ] Cell library +> - [ ] Documentation +> - [ ] Regression tests +> - [ ] Continous Integration (CI) scripts -### Which part of the code base require a change -**In general, modification on existing submodules are not acceptable. You should push changes to upstream.** -- [ ] VPR -- [ ] OpenFPGA libraries -- [ ] FPGA-Verilog -- [ ] FPGA-Bitstream -- [ ] FPGA-SDC -- [ ] FPGA-SPICE -- [ ] Flow scripts -- [ ] Architecture library -- [ ] Cell library +> ### Impact of the pull request -### Checklist of the pull request -- [ ] Require code changes. -- [ ] Require new tests to be added -- [ ] Require an update on documentation - -### Impact of the pull request -- [ ] Require a change on Quality of Results (QoR) -- [ ] Break back-compatibility. If so, please list who may be influenced. +> - [ ] Require a change on Quality of Results (QoR) +> - [ ] Break back-compatibility. If so, please list who may be influenced. From 4b8dab09134db788151f7b6c44b8630762afdd6d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 20:51:29 -0600 Subject: [PATCH 047/164] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 47 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dc13788d2..7c4465cb3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,16 +9,16 @@ assignees: '' **Describe the bug** A clear and concise description of what the bug is. - -Which part of OpenFPGA is buggy -- [ ] Documentation -- [ ] OpenFPGA flow -- [ ] FPGA-Verilog -- [ ] FPGA-Bitstream -- [ ] FPGA-SDC -- [ ] FPGA-SPICE -- [ ] VPR -- [ ] Yosys + + + + + + + + + + **To Reproduce** Steps to reproduce the behavior: @@ -33,22 +33,23 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Enviornment (please complete the following information):** + - OS: - - [ ] CentOS 7 - - [ ] Ubuntu 18.04 - - [ ] Others. If so, please specify: + + + - Compiler - - [ ] gcc-5 - - [ ] gcc-6 - - [ ] gcc-7 - - [ ] gcc-8 - - [ ] gcc-9 - - [ ] clang-6 - - [ ] clang-8 - - [ ] Others. If so, please specify: + + + + + + + + - Version - - [ ] Current master - - [ ] Others. If so, please specify Github commit id: + + **Additional context** Add any other context about the problem here. From 83167b6b61097bb44d1d08a7fb12e441686a71f3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 22:06:13 -0600 Subject: [PATCH 048/164] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7c4465cb3..d7543b878 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,7 +38,7 @@ If applicable, add screenshots to help explain your problem. - - Compiler + - Compiler: @@ -47,7 +47,7 @@ If applicable, add screenshots to help explain your problem. - - Version + - Version: From a8b2966709e13774cca8edc3cbd758588fcafc3d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 22:08:17 -0600 Subject: [PATCH 049/164] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d7543b878..b5ef99630 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,8 +7,8 @@ assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +> **Describe the bug** +> A clear and concise description of what the bug is. @@ -20,25 +20,25 @@ A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Clone OpenFPGA repository and checkout commit id: -2. Execute OpenFPGA task or your own example: -3. See error +> **To Reproduce** +> Steps to reproduce the behavior: +> 1. Clone OpenFPGA repository and checkout commit id: +> 2. Execute OpenFPGA task or your own example: +> 3. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +> **Expected behavior** +> A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +> **Screenshots** +> If applicable, add screenshots to help explain your problem. -**Enviornment (please complete the following information):** +> **Enviornment (please complete the following information):** - - OS: +> - OS: - - Compiler: +> - Compiler: @@ -47,9 +47,9 @@ If applicable, add screenshots to help explain your problem. - - Version: +> - Version: -**Additional context** -Add any other context about the problem here. +> **Additional context** +> Add any other context about the problem here. From deb9f4a9f78990276a5a4051304c5d43a6284b79 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Apr 2021 22:11:34 -0600 Subject: [PATCH 050/164] Update pull_request_template.md --- .../pull_request_template.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md index 8bec074e4..f1b1063fd 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -4,17 +4,21 @@ > ### Describe the technical details > #### What is currently done? (Provide issue link if applicable) -> ** Please provide a list of limitations if not specified in any issue ** -> - [ ] -> - [ ] +> +> +> +> +> > > #### What does this pull request change? -> ** Please provide a list of highlights of your changes. ** -> - [ ]
-> - [ ] +> +> +> +> +> > ### Which part of the code base require a change -> **In general, modification on existing submodules are not acceptable. You should push changes to upstream.** +> > - [ ] VPR > - [ ] Tileable routing architecture generator > - [ ] OpenFPGA libraries From 880624e69925defd9a858d0e88de352379d071b7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 11:48:02 -0600 Subject: [PATCH 051/164] [Tool] Update comments in tileable rr_graph generator to be easier to be understood --- .../tileable_rr_graph_gsb.cpp | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp index 2cef83e5c..524a856da 100755 --- a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -821,9 +821,8 @@ RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, opin_grid_side[1] = NUM_SIDES; } - /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block */ for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { - /* Local variables inside this for loop */ SideManager side_manager(side); size_t ix; size_t iy; @@ -832,42 +831,34 @@ RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, enum e_side ipin_rr_node_grid_side; switch (side) { - case TOP: /* TOP = 0 */ - /* For the bording, we should take special care */ - /* Check if left side chan width is 0 or not */ + case TOP: + /* Consider the routing channel that is connected to the left side of the switch block */ chan_side = LEFT; - /* Build the connection block: ipin and ipin_grid_side */ - /* BOTTOM side INPUT Pins of Grid[x][y+1] */ + /* The input pins of the routing channel come from the bottom side of Grid[x][y+1] */ ix = rr_gsb.get_sb_x(); iy = rr_gsb.get_sb_y() + 1; ipin_rr_node_grid_side = BOTTOM; break; - case RIGHT: /* RIGHT = 1 */ - /* For the bording, we should take special care */ - /* Check if TOP side chan width is 0 or not */ + case RIGHT: + /* Consider the routing channel that is connected to the top side of the switch block */ chan_side = TOP; - /* Build the connection block: ipin and ipin_grid_side */ - /* LEFT side INPUT Pins of Grid[x+1][y+1] */ + /* The input pins of the routing channel come from the left side of Grid[x+1][y+1] */ ix = rr_gsb.get_sb_x() + 1; iy = rr_gsb.get_sb_y() + 1; ipin_rr_node_grid_side = LEFT; break; - case BOTTOM: /* BOTTOM = 2*/ - /* For the bording, we should take special care */ - /* Check if left side chan width is 0 or not */ + case BOTTOM: + /* Consider the routing channel that is connected to the left side of the switch block */ chan_side = LEFT; - /* Build the connection block: ipin and ipin_grid_side */ - /* TOP side INPUT Pins of Grid[x][y] */ + /* The input pins of the routing channel come from the top side of Grid[x][y] */ ix = rr_gsb.get_sb_x(); iy = rr_gsb.get_sb_y(); ipin_rr_node_grid_side = TOP; break; - case LEFT: /* LEFT = 3 */ - /* For the bording, we should take special care */ - /* Check if left side chan width is 0 or not */ + case LEFT: + /* Consider the routing channel that is connected to the top side of the switch block */ chan_side = TOP; - /* Build the connection block: ipin and ipin_grid_side */ - /* RIGHT side INPUT Pins of Grid[x][y+1] */ + /* The input pins of the routing channel come from the right side of Grid[x][y+1] */ ix = rr_gsb.get_sb_x(); iy = rr_gsb.get_sb_y() + 1; ipin_rr_node_grid_side = RIGHT; From cbd7105083fc0b2a3c2ad26da98263041c6fc350 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 11:57:17 -0600 Subject: [PATCH 052/164] [Tool] Add illustrative comments to tileable rr_graph generator --- .../tileable_rr_graph_gsb.cpp | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp index 524a856da..12b9dd0b8 100755 --- a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -821,7 +821,34 @@ RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, opin_grid_side[1] = NUM_SIDES; } - /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block */ + /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * | Grid[x][y+1] | + * | BOTTOM side | + * +-----------------------+ + * | + * v + * +-----------------------+ + * | TOP side | + * | X- Connection Block | + * | BOTTOM side | + * +-----------------------+ + * ^ + * | + * +-----------------------+ + * | TOP side | + * | Grid[x][y] | + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * ---------------+ +---------------------- ... ---------------------+ +---------------- + * Grid[x][y+1] |->| Y- Connection Block Y- Connection Block |<-| Grid[x+1][y+1] + * RIGHT side | | LEFT side ... RIGHT side | | LEFT side + * --------------+ +---------------------- ... ---------------------+ +---------------- + * + */ for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { SideManager side_manager(side); size_t ix; From 6e87b8875bc90f4ee69cb44ba3b9e768ac8ad3aa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 11:59:25 -0600 Subject: [PATCH 053/164] [Arch] Patch the pin location of frac dsp16 to appear on the top side of a height=2 block --- ...r_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml index 2d817e7cd..fd23eb91e 100644 --- a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm.xml @@ -219,10 +219,10 @@ mult_16.a[0:2] mult_16.b[0:2] mult_16.out[0:5] mult_16.a[3:5] mult_16.b[3:5] mult_16.out[6:10] - + mult_16.a[6:7] mult_16.b[6:7] mult_16.out[11:15] mult_16.a[8:10] mult_16.b[8:10] mult_16.out[16:21] mult_16.a[11:13] mult_16.b[11:13] mult_16.out[22:26] - mult_16.a[6:7] mult_16.b[6:7] mult_16.out[11:15] mult_16.a[14:15] mult_16.b[14:15] mult_16.out[27:31] + mult_16.a[14:15] mult_16.b[14:15] mult_16.out[27:31] From 7d4c5e3cd145e52e93ca93d7a879774b630fce49 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 12:00:57 -0600 Subject: [PATCH 054/164] [Arch] Patch pin location of dsp8 to be evenly placed on the right side of a height=2 block --- ...register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml index fea61541c..187a3e474 100644 --- a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml @@ -210,7 +210,8 @@ - mult_8.a[0:5] mult_8.b[0:5] mult_8.out[0:10] + mult_8.a[0:2] mult_8.b[0:2] mult_8.out[0:5] + mult_8.a[3:5] mult_8.b[3:5] mult_8.out[6:10] mult_8.a[6:7] mult_8.b[6:7] mult_8.out[11:15] From 05f08c2f2510bb1378dc8cf3350538aa24d6ae8d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 12:05:37 -0600 Subject: [PATCH 055/164] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 64 +++++++++++++++++--------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d1c458c04..f1b1063fd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,33 +1,39 @@ ---- -name: Pull request -about: Push a change to this project ---- +> ### Motivate of the pull request +> - [ ] To address an existing issue. If so, please provide a link to the issue: +> - [ ] Breaking new feature. If so, please describe details in the description part. -### Motivate of the pull request -- [ ] To address an existing issue. If so, please provide a link to the issue. -- [ ] Breaking new feature. If so, please decribe details in the description part. +> ### Describe the technical details +> #### What is currently done? (Provide issue link if applicable) +> +> +> +> +> +> +> #### What does this pull request change? +> +> +> +> +> -### Describe the technical details -- What is currently done? (Provide issue link if applicable) -- What does this pull request change? +> ### Which part of the code base require a change +> +> - [ ] VPR +> - [ ] Tileable routing architecture generator +> - [ ] OpenFPGA libraries +> - [ ] FPGA-Verilog +> - [ ] FPGA-Bitstream +> - [ ] FPGA-SDC +> - [ ] FPGA-SPICE +> - [ ] Flow scripts +> - [ ] Architecture library +> - [ ] Cell library +> - [ ] Documentation +> - [ ] Regression tests +> - [ ] Continous Integration (CI) scripts -### Which part of the code base require a change -**In general, modification on existing submodules are not acceptable. You should push changes to upstream.** -- [ ] VPR -- [ ] OpenFPGA libraries -- [ ] FPGA-Verilog -- [ ] FPGA-Bitstream -- [ ] FPGA-SDC -- [ ] FPGA-SPICE -- [ ] Flow scripts -- [ ] Architecture library -- [ ] Cell library +> ### Impact of the pull request -### Checklist of the pull request -- [ ] Require code changes. -- [ ] Require new tests to be added -- [ ] Require an update on documentation - -### Impact of the pull request -- [ ] Require a change on Quality of Results (QoR) -- [ ] Break back-compatibility. If so, please list who may be influenced. +> - [ ] Require a change on Quality of Results (QoR) +> - [ ] Break back-compatibility. If so, please list who may be influenced. From 8c007c7c4987ba6c7dfb28ade2936cf46ad83f51 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 16:28:10 -0600 Subject: [PATCH 056/164] [Arch] Add a new example architecture where a DSP block occupies a 2x2 grid --- ...c_dsp16_nonLR_caravel_io_skywater130nm.xml | 966 ++++++++++++++++++ 1 file changed, 966 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_wide_frac_dsp16_nonLR_caravel_io_skywater130nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_wide_frac_dsp16_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_wide_frac_dsp16_nonLR_caravel_io_skywater130nm.xml new file mode 100644 index 000000000..4e30607e2 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_wide_frac_dsp16_nonLR_caravel_io_skywater130nm.xml @@ -0,0 +1,966 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io_top.outpad io_top.inpad + + + + + + + + + + + + io_right.outpad io_right.inpad + + + + + + + + + + + + io_bottom.outpad io_bottom.inpad + + + + + + + + + + + + io_left.outpad io_left.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clb.clk clb.reset + clb.reg_in clb.sc_in clb.cin clb.O[7:0] clb.I0 clb.I0i clb.I1 clb.I1i clb.I2 clb.I2i clb.I3 clb.I3i + clb.O[15:8] clb.I4 clb.I4i clb.I5 clb.I5i clb.I6 clb.I6i clb.I7 clb.I7i + clb.reg_out clb.sc_out clb.cout + + + + + + + + + + + + + + mult_16.a[0:1] mult_16.b[0:1] mult_16.out[0:3] + mult_16.a[2:3] mult_16.b[2:3] mult_16.out[4:7] + mult_16.a[4:5] mult_16.b[4:5] mult_16.out[8:11] + mult_16.a[6:7] mult_16.b[6:7] mult_16.out[12:15] + mult_16.a[8:9] mult_16.b[8:9] mult_16.out[16:19] + mult_16.a[10:11] mult_16.b[10:11] mult_16.out[20:23] + mult_16.a[12:13] mult_16.b[12:13] mult_16.out[24:27] + mult_16.a[14:15] mult_16.b[14:15] mult_16.out[28:31] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 6291871faf9874777eaffd25910b64d74277a4ba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 16:28:43 -0600 Subject: [PATCH 057/164] [Test] Added a test for the example architecture with 2x2 DSP blocks --- .../config/task.conf | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/dsp/wide_multi_mode_mult_16x16/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/dsp/wide_multi_mode_mult_16x16/config/task.conf b/openfpga_flow/tasks/fpga_verilog/dsp/wide_multi_mode_mult_16x16/config/task.conf new file mode 100644 index 000000000..3923047e8 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/dsp/wide_multi_mode_mult_16x16/config/task.conf @@ -0,0 +1,45 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N8_reset_softadder_register_scan_chain_frac_dsp16_caravel_io_skywater130nm_fdhd_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +# Yosys script parameters +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_cell_sim.v +yosys_dsp_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k4_frac_N8_tileable_reset_softadder_register_scan_chain_frac_dsp16_nonLR_caravel_io_skywater130nm_dsp_map.v +yosys_dsp_map_parameters=-D DSP_A_MAXWIDTH=8 -D DSP_B_MAXWIDTH=8 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_8x8 +# VPR parameter +openfpga_vpr_device_layout=4x4 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N8_tileable_reset_softadder_register_scan_chain_wide_frac_dsp16_nonLR_caravel_io_skywater130nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_8/mac_8.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/mac/mac_16/mac_16.v + +[SYNTHESIS_PARAM] +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dsp_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys + +bench0_top = mac_8 +bench1_top = mac_16 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 1d5e926d9e805fafa011a4b2838f6373f0123e90 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 16:29:54 -0600 Subject: [PATCH 058/164] [Test] Deploy new test to CI --- openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh index 9168cbd1e..8f64816de 100755 --- a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh @@ -50,6 +50,9 @@ run-task fpga_verilog/dsp/single_mode_mult_8x8 --debug --show_thread_logs echo -e "Testing Verilog generation with heterogeneous fabric using 16-bit multi-mode multipliers "; run-task fpga_verilog/dsp/multi_mode_mult_16x16 --debug --show_thread_logs +echo -e "Testing Verilog generation with heterogeneous fabric using multi-width 16-bit multi-mode multipliers "; +run-task fpga_verilog/dsp/wide_multi_mode_mult_16x16 --debug --show_thread_logs + echo -e "Testing Verilog generation with different I/O capacities on each side of an FPGA "; run-task fpga_verilog/io/multi_io_capacity --debug --show_thread_logs From 33c12c8f0ef801d5e3cdc9a2981367334ddfd1e9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Apr 2021 20:51:54 -0600 Subject: [PATCH 059/164] Update CMakeLists.txt --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49b2fa1fb..a6a055ba1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,6 @@ else () "-D__USE_FIXED_PROTOTYPES__" "-ansi" "-Wshadow" - "-Wcast-allign" "-Wno-write-strings" "-D_POSIX_SOURCE" "-Wall" #Most warnings, typically good From 43c1e052efcf28f8ceeb7b5f5e58dfa5ae677652 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:30:16 -0600 Subject: [PATCH 060/164] [Tool] Add a writer to output I/O mapping information to XML files --- openfpga/src/base/io_map.cpp | 54 +++++++ openfpga/src/base/io_map.h | 59 +++++++ openfpga/src/base/io_map_fwd.h | 23 +++ openfpga/src/base/openfpga_bitstream.cpp | 44 ++++++ openfpga/src/base/openfpga_bitstream.h | 3 + .../src/base/openfpga_bitstream_command.cpp | 38 +++++ .../fpga_bitstream/build_io_mapping_info.cpp | 144 +++++++++++++++++ .../fpga_bitstream/build_io_mapping_info.h | 33 ++++ .../fpga_bitstream/write_xml_io_mapping.cpp | 145 ++++++++++++++++++ .../src/fpga_bitstream/write_xml_io_mapping.h | 25 +++ 10 files changed, 568 insertions(+) create mode 100644 openfpga/src/base/io_map.cpp create mode 100644 openfpga/src/base/io_map.h create mode 100644 openfpga/src/base/io_map_fwd.h create mode 100644 openfpga/src/fpga_bitstream/build_io_mapping_info.cpp create mode 100644 openfpga/src/fpga_bitstream/build_io_mapping_info.h create mode 100644 openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp create mode 100644 openfpga/src/fpga_bitstream/write_xml_io_mapping.h diff --git a/openfpga/src/base/io_map.cpp b/openfpga/src/base/io_map.cpp new file mode 100644 index 000000000..3012c4285 --- /dev/null +++ b/openfpga/src/base/io_map.cpp @@ -0,0 +1,54 @@ +/****************************************************************************** + * Memember functions for data structure IoMap + ******************************************************************************/ +#include "vtr_assert.h" + +#include "io_map.h" + +/* begin namespace openfpga */ +namespace openfpga { + +IoMap::io_map_range IoMap::io_map() const { + return vtr::make_range(io_map_ids_.begin(), io_map_ids_.end()); +} + +BasicPort IoMap::io_port(IoMapId io_map_id) const { + VTR_ASSERT(valid_io_map_id(io_map_id)); + return io_ports_[io_map_id]; +} + +BasicPort IoMap::io_net(IoMapId io_map_id) const { + VTR_ASSERT(valid_io_map_id(io_map_id)); + return mapped_nets_[io_map_id]; +} + +bool IoMap::is_io_output(IoMapId io_map_id) const { + VTR_ASSERT(valid_io_map_id(io_map_id)); + return IoMap::IO_MAP_DIR_OUTPUT == io_directionality_[io_map_id]; +} + +bool IoMap::is_io_input(IoMapId io_map_id) const { + VTR_ASSERT(valid_io_map_id(io_map_id)); + return IoMap::IO_MAP_DIR_INPUT == io_directionality_[io_map_id]; +} + +IoMapId IoMap::create_io_mapping(const BasicPort& port, + const BasicPort& net, + IoMap::e_direction dir) { + /* Create a new id */ + IoMapId io_map_id = IoMapId(io_map_ids_.size()); + io_map_ids_.push_back(io_map_id); + + /* Allocate related attributes */ + io_ports_.push_back(port); + mapped_nets_.push_back(net); + io_directionality_.push_back(dir); + + return io_map_id; +} + +bool IoMap::valid_io_map_id(IoMapId io_map_id) const { + return (size_t(io_map_id) < io_map_ids_.size()) && (io_map_id == io_map_ids_[io_map_id]); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/io_map.h b/openfpga/src/base/io_map.h new file mode 100644 index 000000000..5bc378000 --- /dev/null +++ b/openfpga/src/base/io_map.h @@ -0,0 +1,59 @@ +#ifndef IO_MAP_H +#define IO_MAP_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include "vtr_vector.h" +#include "openfpga_port.h" +#include "io_map_fwd.h" + +/* Begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This is a data structure storing io mapping information + * - the net-to-I/O mapping + * - each I/O directionality + *******************************************************************/ +class IoMap { + public: /* Types and ranges */ + enum e_direction { + IO_MAP_DIR_INPUT, + IO_MAP_DIR_OUTPUT, + NUM_IO_MAP_DIR_TYPES + }; + typedef vtr::vector::const_iterator io_map_iterator; + typedef vtr::Range io_map_range; + public: /* Public aggregators */ + /* Find all io mapping */ + io_map_range io_map() const; + + /* Get the port of the io that is mapped */ + BasicPort io_port(IoMapId io_map_id) const; + + /* Get the net of the io that is mapped to */ + BasicPort io_net(IoMapId io_map_id) const; + + /* Query on if an io is configured as an input */ + bool is_io_input(IoMapId io_map_id) const; + + /* Query on if an io is configured as an output */ + bool is_io_output(IoMapId io_map_id) const; + public: /* Public mutators */ + /* Create a new I/O mapping */ + IoMapId create_io_mapping(const BasicPort& port, + const BasicPort& net, + e_direction dir); + public: /* Public validators/invalidators */ + bool valid_io_map_id(IoMapId io_map_id) const; + private: /* Internal Data */ + vtr::vector io_map_ids_; + vtr::vector io_ports_; + vtr::vector mapped_nets_; + vtr::vector io_directionality_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/base/io_map_fwd.h b/openfpga/src/base/io_map_fwd.h new file mode 100644 index 000000000..0de3e99c1 --- /dev/null +++ b/openfpga/src/base/io_map_fwd.h @@ -0,0 +1,23 @@ +/************************************************** + * This file includes only declarations for + * the data structures for IoMap + * Please refer to io_map.h for more details + *************************************************/ +#ifndef IO_MAP_FWD_H +#define IO_MAP_FWD_H + +#include "vtr_strong_id.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/* Strong Ids */ +struct io_map_id_tag; + +typedef vtr::StrongId IoMapId; + +class IoMap; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp index c32e9ae87..c35017ba8 100644 --- a/openfpga/src/base/openfpga_bitstream.cpp +++ b/openfpga/src/base/openfpga_bitstream.cpp @@ -15,10 +15,14 @@ #include "read_xml_arch_bitstream.h" #include "write_xml_arch_bitstream.h" +#include "openfpga_naming.h" + #include "build_device_bitstream.h" #include "write_text_fabric_bitstream.h" #include "write_xml_fabric_bitstream.h" #include "build_fabric_bitstream.h" +#include "build_io_mapping_info.h" +#include "write_xml_io_mapping.h" #include "openfpga_bitstream.h" /* Include global variables of VPR */ @@ -121,4 +125,44 @@ int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx, return status; } +/******************************************************************** + * A wrapper function to call the write_io_mapping() in FPGA bitstream + *******************************************************************/ +int write_io_mapping(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_verbose = cmd.option("verbose"); + CommandOptionId opt_file = cmd.option("file"); + + /* Write fabric bitstream if required */ + int status = CMD_EXEC_SUCCESS; + + VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_file)); + + std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_file)); + + /* Create directories */ + create_directory(src_dir_path); + + /* Create a module as the top-level fabric, and add it to the module manager */ + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = openfpga_ctx.module_graph().find_module(top_module_name); + VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); + + IoMap io_map = build_fpga_io_mapping_info(openfpga_ctx.module_graph(), + top_module, + g_vpr_ctx.atom(), + g_vpr_ctx.placement(), + openfpga_ctx.io_location_map(), + openfpga_ctx.vpr_netlist_annotation(), + std::string(), + std::string()); + + status = write_io_mapping_to_xml_file(io_map, + cmd_context.option_value(cmd, opt_file), + cmd_context.option_enable(cmd, opt_verbose)); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_bitstream.h b/openfpga/src/base/openfpga_bitstream.h index c210d6bab..2e438d56b 100644 --- a/openfpga/src/base/openfpga_bitstream.h +++ b/openfpga/src/base/openfpga_bitstream.h @@ -24,6 +24,9 @@ int build_fabric_bitstream(OpenfpgaContext& openfpga_ctx, int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_io_mapping(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_bitstream_command.cpp b/openfpga/src/base/openfpga_bitstream_command.cpp index 6b7397a1c..3599bcac5 100644 --- a/openfpga/src/base/openfpga_bitstream_command.cpp +++ b/openfpga/src/base/openfpga_bitstream_command.cpp @@ -131,6 +131,36 @@ ShellCommandId add_openfpga_write_fabric_bitstream_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_io_mapping"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId opt_file = shell_cmd.add_option("file", true, "file path to output the io mapping information"); + shell_cmd.set_option_short_name(opt_file, "f"); + shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command 'fabric_bitstream' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Write the I/O mapping information to a file"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_io_mapping); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + /******************************************************************** * Top-level function to add all the commands related to FPGA-Bitstream *******************************************************************/ @@ -172,6 +202,14 @@ void add_openfpga_bitstream_commands(openfpga::Shell& shell) { std::vector cmd_dependency_write_fabric_bitstream; cmd_dependency_write_fabric_bitstream.push_back(shell_cmd_build_fabric_bitstream_id); add_openfpga_write_fabric_bitstream_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_write_fabric_bitstream); + + /******************************** + * Command 'write_io_mapping' + */ + /* The 'write_io_mapping' command should NOT be executed before 'build_fabric' */ + std::vector cmd_dependency_write_io_mapping; + cmd_dependency_write_io_mapping.push_back(shell_cmd_build_fabric_id); + add_openfpga_write_io_mapping_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_write_io_mapping); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp b/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp new file mode 100644 index 000000000..76fbda9a2 --- /dev/null +++ b/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp @@ -0,0 +1,144 @@ +/******************************************************************** + * This file includes functions that build io mapping information + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from archopenfpga library */ +#include "openfpga_naming.h" + +#include "module_manager_utils.h" +#include "build_io_mapping_info.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function + * - builds the net-to-I/O mapping + * - identifies each I/O directionality + * - return a database containing the above information + * + * TODO: This function duplicates codes from + * function: print_verilog_testbench_connect_fpga_ios() in + * source file: verilog_testbench_utils.cpp + * Should consider how to merge the codes and share same builder function + *******************************************************************/ +IoMap build_fpga_io_mapping_info(const ModuleManager& module_manager, + const ModuleId& top_module, + const AtomContext& atom_ctx, + const PlacementContext& place_ctx, + const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& io_input_port_name_postfix, + const std::string& io_output_port_name_postfix) { + IoMap io_map; + + /* Only mappable i/o ports can be considered */ + std::vector module_io_ports; + for (const ModuleManager::e_module_port_type& module_io_port_type : MODULE_IO_PORT_TYPES) { + for (const ModulePortId& gpio_port_id : module_manager.module_port_ids_by_type(top_module, module_io_port_type)) { + /* Only care mappable I/O */ + if (false == module_manager.port_is_mappable_io(top_module, gpio_port_id)) { + continue; + } + module_io_ports.push_back(gpio_port_id); + } + } + + /* Type mapping between VPR block and Module port */ + std::map atom_block_type_to_module_port_type; + atom_block_type_to_module_port_type[AtomBlockType::INPAD] = ModuleManager::MODULE_GPIN_PORT; + atom_block_type_to_module_port_type[AtomBlockType::OUTPAD] = ModuleManager::MODULE_GPOUT_PORT; + + /* Type mapping between VPR block and io mapping direction */ + std::map atom_block_type_to_io_map_direction; + atom_block_type_to_io_map_direction[AtomBlockType::INPAD] = IoMap::IO_MAP_DIR_INPUT; + atom_block_type_to_io_map_direction[AtomBlockType::OUTPAD] = IoMap::IO_MAP_DIR_OUTPUT; + + for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) { + /* Bypass non-I/O atom blocks ! */ + if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) + && (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) { + continue; + } + + /* If there is a GPIO port, use it directly + * Otherwise, should find a GPIN for INPAD + * or should find a GPOUT for OUTPAD + */ + std::pair mapped_module_io_info = std::make_pair(ModulePortId::INVALID(), -1); + for (const ModulePortId& module_io_port_id : module_io_ports) { + const BasicPort& module_io_port = module_manager.module_port(top_module, module_io_port_id); + + /* Find the index of the mapped GPIO in top-level FPGA fabric */ + size_t temp_io_index = io_location_map.io_index(place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x, + place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y, + place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.z, + module_io_port.get_name()); + + /* Bypass invalid index (not mapped to this GPIO port) */ + if (size_t(-1) == temp_io_index) { + continue; + } + + /* If the port is an GPIO port, just use it */ + if (ModuleManager::MODULE_GPIO_PORT == module_manager.port_type(top_module, module_io_port_id)) { + mapped_module_io_info = std::make_pair(module_io_port_id, temp_io_index); + break; + } + + /* If this is an INPAD, we can use an GPIN port (if available) */ + if (atom_block_type_to_module_port_type[atom_ctx.nlist.block_type(atom_blk)] == module_manager.port_type(top_module, module_io_port_id)) { + mapped_module_io_info = std::make_pair(module_io_port_id, temp_io_index); + break; + } + } + + /* We must find a valid one */ + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, mapped_module_io_info.first)); + VTR_ASSERT(size_t(-1) != mapped_module_io_info.second); + + /* Ensure that IO index is in range */ + BasicPort module_mapped_io_port = module_manager.module_port(top_module, mapped_module_io_info.first); + size_t io_index = mapped_module_io_info.second; + + /* Set the port pin index */ + VTR_ASSERT(io_index < module_mapped_io_port.get_width()); + module_mapped_io_port.set_width(io_index, io_index); + + /* The block may be renamed as it contains special characters which violate Verilog syntax */ + std::string block_name = atom_ctx.nlist.block_name(atom_blk); + if (true == netlist_annotation.is_block_renamed(atom_blk)) { + block_name = netlist_annotation.block_name(atom_blk); + } + + /* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1 + * In addition, the input and output ports may have different postfix in naming + * due to verification context! Here, we give full customization on naming + */ + BasicPort benchmark_io_port; + if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { + benchmark_io_port.set_name(std::string(block_name + io_input_port_name_postfix)); + benchmark_io_port.set_width(1); + } else { + VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)); + benchmark_io_port.set_name(std::string(block_name + io_output_port_name_postfix)); + benchmark_io_port.set_width(1); + } + + io_map.create_io_mapping(module_mapped_io_port, + benchmark_io_port, + atom_block_type_to_io_map_direction[atom_ctx.nlist.block_type(atom_blk)]); + } + + return io_map; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/build_io_mapping_info.h b/openfpga/src/fpga_bitstream/build_io_mapping_info.h new file mode 100644 index 000000000..615fafdd8 --- /dev/null +++ b/openfpga/src/fpga_bitstream/build_io_mapping_info.h @@ -0,0 +1,33 @@ +#ifndef BUILD_IO_MAPPING_INFO_H +#define BUILD_IO_MAPPING_INFO_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "vpr_context.h" +#include "io_location_map.h" +#include "io_map.h" +#include "vpr_netlist_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +IoMap build_fpga_io_mapping_info(const ModuleManager& module_manager, + const ModuleId& top_module, + const AtomContext& atom_ctx, + const PlacementContext& place_ctx, + const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& io_input_port_name_postfix, + const std::string& io_output_port_name_postfix); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp new file mode 100644 index 000000000..b9c63ddc2 --- /dev/null +++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp @@ -0,0 +1,145 @@ +/******************************************************************** + * This file includes functions that output io mapping information + * to files in XML format + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Headers from archopenfpga library */ +#include "openfpga_naming.h" + +#include "openfpga_version.h" + +#include "build_io_mapping_info.h" +#include "write_xml_io_mapping.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function write header information to an I/O mapping file + *******************************************************************/ +static +void write_io_mapping_xml_file_head(std::fstream& fp) { + valid_file_stream(fp); + + auto end = std::chrono::system_clock::now(); + std::time_t end_time = std::chrono::system_clock::to_time_t(end); + + fp << "" << std::endl; + fp << std::endl; +} + +/******************************************************************** + * Write an io mapping pair to an XML file + * + * Return: + * - 0 if succeed + * - 1 if critical errors occured + *******************************************************************/ +static +int write_io_mapping_pair_to_xml_file(std::fstream& fp, + const IoMap& io_map, + const IoMapId& io_map_id, + int xml_hierarchy_depth) { + if (false == valid_file_stream(fp)) { + return 1; + } + + write_tab_to_file(fp, xml_hierarchy_depth); + + BasicPort io_port = io_map.io_port(io_map_id); + fp << "\n"; + + return 0; +} + +/******************************************************************** + * Write the io mapping information to an XML file + * Notes: + * - This file is designed for users to learn + * - what nets are mapped to each I/O is mapped, io[0] -> netA + * - what directionality is applied to each I/O, io[0] -> input + * + * Return: + * - 0 if succeed + * - 1 if critical errors occured + *******************************************************************/ +int write_io_mapping_to_xml_file(const IoMap& io_map, + const std::string& fname, + const bool& verbose) { + /* Ensure that we have a valid file name */ + if (true == fname.empty()) { + VTR_LOG_ERROR("Received empty file name to output io_mapping!\n\tPlease specify a valid file name.\n"); + return 1; + } + + std::string timer_message = std::string("Write I/O mapping 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); + + /* Write XML head */ + write_io_mapping_xml_file_head(fp); + + int xml_hierarchy_depth = 0; + fp << "\n"; + + /* Output fabric bitstream to the file */ + int status = 0; + int io_map_cnt = 0; + for (const auto& io_map_id : io_map.io_map()) { + status = write_io_mapping_pair_to_xml_file(fp, + io_map, io_map_id, + xml_hierarchy_depth + 1); + io_map_cnt++; + if (1 == status) { + break; + } + } + + /* Print an end to the file here */ + fp << "\n"; + + VTR_LOGV(verbose, + "Outputted %d I/O mapping to file '%s'\n", + io_map, + fname.c_str()); + + /* Close file handler */ + fp.close(); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.h b/openfpga/src/fpga_bitstream/write_xml_io_mapping.h new file mode 100644 index 000000000..330106141 --- /dev/null +++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.h @@ -0,0 +1,25 @@ +#ifndef WRITE_XML_IO_MAPPING_H +#define WRITE_XML_IO_MAPPING_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "vpr_context.h" +#include "io_map.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int write_io_mapping_to_xml_file(const IoMap& io_map, + const std::string& fname, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif From c5d36757c6bfcfb53ba03984d3caf6f077b6346b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:39:57 -0600 Subject: [PATCH 061/164] [Tool] Fix typo in io mapping writing --- openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp index b9c63ddc2..61f977e7e 100644 --- a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp @@ -66,13 +66,13 @@ int write_io_mapping_pair_to_xml_file(std::fstream& fp, fp << "name=\"" << io_port.get_name().c_str() << "[" << io_port.get_lsb() << ":" << io_port.get_msb() << "]" << "\""; VTR_ASSERT(1 == io_map.io_net(io_map_id).get_width()); - fp << "net=\"" << io_map.io_net(io_map_id).get_name().c_str() << "\""; + fp << " net=\"" << io_map.io_net(io_map_id).get_name().c_str() << "\""; if (io_map.is_io_input(io_map_id)) { - fp << "dir=\"" << "input" << "\""; + fp << " dir=\"" << "input" << "\""; } else { VTR_ASSERT_SAFE(io_map.is_io_output(io_map_id)); - fp << "dir=\"" << "output" << "\""; + fp << " dir=\"" << "output" << "\""; } fp << "/>\n"; From f9fd444b8694c897e96b89bb678866a6b8a312c7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:40:26 -0600 Subject: [PATCH 062/164] [Script] Add an write I/O mapping example script for openfpga shell --- .../write_io_mapping_example_script.openfpga | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga new file mode 100644 index 000000000..62735f8bd --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga @@ -0,0 +1,38 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --absorb_buffer_luts off + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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 +# - Enabled frame view creation to save runtime and memory +# Note that this is turned on when bitstream generation +# is the ONLY purpose of the flow!!! +build_fabric --compress_routing --frame_view #--verbose + +# Write I/O net mapping information +write_io_mapping --file io_mapping.xml --verbose + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory From b8ced5377f3d6bb03070c8af95a632dcabe679ce Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:41:15 -0600 Subject: [PATCH 063/164] [Test] Add a test case for i/o mapping writer --- .../write_io_mapping/config/task.conf | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf diff --git a/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf new file mode 100644 index 000000000..a61ec038b --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf @@ -0,0 +1,32 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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 = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SYNTHESIS_PARAM] +bench0_top = and2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] From 6cb4d7d720182fa6fb4b1340bb725abc22664a76 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:41:38 -0600 Subject: [PATCH 064/164] [Test] Add the new test to regressiont test --- .../regression_test_scripts/fpga_bitstream_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) 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 db692c002..1f1eac786 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -25,3 +25,6 @@ run-task fpga_bitstream/repack_wire_lut --debug --show_thread_logs echo -e "Testing overloading default paths for programmable interconnect when generating bitstream"; run-task fpga_bitstream/overload_mux_default_path --debug --show_thread_logs + +echo -e "Testing outputting I/O mapping result to file"; +run-task fpga_bitstream/write_io_mapping --debug --show_thread_logs From 1bae59dc6a8bdfde8fabbe75b2547c02930cd7f4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 14:54:57 -0600 Subject: [PATCH 065/164] [Doc] Update documentation for the write_io_mapping command --- docs/source/manual/file_formats/index.rst | 2 ++ .../manual/file_formats/io_mapping_file.rst | 33 +++++++++++++++++++ .../fpga_bitstream_commands.rst | 16 +++++++++ 3 files changed, 51 insertions(+) create mode 100644 docs/source/manual/file_formats/io_mapping_file.rst diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index f02218cd3..d1a77e2d0 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -21,3 +21,5 @@ OpenFPGA widely uses XML format for interchangable files bitstream_setting fabric_key + + io_mapping_file diff --git a/docs/source/manual/file_formats/io_mapping_file.rst b/docs/source/manual/file_formats/io_mapping_file.rst new file mode 100644 index 000000000..722f477f6 --- /dev/null +++ b/docs/source/manual/file_formats/io_mapping_file.rst @@ -0,0 +1,33 @@ +.. _file_format_io_mapping_file: + +I/O Mapping File (.xml) +----------------------- + +The I/O mapping file aims to show + +- What nets have been mapped to each I/O +- What is the directionality of each mapped I/O + +An example of design constraints is shown as follows. + +.. code-block:: xml + + + + + + + +.. option:: name="" + + The pin name of the FPGA fabric which has been mapped, which should be a valid pin defined in OpenFPGA architecture description. + + .. note:: You should be find the exact pin in the top-level module of FPGA fabric if you output the Verilog netlists. + +.. option:: net="" + + The net name which is actually mapped to a pin, which should be consistent with net definition in your ``.blif`` file. + +.. option:: dir="" + + The direction of an I/O, which can be either ``input`` or ``output``. diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst index d2461b5ef..fbe10e033 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst @@ -72,3 +72,19 @@ write_fabric_bitstream .. option:: --verbose Show verbose log + +write_io_mapping +~~~~~~~~~~~~~~~~ + + Output the I/O mapping information to a file + + .. option:: --file or -f + + Specify the file name where the I/O mapping will be outputted to. + See file formats in :ref:`file_format_io_mapping_file`. + + .. option:: --verbose + + Show verbose log + + From 8728fd9561daa39968555ffc1c50e5ee827892aa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 15:06:07 -0600 Subject: [PATCH 066/164] [Tool] Typo fix to resolve clang errors --- openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp index 61f977e7e..5b3f383b1 100644 --- a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp @@ -133,7 +133,7 @@ int write_io_mapping_to_xml_file(const IoMap& io_map, VTR_LOGV(verbose, "Outputted %d I/O mapping to file '%s'\n", - io_map, + io_map_cnt, fname.c_str()); /* Close file handler */ From 1d498bb29665881f821ec9a4885a72ef520833a3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 15:26:52 -0600 Subject: [PATCH 067/164] [Benchmark] Add a scalable micro benchmark fifo --- .../micro_benchmark/fifo/rtl/fifo.v | 97 +++++++++++++++++++ .../fifo/rtl/sync_dual_port_ram.v | 35 +++++++ 2 files changed, 132 insertions(+) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v create mode 100644 openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v new file mode 100644 index 000000000..af1bc6921 --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v @@ -0,0 +1,97 @@ +// FIFO buffer implemented with synchronous dual-port block ram +// Reference: +// https://embeddedthoughts.com/2016/07/13/fifo-buffer-using-block-ram-on-a-xilinx-spartan-3-fpga/ +module fifo + #( parameter ADDRESS_WIDTH = 12, // number of words in ram + DATA_WIDTH = 8 // number of bits in word + ) + + // IO ports + ( + input wire clk, reset, + input wire read, write, + input wire [DATA_WIDTH-1:0] write_data, + output wire empty, full, + output wire [DATA_WIDTH-1:0] read_data + ); + + // internal signal declarations + reg [ADDRESS_WIDTH-1:0] write_address_reg, write_address_next, write_address_after; + reg [ADDRESS_WIDTH-1:0] read_address_reg, read_address_next, read_address_after; + reg full_reg, empty_reg, full_next, empty_next; + wire write_en; + + // write enable is asserted when write input is asserted and FIFO isn't full + assign write_en = write & ~full_reg; + + // instantiate synchronous block ram + sync_dual_port_ram #(.ADDRESS_WIDTH(ADDRESS_WIDTH), .DATA_WIDTH(DATA_WIDTH)) ram + (.clk(clk), .write_en(write_en), .write_address(write_address_reg), + .read_address(read_address_reg), .write_data_in(write_data), + .write_data_out(), .read_data_out(read_data)); + + // register for address pointers, full/empty status + always @(posedge clk, posedge reset) + if (reset) + begin + write_address_reg <= 0; + read_address_reg <= 0; + full_reg <= 1'b0; + empty_reg <= 1'b1; + end + else + begin + write_address_reg <= write_address_next; + read_address_reg <= read_address_next; + full_reg <= full_next; + empty_reg <= empty_next; + end + + // next-state logic for address index values after read/write operations + always @* + begin + write_address_after = write_address_reg + 1; + read_address_after = read_address_reg + 1; + end + + // next-state logic for address pointers + always @* + begin + // defaults + write_address_next = write_address_reg; + read_address_next = read_address_reg; + full_next = full_reg; + empty_next = empty_reg; + + // if read input asserted and FIFO isn't empty + if(read && ~empty_reg && ~write) + begin + read_address_next = read_address_after; // read address moves forward + full_next = 1'b0; // FIFO isn't full if a read occured + + if (read_address_after == write_address_reg) // if read address caught up with write address, + empty_next = 1'b1; // FIFO is empty + end + + // if write input asserted and FIFO isn't full + else if(write && ~full_reg && ~read) + begin + write_address_next = write_address_after; // write address moves forward + empty_next = 1'b0; // FIFO isn't empty if write occured + + if (write_address_after == read_address_reg) // if write address caught up with read address + full_next = 1'b1; // FIFO is full + end + + // if write and read are asserted + else if(write && read) + begin + write_address_next = write_address_after; // write address moves forward + read_address_next = read_address_after; // read address moves forward + end + end + + // assign full/empty status to output ports + assign full = full_reg; + assign empty = empty_reg; +endmodule \ No newline at end of file diff --git a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v new file mode 100644 index 000000000..5914129e4 --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v @@ -0,0 +1,35 @@ +// Synchronous dual-port block ram +// Reference: +// https://embeddedthoughts.com/2016/07/13/fifo-buffer-using-block-ram-on-a-xilinx-spartan-3-fpga/ +module sync_dual_port_ram + #( parameter ADDRESS_WIDTH = 12, // number of words in ram + DATA_WIDTH = 8 // number of bits in word + ) + + // IO ports + ( + input wire clk, // clk for synchronous read/write + input wire write_en, // signal to enable synchronous write + input wire [ADDRESS_WIDTH-1:0] read_address, write_address, // inputs for dual port addresses + input wire [DATA_WIDTH-1:0] write_data_in, // input for data to write to ram + output wire [DATA_WIDTH-1:0] read_data_out, write_data_out // outputs for dual data ports + ); + + // internal signal declarations + reg [DATA_WIDTH-1:0] ram [2**ADDRESS_WIDTH-1:0]; // ADDRESS_WIDTH x DATA_WIDTH RAM declaration + reg [ADDRESS_WIDTH-1:0] read_address_reg, write_address_reg; // dual port address declarations + + // synchronous write and address update + always @(posedge clk) + begin + if (write_en) // if write enabled + ram[write_address] <= write_data_in; // write data to ram and write_address + + read_address_reg <= read_address; // store read_address to reg + write_address_reg <= write_address; // store write_address to reg + end + + // assignments for two data out ports + assign read_data_out = ram[read_address_reg]; + assign write_data_out = ram[write_address_reg]; +endmodule \ No newline at end of file From 0f8aaae2bcc95dabc62e242a18b055929f8ceb57 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 19:54:34 -0600 Subject: [PATCH 068/164] [Arch] Patch architecture using 16kbit dual port RAM --- ...c_N10_adder_chain_mem16K_40nm_openfpga.xml | 20 ++-- .../k6_frac_N10_adder_chain_mem16K_40nm.xml | 86 +++++++++-------- ...c_N10_tileable_adder_chain_mem16K_40nm.xml | 86 +++++++++-------- ..._tileable_adder_chain_wide_mem16K_40nm.xml | 93 ++++++++++--------- 4 files changed, 156 insertions(+), 129 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml index 5259e5e03..19064e7b6 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml @@ -194,17 +194,17 @@ - + - - - + + + - - + + @@ -222,6 +222,12 @@ + + + + + + @@ -267,7 +273,7 @@ - + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_adder_chain_mem16K_40nm.xml index 796369e64..e60a26611 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_adder_chain_mem16K_40nm.xml @@ -118,14 +118,14 @@ - + - + @@ -135,7 +135,7 @@ - + @@ -180,15 +180,21 @@ - - - + + + - + - + + memory.clk + + memory.wen memory.waddr[0:3] memory.raddr[0:3] memory.data_in[0:2] memory.data_out[0:2] + memory.ren memory.waddr[4:7] memory.raddr[4:7] memory.data_in[3:5] memory.data_out[3:5] + memory.waddr[8:11] memory.raddr[8:11] memory.data_in[6:7] memory.data_out[6:7] + @@ -682,57 +688,57 @@ - - - + + + - + - - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml index 53ad0929f..5040107e1 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml @@ -118,14 +118,14 @@ - + - + @@ -135,7 +135,7 @@ - + @@ -180,15 +180,21 @@ - - - + + + - + - + + memory.clk + + memory.wen memory.waddr[0:3] memory.raddr[0:3] memory.data_in[0:2] memory.data_out[0:2] + memory.ren memory.waddr[4:7] memory.raddr[4:7] memory.data_in[3:5] memory.data_out[3:5] + memory.waddr[8:11] memory.raddr[8:11] memory.data_in[6:7] memory.data_out[6:7] + @@ -682,57 +688,57 @@ - - - + + + - + - - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml index 476602970..142d286b4 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml @@ -118,14 +118,14 @@ - + - + @@ -135,7 +135,7 @@ - + @@ -176,19 +176,28 @@ clb.cout clb.O[19:10] clb.I[39:20] - + - - - + + + - + - + + memory.clk memory.waddr[0:1] memory.raddr[0:1] memory.data_in[0:0] memory.data_out[0:0] + memory.waddr[2:3] memory.raddr[2:3] memory.data_in[1:1] memory.data_out[1:1] + memory.waddr[4:5] memory.raddr[4:5] memory.data_in[2:2] memory.data_out[2:2] + memory.waddr[6:7] memory.raddr[6:7] memory.data_in[3:3] memory.data_out[3:3] + memory.waddr[8:8] memory.raddr[8:8] memory.data_in[4:4] memory.data_out[4:4] + memory.waddr[9:9] memory.raddr[9:9] memory.data_in[5:5] memory.data_out[5:5] + memory.wen memory.waddr[10:10] memory.raddr[10:10] memory.data_in[6:6] memory.data_out[6:6] + memory.ren memory.waddr[11:11] memory.raddr[11:11] memory.data_in[7:7] memory.data_out[7:7] + @@ -204,7 +213,7 @@ - + @@ -682,57 +691,57 @@ - - - + + + - + - - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + From e67095edd2282bb40d73c1a54fb680069041592c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 19:55:16 -0600 Subject: [PATCH 069/164] [HDL] Add 16k-bit dual port ram verilog --- .../verilog/dpram_2048x8.v | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v diff --git a/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v b/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v new file mode 100644 index 000000000..53561c1e4 --- /dev/null +++ b/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v @@ -0,0 +1,62 @@ +//----------------------------------------------------- +// Design Name : dpram_2048x8 +// File Name : dpram_2048x8.v +// Function : Dual port RAM 2048 x 8bit +// Coder : Xifan Tang +//----------------------------------------------------- + +module dpram_2048x8 ( + input clk, + input wen, + input ren, + input[0:11] waddr, + input[0:11] raddr, + input[0:7] data_in, + output[0:7] data_out ); + + dual_port_sram memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (data_in), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .data_out (data_out) ); + +endmodule + +//----------------------------------------------------- +// Design Name : dpram_2048x8_core +// File Name : dpram_2048x8.v +// Function : Core module of dual port RAM 2048 addresses x 8 bit +// Coder : Xifan tang +//----------------------------------------------------- +module dual_port_sram ( + input wclk, + input wen, + input[0:11] waddr, + input[0:7] data_in, + input rclk, + input ren, + input[0:11] raddr, + output[0:7] data_out ); + + reg[0:7] ram[0:2047]; + reg[0:7] internal; + + assign data_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule From 2802b0895cf1ebaadcffe8e49f8d59a11be06d01 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 19:55:46 -0600 Subject: [PATCH 070/164] [HDL] Add yosys technology library for reworked architecture using 16k-bit DPRAM --- ..._tileable_adder_chain_mem16K_40nm_bram.txt | 18 ++++++ ...ileable_adder_chain_mem16K_40nm_bram_map.v | 21 +++++++ ...ileable_adder_chain_mem16K_40nm_cell_sim.v | 58 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt create mode 100644 openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v create mode 100644 openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt new file mode 100644 index 000000000..8cbf62755 --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt @@ -0,0 +1,18 @@ +bram $__MY_DPRAM_2048x8 + init 0 + abits 12 + dbits 8 + groups 2 + ports 1 1 + wrmode 1 0 + enable 1 1 + transp 0 0 + clocks 1 1 + clkpol 1 1 +endbram + +match $__MY_DPRAM_2048x8 + min efficiency 0 + make_transp +endmatch + diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v new file mode 100644 index 000000000..cc4881d77 --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v @@ -0,0 +1,21 @@ +module $__MY_DPRAM_2048x8 ( + output [0:7] B1DATA, + input CLK1, + input [0:11] B1ADDR, + input [0:11] A1ADDR, + input [0:7] A1DATA, + input A1EN, + input B1EN ); + + generate + dpram_2048x8 #() _TECHMAP_REPLACE_ ( + .clk (CLK1), + .wen (A1EN), + .waddr (A1ADDR), + .data_in (A1DATA), + .ren (B1EN), + .raddr (B1ADDR), + .data_out (B1DATA) ); + endgenerate + +endmodule diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v new file mode 100644 index 000000000..5e772bf8d --- /dev/null +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v @@ -0,0 +1,58 @@ +//----------------------------- +// Dual-port RAM 2048x8 bit (8Kbit) +// Core logic +//----------------------------- +module dpram_2048x8_core ( + input wclk, + input wen, + input [0:11] waddr, + input [0:7] data_in, + input rclk, + input ren, + input [0:11] raddr, + output [0:7] data_out ); + + reg [0:7] ram[0:2047]; + reg [0:7] internal; + + assign data_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule + +//----------------------------- +// Dual-port RAM 2048x8 bit (8Kbit) wrapper +// where the read clock and write clock +// are combined to a unified clock +//----------------------------- +module dpram_2048x8 ( + input clk, + input wen, + input ren, + input [0:11] waddr, + input [0:11] raddr, + input [0:7] data_in, + output [0:7] data_out ); + + dpram_2048x8_core memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (data_in), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .data_out (data_out) ); + +endmodule From fdfbdc46137afe8a5526104912ac173af0347560 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 20:05:04 -0600 Subject: [PATCH 071/164] [Test] Update task configuration files to use dedicated yosys script --- .../bram/dpram16k/config/task.conf | 20 +++++++++------- .../bram/wide_dpram16k/config/task.conf | 24 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf index b7d8715ec..3aa8178f1 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf @@ -9,29 +9,33 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 -fpga_flow=vpr_blif +fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.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_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +# Yosys script parameters +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v +yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt +yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +# VPR parameter openfpga_vpr_device_layout=3x2 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/*.v [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 -bench0_chan_width = 300 +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys +bench0_top = fifo [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= diff --git a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf index d8dc5d26d..96175116c 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf @@ -9,29 +9,33 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 -fpga_flow=vpr_blif +fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.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_vpr_device_layout=4x4 +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +# Yosys script parameters +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v +yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt +yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +# VPR parameter +openfpga_vpr_device_layout=4x2 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/*.v [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 -bench0_chan_width = 300 +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys +bench0_top = fifo [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From dd467808651c2bb09b2704b80fb527a3351f7be5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 21:44:27 -0600 Subject: [PATCH 072/164] [Script] Update yosys script using BRAMs --- openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys b/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys index d680cf71c..6a8bf372b 100644 --- a/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys +++ b/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys @@ -27,9 +27,6 @@ opt_clean deminout opt -######################### -# Run coarse synthesis -######################### opt_expr opt_clean check @@ -38,8 +35,13 @@ wreduce -keepdc peepopt pmuxtree opt_clean + +######################### +# Run coarse synthesis +######################### # Extract arithmetic functions alumacc +share opt fsm # Run a quick follow-up optimization to sweep out unused nets/signals From 5a85ec9fa0ee4f0e3fbc6714ac40aadf1f309467 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 22:09:10 -0600 Subject: [PATCH 073/164] [Benchmark] Reduce default size of FIFO to limit the number of LUTs and BRAMs to be synthesised --- openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v | 6 +++--- .../micro_benchmark/fifo/rtl/sync_dual_port_ram.v | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v index af1bc6921..65dc95d11 100644 --- a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v +++ b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/fifo.v @@ -2,8 +2,8 @@ // Reference: // https://embeddedthoughts.com/2016/07/13/fifo-buffer-using-block-ram-on-a-xilinx-spartan-3-fpga/ module fifo - #( parameter ADDRESS_WIDTH = 12, // number of words in ram - DATA_WIDTH = 8 // number of bits in word + #( parameter ADDRESS_WIDTH = 4, // number of words in ram + DATA_WIDTH = 4 // number of bits in word ) // IO ports @@ -94,4 +94,4 @@ module fifo // assign full/empty status to output ports assign full = full_reg; assign empty = empty_reg; -endmodule \ No newline at end of file +endmodule diff --git a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v index 5914129e4..bd1de2b4b 100644 --- a/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v +++ b/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/sync_dual_port_ram.v @@ -2,8 +2,8 @@ // Reference: // https://embeddedthoughts.com/2016/07/13/fifo-buffer-using-block-ram-on-a-xilinx-spartan-3-fpga/ module sync_dual_port_ram - #( parameter ADDRESS_WIDTH = 12, // number of words in ram - DATA_WIDTH = 8 // number of bits in word + #( parameter ADDRESS_WIDTH = 4, // number of words in ram + DATA_WIDTH = 4 // number of bits in word ) // IO ports @@ -32,4 +32,4 @@ module sync_dual_port_ram // assignments for two data out ports assign read_data_out = ram[read_address_reg]; assign write_data_out = ram[write_address_reg]; -endmodule \ No newline at end of file +endmodule From 7e2368158e3364aea2e49c747fa1b80003efe038 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 22:12:30 -0600 Subject: [PATCH 074/164] [Benchmark] move benchmarks to microbenchmark category --- .../pipelined_8bit_adder/pipelined_8bit_adder.act | 0 .../pipelined_8bit_adder/pipelined_8bit_adder.blif | 0 .../pipelined_8bit_adder/pipelined_8bit_adder.v | 0 .../pipelined_8bit_adder_formal_random_top_tb.v | 0 .../{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.act | 0 .../{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.blif | 0 .../{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.v | 0 .../{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.act | 0 .../{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.blif | 0 .../{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.v | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename openfpga_flow/benchmarks/{ => micro_benchmark}/pipelined_8bit_adder/pipelined_8bit_adder.act (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/pipelined_8bit_adder/pipelined_8bit_adder.blif (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/pipelined_8bit_adder/pipelined_8bit_adder.v (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.act (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.blif (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k4_N4/K4N4_test_modes.v (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.act (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.blif (100%) rename openfpga_flow/benchmarks/{ => micro_benchmark}/test_modes/k6_N10/K6N10_test_modes.v (100%) diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act b/openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.act similarity index 100% rename from openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act rename to openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.act diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif b/openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.blif similarity index 100% rename from openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif rename to openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.blif diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v b/openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.v similarity index 100% rename from openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v rename to openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder.v diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v b/openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v similarity index 100% rename from openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v rename to openfpga_flow/benchmarks/micro_benchmark/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v diff --git a/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.act b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.act similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.act rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.act diff --git a/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.blif b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.blif similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.blif rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.blif diff --git a/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.v b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.v similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.v rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k4_N4/K4N4_test_modes.v diff --git a/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.act b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.act similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.act rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.act diff --git a/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.blif b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.blif similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.blif rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.blif diff --git a/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.v b/openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.v similarity index 100% rename from openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.v rename to openfpga_flow/benchmarks/micro_benchmark/test_modes/k6_N10/K6N10_test_modes.v From 3c1c33bf1e1e1674eabf956e181d8a737b4b9e9f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 22:51:43 -0600 Subject: [PATCH 075/164] [Benchmark] Add a microbenchmark just fit 16k dual port ram --- .../dual_port_ram_16k/dual_port_ram_16k.v | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v new file mode 100644 index 000000000..a27801d9e --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v @@ -0,0 +1,58 @@ +//----------------------------------------------------- +// Design Name : dual_port_ram_16k +// File Name : dpram.v +// Function : Dual port RAM 2048x8bit +// Coder : Aurelien +//----------------------------------------------------- + +module dual_port_ram_16k ( + input clk, + input wen, + input ren, + input [11:0] waddr, + input [11:0] raddr, + input [7:0] din, + output [7:0] dout +); + + dual_port_sram_16kb memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (din), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .data_out (dout) ); + +endmodule + +module dual_port_sram_16kb ( + input wclk, + input wen, + input [11:0] waddr, + input [7:0] data_in, + input rclk, + input ren, + input [11:0] raddr, + output [7:0] data_out +); + + reg [7:0] ram[2047:0]; + reg [7:0] internal; + + assign data_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule From 7d059f74073cfe17866377bd24ad6bf78566e478 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 23:33:20 -0600 Subject: [PATCH 076/164] [Benchmark] Bug fix in dual port ram 16k benchmark --- .../dual_port_ram_16k/dual_port_ram_16k.v | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v index a27801d9e..60f165ab0 100644 --- a/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v +++ b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v @@ -1,16 +1,16 @@ //----------------------------------------------------- // Design Name : dual_port_ram_16k -// File Name : dpram.v +// File Name : dual_port_ram_16k.v // Function : Dual port RAM 2048x8bit -// Coder : Aurelien +// Coder : Xifan Tang //----------------------------------------------------- module dual_port_ram_16k ( input clk, input wen, input ren, - input [11:0] waddr, - input [11:0] raddr, + input [10:0] waddr, + input [10:0] raddr, input [7:0] din, output [7:0] dout ); @@ -30,11 +30,11 @@ endmodule module dual_port_sram_16kb ( input wclk, input wen, - input [11:0] waddr, + input [10:0] waddr, input [7:0] data_in, input rclk, input ren, - input [11:0] raddr, + input [10:0] raddr, output [7:0] data_out ); From 0bec4b3f321de75286376a04fa86071805f7d00f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 23:34:42 -0600 Subject: [PATCH 077/164] [Test] Update task configuration to use proper openfpgashell script --- .../tasks/fpga_verilog/bram/dpram16k/config/task.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf index 3aa8178f1..c340d7f9f 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf @@ -16,7 +16,7 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.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/fixed_sim_openfpga.xml # Yosys script parameters @@ -30,12 +30,12 @@ openfpga_vpr_device_layout=3x2 arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/*.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v [SYNTHESIS_PARAM] bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys -bench0_top = fifo +bench0_top = dual_port_ram_16k [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 834657f2da5b9584a71d2aedcc007d95f860add6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 23:41:14 -0600 Subject: [PATCH 078/164] [Arch] Patch arch using 16kbit DPRAM due to wrong addr sizes --- ...c_N10_adder_chain_mem16K_40nm_openfpga.xml | 6 +++--- ...c_N10_tileable_adder_chain_mem16K_40nm.xml | 19 +++++++++++-------- ..._tileable_adder_chain_wide_mem16K_40nm.xml | 19 +++++++++++-------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml index 19064e7b6..124fed331 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml @@ -147,7 +147,7 @@ - + @@ -198,8 +198,8 @@ - - + + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml index 5040107e1..93423a81d 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml @@ -164,6 +164,7 @@ + @@ -180,20 +181,22 @@ - - + + - + + + memory.clk memory.wen memory.waddr[0:3] memory.raddr[0:3] memory.data_in[0:2] memory.data_out[0:2] memory.ren memory.waddr[4:7] memory.raddr[4:7] memory.data_in[3:5] memory.data_out[3:5] - memory.waddr[8:11] memory.raddr[8:11] memory.data_in[6:7] memory.data_out[6:7] + memory.waddr[8:10] memory.raddr[8:10] memory.data_in[6:7] memory.data_out[6:7] @@ -688,8 +691,8 @@ - - + + @@ -701,8 +704,8 @@ --> - - + + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml index 142d286b4..c6f578260 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml @@ -164,6 +164,7 @@ + @@ -180,14 +181,16 @@ - - + + - + + + memory.clk memory.waddr[0:1] memory.raddr[0:1] memory.data_in[0:0] memory.data_out[0:0] memory.waddr[2:3] memory.raddr[2:3] memory.data_in[1:1] memory.data_out[1:1] @@ -196,7 +199,7 @@ memory.waddr[8:8] memory.raddr[8:8] memory.data_in[4:4] memory.data_out[4:4] memory.waddr[9:9] memory.raddr[9:9] memory.data_in[5:5] memory.data_out[5:5] memory.wen memory.waddr[10:10] memory.raddr[10:10] memory.data_in[6:6] memory.data_out[6:6] - memory.ren memory.waddr[11:11] memory.raddr[11:11] memory.data_in[7:7] memory.data_out[7:7] + memory.ren memory.data_in[7:7] memory.data_out[7:7] @@ -691,8 +694,8 @@ - - + + @@ -704,8 +707,8 @@ --> - - + + From 411af109337f6f9cbfda86b790e6843cfb12666b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 23:41:47 -0600 Subject: [PATCH 079/164] [Script] Patch yosys script for 16kbit dual port RAM --- .../k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt | 2 +- ...6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v | 4 ++-- ...6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt index 8cbf62755..327dbe9bf 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt @@ -1,6 +1,6 @@ bram $__MY_DPRAM_2048x8 init 0 - abits 12 + abits 11 dbits 8 groups 2 ports 1 1 diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v index cc4881d77..35ec892dc 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v @@ -1,8 +1,8 @@ module $__MY_DPRAM_2048x8 ( output [0:7] B1DATA, input CLK1, - input [0:11] B1ADDR, - input [0:11] A1ADDR, + input [0:10] B1ADDR, + input [0:10] A1ADDR, input [0:7] A1DATA, input A1EN, input B1EN ); diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v index 5e772bf8d..9fc6a1aa1 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v @@ -5,11 +5,11 @@ module dpram_2048x8_core ( input wclk, input wen, - input [0:11] waddr, + input [0:10] waddr, input [0:7] data_in, input rclk, input ren, - input [0:11] raddr, + input [0:10] raddr, output [0:7] data_out ); reg [0:7] ram[0:2047]; @@ -40,8 +40,8 @@ module dpram_2048x8 ( input clk, input wen, input ren, - input [0:11] waddr, - input [0:11] raddr, + input [0:10] waddr, + input [0:10] raddr, input [0:7] data_in, output [0:7] data_out ); From 63309ba72ba6c8f7e94fa2b0e0b1b9cc36fc8a20 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 27 Apr 2021 23:42:31 -0600 Subject: [PATCH 080/164] [HDL] Patch dpram cell --- .../openfpga_cell_library/verilog/dpram_2048x8.v | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v b/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v index 53561c1e4..fa56d30cb 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v +++ b/openfpga_flow/openfpga_cell_library/verilog/dpram_2048x8.v @@ -9,8 +9,8 @@ module dpram_2048x8 ( input clk, input wen, input ren, - input[0:11] waddr, - input[0:11] raddr, + input[0:10] waddr, + input[0:10] raddr, input[0:7] data_in, output[0:7] data_out ); @@ -35,11 +35,11 @@ endmodule module dual_port_sram ( input wclk, input wen, - input[0:11] waddr, + input[0:10] waddr, input[0:7] data_in, input rclk, input ren, - input[0:11] raddr, + input[0:10] raddr, output[0:7] data_out ); reg[0:7] ram[0:2047]; From 79b27a6329a8ed8ae395e38b9ed1d35411ed52d5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 10:29:09 -0600 Subject: [PATCH 081/164] [Arch] Patch arch using DPRAM block with wide = 2 --- .../k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml index c6f578260..da1d573d5 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml @@ -195,7 +195,7 @@ memory.clk memory.waddr[0:1] memory.raddr[0:1] memory.data_in[0:0] memory.data_out[0:0] memory.waddr[2:3] memory.raddr[2:3] memory.data_in[1:1] memory.data_out[1:1] memory.waddr[4:5] memory.raddr[4:5] memory.data_in[2:2] memory.data_out[2:2] - memory.waddr[6:7] memory.raddr[6:7] memory.data_in[3:3] memory.data_out[3:3] + memory.waddr[6:7] memory.raddr[6:7] memory.data_in[3:3] memory.data_out[3:3] memory.waddr[8:8] memory.raddr[8:8] memory.data_in[4:4] memory.data_out[4:4] memory.waddr[9:9] memory.raddr[9:9] memory.data_in[5:5] memory.data_out[5:5] memory.wen memory.waddr[10:10] memory.raddr[10:10] memory.data_in[6:6] memory.data_out[6:6] From 5c729657efdfd201c608e0be708e83cd65e2ec30 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 10:31:22 -0600 Subject: [PATCH 082/164] [Test] Bug fix in test case for DPRAM whose width = 2 --- .../tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf index 96175116c..441d6ff82 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf @@ -30,12 +30,12 @@ openfpga_vpr_device_layout=4x2 arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/fifo/rtl/*.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v [SYNTHESIS_PARAM] bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys -bench0_top = fifo +bench0_top = dual_port_ram_16k [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From be98775ae534810b812ec4ea0d729484b4e23d82 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 10:45:10 -0600 Subject: [PATCH 083/164] [Arch] Reduce the size of DPRAM in example architecture to accelerate testing --- ...c_N10_adder_chain_mem16K_40nm_openfpga.xml | 279 ------------------ ...c_N10_tileable_adder_chain_mem1K_40nm.xml} | 64 ++-- ..._tileable_adder_chain_wide_mem1K_40nm.xml} | 72 ++--- 3 files changed, 68 insertions(+), 347 deletions(-) delete mode 100644 openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml rename openfpga_flow/vpr_arch/{k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml => k6_frac_N10_tileable_adder_chain_mem1K_40nm.xml} (95%) rename openfpga_flow/vpr_arch/{k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml => k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml} (94%) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml deleted file mode 100644 index 124fed331..000000000 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10e-12 - - - 10e-12 - - - - - - - - - 10e-12 - - - 10e-12 - - - - - - - - - 10e-12 - - - 10e-12 - - - - - - - - - - - - 10e-12 5e-12 - - - 10e-12 5e-12 - - - - - - - - - - - - - 10e-12 5e-12 5e-12 - - - 10e-12 5e-12 5e-12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem1K_40nm.xml similarity index 95% rename from openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml rename to openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem1K_40nm.xml index 93423a81d..170e65818 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem1K_40nm.xml @@ -118,7 +118,7 @@ - + @@ -181,8 +181,8 @@ - - + + @@ -194,9 +194,9 @@ memory.clk - memory.wen memory.waddr[0:3] memory.raddr[0:3] memory.data_in[0:2] memory.data_out[0:2] - memory.ren memory.waddr[4:7] memory.raddr[4:7] memory.data_in[3:5] memory.data_out[3:5] - memory.waddr[8:10] memory.raddr[8:10] memory.data_in[6:7] memory.data_out[6:7] + memory.wen memory.waddr[0:2] memory.raddr[0:2] memory.data_in[0:2] memory.data_out[0:2] + memory.ren memory.waddr[3:5] memory.raddr[3:5] memory.data_in[3:5] memory.data_out[3:5] + memory.waddr[6:6] memory.raddr[6:6] memory.data_in[6:7] memory.data_out[6:7] @@ -691,57 +691,57 @@ - - + + - - - - - + + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml similarity index 94% rename from openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml rename to openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml index da1d573d5..0b7820139 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml @@ -118,7 +118,7 @@ - + @@ -181,8 +181,8 @@ - - + + @@ -192,13 +192,13 @@ - memory.clk memory.waddr[0:1] memory.raddr[0:1] memory.data_in[0:0] memory.data_out[0:0] - memory.waddr[2:3] memory.raddr[2:3] memory.data_in[1:1] memory.data_out[1:1] - memory.waddr[4:5] memory.raddr[4:5] memory.data_in[2:2] memory.data_out[2:2] - memory.waddr[6:7] memory.raddr[6:7] memory.data_in[3:3] memory.data_out[3:3] - memory.waddr[8:8] memory.raddr[8:8] memory.data_in[4:4] memory.data_out[4:4] - memory.waddr[9:9] memory.raddr[9:9] memory.data_in[5:5] memory.data_out[5:5] - memory.wen memory.waddr[10:10] memory.raddr[10:10] memory.data_in[6:6] memory.data_out[6:6] + memory.clk memory.waddr[0:0] memory.raddr[0:0] memory.data_in[0:0] memory.data_out[0:0] + memory.waddr[1:1] memory.raddr[1:1] memory.data_in[1:1] memory.data_out[1:1] + memory.waddr[2:2] memory.raddr[2:2] memory.data_in[2:2] memory.data_out[2:2] + memory.waddr[3:3] memory.raddr[3:3] memory.data_in[3:3] memory.data_out[3:3] + memory.waddr[4:4] memory.raddr[4:4] memory.data_in[4:4] memory.data_out[4:4] + memory.waddr[5:5] memory.raddr[5:5] memory.data_in[5:5] memory.data_out[5:5] + memory.wen memory.waddr[5:5] memory.raddr[5:5] memory.data_in[6:6] memory.data_out[6:6] memory.ren memory.data_in[7:7] memory.data_out[7:7] @@ -694,57 +694,57 @@ - - + + - - - - - + + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + From ec4b60f3cc7daca3020a337f45d5db14c2428734 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 10:47:17 -0600 Subject: [PATCH 084/164] [Arch] Add example arch using 1-kbit DPRAM --- ...ac_N10_adder_chain_mem1K_40nm_openfpga.xml | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml new file mode 100644 index 000000000..6af9952f1 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 + + + 10e-12 5e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c24edbd6740df55f551d85c0fdba113f5834aecb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 10:55:59 -0600 Subject: [PATCH 085/164] [Script] Update yosys script due to arch changes in DPRAM sizes --- ..._tileable_adder_chain_mem1K_40nm_bram.txt} | 6 +++--- ...ileable_adder_chain_mem1K_40nm_bram_map.v} | 8 ++++---- ...ileable_adder_chain_mem1K_40nm_cell_sim.v} | 20 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) rename openfpga_flow/openfpga_yosys_techlib/{k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt => k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt} (73%) rename openfpga_flow/openfpga_yosys_techlib/{k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v => k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v} (72%) rename openfpga_flow/openfpga_yosys_techlib/{k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v => k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v} (76%) diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt similarity index 73% rename from openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt rename to openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt index 327dbe9bf..51617f271 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt @@ -1,6 +1,6 @@ -bram $__MY_DPRAM_2048x8 +bram $__MY_DPRAM_128x8 init 0 - abits 11 + abits 7 dbits 8 groups 2 ports 1 1 @@ -11,7 +11,7 @@ bram $__MY_DPRAM_2048x8 clkpol 1 1 endbram -match $__MY_DPRAM_2048x8 +match $__MY_DPRAM_128x8 min efficiency 0 make_transp endmatch diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v similarity index 72% rename from openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v rename to openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v index 35ec892dc..8cf462c1a 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v @@ -1,14 +1,14 @@ -module $__MY_DPRAM_2048x8 ( +module $__MY_DPRAM_128x8 ( output [0:7] B1DATA, input CLK1, - input [0:10] B1ADDR, - input [0:10] A1ADDR, + input [0:6] B1ADDR, + input [0:6] A1ADDR, input [0:7] A1DATA, input A1EN, input B1EN ); generate - dpram_2048x8 #() _TECHMAP_REPLACE_ ( + dpram_128x8 #() _TECHMAP_REPLACE_ ( .clk (CLK1), .wen (A1EN), .waddr (A1ADDR), diff --git a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v similarity index 76% rename from openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v rename to openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v index 9fc6a1aa1..9ec66e6ee 100644 --- a/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v +++ b/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v @@ -1,18 +1,18 @@ //----------------------------- -// Dual-port RAM 2048x8 bit (8Kbit) +// Dual-port RAM 128x8 bit (1Kbit) // Core logic //----------------------------- -module dpram_2048x8_core ( +module dpram_128x8_core ( input wclk, input wen, - input [0:10] waddr, + input [0:6] waddr, input [0:7] data_in, input rclk, input ren, - input [0:10] raddr, + input [0:6] raddr, output [0:7] data_out ); - reg [0:7] ram[0:2047]; + reg [0:7] ram[0:127]; reg [0:7] internal; assign data_out = internal; @@ -32,20 +32,20 @@ module dpram_2048x8_core ( endmodule //----------------------------- -// Dual-port RAM 2048x8 bit (8Kbit) wrapper +// Dual-port RAM 128x8 bit (1Kbit) wrapper // where the read clock and write clock // are combined to a unified clock //----------------------------- -module dpram_2048x8 ( +module dpram_128x8 ( input clk, input wen, input ren, - input [0:10] waddr, - input [0:10] raddr, + input [0:6] waddr, + input [0:6] raddr, input [0:7] data_in, output [0:7] data_out ); - dpram_2048x8_core memory_0 ( + dpram_128x8_core memory_0 ( .wclk (clk), .wen (wen), .waddr (waddr), From a571b063b666842332647a2ffafd6b0960148f31 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 11:26:31 -0600 Subject: [PATCH 086/164] [Benchmark] Add 1k DPRAM benchmark which can fit new arch --- .../dual_port_ram_1k/dual_port_ram_1k.v | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v new file mode 100644 index 000000000..c8bcc4e58 --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v @@ -0,0 +1,58 @@ +//----------------------------------------------------- +// Design Name : dual_port_ram_1k +// File Name : dual_port_ram_1k.v +// Function : Dual port RAM 128x8bit +// Coder : Xifan Tang +//----------------------------------------------------- + +module dual_port_ram_1k ( + input clk, + input wen, + input ren, + input [6:0] waddr, + input [6:0] raddr, + input [7:0] din, + output [7:0] dout +); + + dual_port_sram_1kb memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (din), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .data_out (dout) ); + +endmodule + +module dual_port_sram_1kb ( + input wclk, + input wen, + input [6:0] waddr, + input [7:0] data_in, + input rclk, + input ren, + input [6:0] raddr, + output [7:0] data_out +); + + reg [7:0] ram[127:0]; + reg [7:0] internal; + + assign data_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule From 117cea295d75181a7706789159b8fe71cd878ed9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 11:28:23 -0600 Subject: [PATCH 087/164] [Arch] Patch architecture to be compatible with pin names of DPRAM cell --- .../k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml index 6af9952f1..e66611fb0 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml @@ -200,10 +200,10 @@ - + - + From b72d4bd8076037599e8cada9a39995838786ccc5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 11:28:53 -0600 Subject: [PATCH 088/164] [Test] Update test case for 1kbit DPRAM architectures --- .../bram/{dpram16k => dpram1k}/config/task.conf | 14 +++++++------- .../config/task.conf | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) rename openfpga_flow/tasks/fpga_verilog/bram/{dpram16k => dpram1k}/config/task.conf (82%) rename openfpga_flow/tasks/fpga_verilog/bram/{wide_dpram16k => wide_dpram1k}/config/task.conf (82%) diff --git a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/dpram1k/config/task.conf similarity index 82% rename from openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/bram/dpram1k/config/task.conf index c340d7f9f..67ab566f4 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/dpram1k/config/task.conf @@ -17,25 +17,25 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml # Yosys script parameters -yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v -yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt -yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v +yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt +yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v # VPR parameter openfpga_vpr_device_layout=3x2 [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem16K_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_mem1K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v [SYNTHESIS_PARAM] bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys -bench0_top = dual_port_ram_16k +bench0_top = dual_port_ram_1k [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= diff --git a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram1k/config/task.conf similarity index 82% rename from openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/bram/wide_dpram1k/config/task.conf index 441d6ff82..234150aa1 100644 --- a/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram1k/config/task.conf @@ -17,25 +17,25 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem1K_40nm_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml # Yosys script parameters -yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_cell_sim.v -yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram.txt -yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem16K_40nm_bram_map.v +yosys_cell_sim_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_cell_sim.v +yosys_bram_map_rules=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram.txt +yosys_bram_map_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_mem1K_40nm_bram_map.v # VPR parameter openfpga_vpr_device_layout=4x2 [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_16k/dual_port_ram_16k.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/dual_port_ram_1k/dual_port_ram_1k.v [SYNTHESIS_PARAM] bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_bram_flow.ys bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys -bench0_top = dual_port_ram_16k +bench0_top = dual_port_ram_1k [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 870432e7f1084f0c4553d0729c1992c0441e68d0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 12:45:52 -0600 Subject: [PATCH 089/164] [Test] Patch regression test script due to the change of DPRAM test case --- .../regression_test_scripts/fpga_verilog_reg_test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh index 8f64816de..70137dd77 100755 --- a/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_verilog_reg_test.sh @@ -38,11 +38,11 @@ run-task fpga_verilog/adder/hard_adder --debug --show_thread_logs echo -e "Testing Verilog generation with soft adder chain in CLBs "; run-task fpga_verilog/adder/soft_adder --debug --show_thread_logs -echo -e "Testing Verilog generation with 16k block RAMs "; -run-task fpga_verilog/bram/dpram16k --debug --show_thread_logs +echo -e "Testing Verilog generation with 1k block RAMs "; +run-task fpga_verilog/bram/dpram1k --debug --show_thread_logs -echo -e "Testing Verilog generation with 16k block RAMs spanning two columns "; -run-task fpga_verilog/bram/wide_dpram16k --debug --show_thread_logs +echo -e "Testing Verilog generation with 1k block RAMs spanning two columns "; +run-task fpga_verilog/bram/wide_dpram1k --debug --show_thread_logs echo -e "Testing Verilog generation with heterogeneous fabric using 8-bit single-mode multipliers "; run-task fpga_verilog/dsp/single_mode_mult_8x8 --debug --show_thread_logs From bc34efe3378e6c8961a35b55bd5dcb24fd582f26 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 14:32:17 -0600 Subject: [PATCH 090/164] [Arch] Bug fix in the architecture using BRAM spanning two columns --- .../k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml index 0b7820139..e4119f1cb 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem1K_40nm.xml @@ -198,7 +198,7 @@ memory.waddr[3:3] memory.raddr[3:3] memory.data_in[3:3] memory.data_out[3:3] memory.waddr[4:4] memory.raddr[4:4] memory.data_in[4:4] memory.data_out[4:4] memory.waddr[5:5] memory.raddr[5:5] memory.data_in[5:5] memory.data_out[5:5] - memory.wen memory.waddr[5:5] memory.raddr[5:5] memory.data_in[6:6] memory.data_out[6:6] + memory.wen memory.waddr[6:6] memory.raddr[6:6] memory.data_in[6:6] memory.data_out[6:6] memory.ren memory.data_in[7:7] memory.data_out[7:7] From f77b81fe5bbb1b17ef7185cf3379b410a55464c8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 28 Apr 2021 15:05:30 -0600 Subject: [PATCH 091/164] [Arch] recover the mem16k arch as it is used in other test cases --- ...c_N10_adder_chain_mem16K_40nm_openfpga.xml | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml new file mode 100644 index 000000000..5259e5e03 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 + + + 10e-12 5e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 16fff90607b77685272fd61e65831cc1eb2d0399 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 May 2021 15:17:27 -0600 Subject: [PATCH 092/164] [Benchmark] Add microbenchmark 1-bit blinking --- .../micro_benchmark/blinking/blinking.v | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/blinking/blinking.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/blinking/blinking.v b/openfpga_flow/benchmarks/micro_benchmark/blinking/blinking.v new file mode 100644 index 000000000..b8587055c --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/blinking/blinking.v @@ -0,0 +1,17 @@ +// ------------------------------ +// Design Name: Blinking +// Functionality: 1-bit blinking +// ------------------------------ +module blinking( + clk, + out +); + +input clk; +output out; + + always @(posedge clk) begin + out = ~out; + end + +endmodule From f1658cb735c27df5bef0eac617f37743618b695d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 May 2021 15:17:45 -0600 Subject: [PATCH 093/164] [Test] Deploy blinking to test cases --- .../full_testbench/fast_configuration_chain/config/task.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf index 6f4200ffe..1967bd2a4 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf @@ -27,6 +27,7 @@ arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v +bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/blinking/blinking.v [SYNTHESIS_PARAM] bench0_top = and2 @@ -38,5 +39,8 @@ bench1_chan_width = 300 bench2_top = and2_latch bench2_chan_width = 300 +bench3_top = blinking +bench3_chan_width = 300 + [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 01b3a96e4bd3223415826cd80545debae28f347f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 11:22:01 -0600 Subject: [PATCH 094/164] [Tool] Add report bitstream distribution functionality to architecture bitstream library --- .../src/bitstream_manager_utils.cpp | 22 ++++ .../src/bitstream_manager_utils.h | 3 + .../report_arch_bitstream_distribution.cpp | 121 ++++++++++++++++++ .../src/report_arch_bitstream_distribution.h | 23 ++++ .../test/test_arch_bitstream.cpp | 16 ++- 5 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp create mode 100644 libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h diff --git a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp index 8968b8d1c..721c147d4 100644 --- a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp +++ b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp @@ -71,5 +71,27 @@ size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamMa return curr_index; } +/******************************************************************** + * Find the total number of configuration bits under a block + * As configuration bits are stored only under the leaf blocks, + * this function will recursively visit all the child blocks + * until reaching a leaf block, where we collect the number of bits + *******************************************************************/ +size_t rec_find_bitstream_manager_block_sum_of_bits(const BitstreamManager& bitstream_manager, + const ConfigBlockId& block) { + /* For leaf block, return directly with the number of bits, because it has not child block */ + if (0 < bitstream_manager.block_bits(block).size()) { + VTR_ASSERT_SAFE(bitstream_manager.block_children(block).empty()); + return bitstream_manager.block_bits(block).size(); + } + + size_t sum_of_bits = 0; + /* Dive to child blocks if this block has any */ + for (const ConfigBlockId& child_block : bitstream_manager.block_children(block)) { + sum_of_bits += rec_find_bitstream_manager_block_sum_of_bits(bitstream_manager, child_block); + } + + return sum_of_bits; +} } /* end namespace openfpga */ diff --git a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h index 328394b01..e1e8b8567 100644 --- a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h +++ b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h @@ -22,6 +22,9 @@ std::vector find_bitstream_manager_top_blocks(const BitstreamMana size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager, const ConfigBitId& bit_id); +size_t rec_find_bitstream_manager_block_sum_of_bits(const BitstreamManager& bitstream_manager, + const ConfigBlockId& block); + } /* end namespace openfpga */ #endif diff --git a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp new file mode 100644 index 000000000..e5a1d47d9 --- /dev/null +++ b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp @@ -0,0 +1,121 @@ +/******************************************************************** + * This file includes functions that report distribution of bitstream by blocks + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" +#include "openfpga_tokenizer.h" +#include "openfpga_version.h" + +#include "openfpga_reserved_words.h" + +#include "bitstream_manager_utils.h" +#include "report_arch_bitstream_distribution.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function write header information for an XML file of bitstream distribution + *******************************************************************/ +static +void report_architecture_bitstream_distribution_xml_file_head(std::fstream& fp) { + valid_file_stream(fp); + + auto end = std::chrono::system_clock::now(); + std::time_t end_time = std::chrono::system_clock::to_time_t(end); + + fp << " " << std::endl; + fp << std::endl; +} + +/******************************************************************** + * Recursively report the bitstream distribution of a block to a file + * This function will use a Depth-First Search in outputting bitstream + * for each block + * For block with child blocks, we visit each child recursively + * The reporting can be stopped at a given maximum hierarchy level + * which is used to limit the length of the report + *******************************************************************/ +static +void rec_report_block_bitstream_distribution_to_xml_file(std::fstream& fp, + const BitstreamManager& bitstream_manager, + const ConfigBlockId& block, + const size_t& max_hierarchy_level, + const size_t& hierarchy_level) { + valid_file_stream(fp); + + if (hierarchy_level > max_hierarchy_level) { + return; + } + + /* Write the bitstream distribution 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_report_block_bitstream_distribution_to_xml_file(fp, bitstream_manager, child_block, + max_hierarchy_level, hierarchy_level + 1); + } + + write_tab_to_file(fp, hierarchy_level); + fp << "" < 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_report_block_bitstream_distribution_to_xml_file(fp, bitstream_manager, top_block[0], max_hierarchy_level, 0); + + /* Close file handler */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h new file mode 100644 index 000000000..aedbce765 --- /dev/null +++ b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h @@ -0,0 +1,23 @@ +#ifndef REPORT_ARCH_BITSTREAM_DISTRIBUTION_H +#define REPORT_ARCH_BITSTREAM_DISTRIBUTION_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "bitstream_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void report_architecture_bitstream_distribution(const BitstreamManager& bitstream_manager, + const std::string& fname, + const size_t& max_hierarchy_level = 1); + +} /* end namespace openfpga */ + +#endif diff --git a/libopenfpga/libfpgabitstream/test/test_arch_bitstream.cpp b/libopenfpga/libfpgabitstream/test/test_arch_bitstream.cpp index 88d00601e..d71b077c4 100644 --- a/libopenfpga/libfpgabitstream/test/test_arch_bitstream.cpp +++ b/libopenfpga/libfpgabitstream/test/test_arch_bitstream.cpp @@ -10,17 +10,18 @@ /* Headers from fabric key */ #include "read_xml_arch_bitstream.h" #include "write_xml_arch_bitstream.h" +#include "report_arch_bitstream_distribution.h" int main(int argc, const char** argv) { - /* Ensure we have only one or two argument */ - VTR_ASSERT((2 == argc) || (3 == argc)); + /* Ensure we have only one or two or 3 argument */ + VTR_ASSERT((2 == argc) || (3 == argc) || (4 == argc)); /* Parse the bitstream from an XML file */ openfpga::BitstreamManager test_bitstream = openfpga::read_xml_architecture_bitstream(argv[1]); VTR_LOG("Read the bitstream from an XML file: %s.\n", argv[1]); - /* Output the circuit library to an XML file + /* Output the bitstream database to an XML file * This is optional only used when there is a second argument */ if (3 <= argc) { @@ -28,6 +29,15 @@ int main(int argc, const char** argv) { VTR_LOG("Echo the bitstream to an XML file: %s.\n", argv[2]); } + /* Output the bitstream distribution to an XML file + * This is optional only used when there is a third argument + */ + if (4 <= argc) { + openfpga::report_architecture_bitstream_distribution(test_bitstream, argv[3]); + VTR_LOG("Echo the bitstream distribution to an XML file: %s.\n", + argv[3]); + } + } From db9bb9124e9462cda4780e9306cf3815fc837eba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 11:41:25 -0600 Subject: [PATCH 095/164] [Tool] Add report bitstream distribution command to openfpga shell --- .../report_arch_bitstream_distribution.cpp | 9 ++-- .../src/report_arch_bitstream_distribution.h | 6 +-- openfpga/src/base/openfpga_bitstream.cpp | 38 +++++++++++++++++ openfpga/src/base/openfpga_bitstream.h | 3 ++ .../src/base/openfpga_bitstream_command.cpp | 42 +++++++++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp index e5a1d47d9..1667b3e86 100644 --- a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp +++ b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.cpp @@ -86,12 +86,13 @@ void rec_report_block_bitstream_distribution_to_xml_file(std::fstream& fp, * Notes: * - The output format is a table whose format is compatible with RST files *******************************************************************/ -void report_architecture_bitstream_distribution(const BitstreamManager& bitstream_manager, - const std::string& fname, - const size_t& max_hierarchy_level) { +int report_architecture_bitstream_distribution(const BitstreamManager& bitstream_manager, + const std::string& fname, + const size_t& max_hierarchy_level) { /* Ensure that we have a valid file name */ if (true == fname.empty()) { VTR_LOG_ERROR("Received empty file name to report bitstream!\n\tPlease specify a valid file name.\n"); + return 1; } std::string timer_message = std::string("Report architecture bitstream distribution into XML file '") + fname + std::string("'"); @@ -116,6 +117,8 @@ void report_architecture_bitstream_distribution(const BitstreamManager& bitstrea /* Close file handler */ fp.close(); + + return 0; } } /* end namespace openfpga */ diff --git a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h index aedbce765..322a65901 100644 --- a/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h +++ b/libopenfpga/libfpgabitstream/src/report_arch_bitstream_distribution.h @@ -14,9 +14,9 @@ /* begin namespace openfpga */ namespace openfpga { -void report_architecture_bitstream_distribution(const BitstreamManager& bitstream_manager, - const std::string& fname, - const size_t& max_hierarchy_level = 1); +int report_architecture_bitstream_distribution(const BitstreamManager& bitstream_manager, + const std::string& fname, + const size_t& max_hierarchy_level = 1); } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp index c35017ba8..f4ac33ab5 100644 --- a/openfpga/src/base/openfpga_bitstream.cpp +++ b/openfpga/src/base/openfpga_bitstream.cpp @@ -14,6 +14,7 @@ /* Headers from fpgabitstream library */ #include "read_xml_arch_bitstream.h" #include "write_xml_arch_bitstream.h" +#include "report_arch_bitstream_distribution.h" #include "openfpga_naming.h" @@ -165,4 +166,41 @@ int write_io_mapping(const OpenfpgaContext& openfpga_ctx, return status; } +/******************************************************************** + * A wrapper function to call the report_arch_bitstream_distribution() in FPGA bitstream + *******************************************************************/ +int report_bitstream_distribution(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_file = cmd.option("file"); + + int status = CMD_EXEC_SUCCESS; + + VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_file)); + + std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_file)); + + /* Create directories */ + create_directory(src_dir_path); + + /* Default depth requirement, this is to limit the report size by default */ + int depth = 1; + CommandOptionId opt_depth = cmd.option("depth"); + if (true == cmd_context.option_enable(cmd, opt_depth)) { + depth = std::atoi(cmd_context.option_value(cmd, opt_depth).c_str()); + /* Error out if we have negative depth */ + if (0 > depth) { + VTR_LOG_ERROR("Invalid depth '%d' which should be 0 or a positive number!\n", + depth); + return CMD_EXEC_FATAL_ERROR; + } + } + + status = report_architecture_bitstream_distribution(openfpga_ctx.bitstream_manager(), + cmd_context.option_value(cmd, opt_file), + depth); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_bitstream.h b/openfpga/src/base/openfpga_bitstream.h index 2e438d56b..c04fb7656 100644 --- a/openfpga/src/base/openfpga_bitstream.h +++ b/openfpga/src/base/openfpga_bitstream.h @@ -27,6 +27,9 @@ int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx, int write_io_mapping(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int report_bitstream_distribution(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_bitstream_command.cpp b/openfpga/src/base/openfpga_bitstream_command.cpp index 3599bcac5..80b4d8263 100644 --- a/openfpga/src/base/openfpga_bitstream_command.cpp +++ b/openfpga/src/base/openfpga_bitstream_command.cpp @@ -72,6 +72,40 @@ ShellCommandId add_openfpga_build_arch_bitstream_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("report_bitstream_distribution"); + + /* Add an option '--file' */ + CommandOptionId opt_file = shell_cmd.add_option("file", true, "file path to output the bitstream distribution"); + shell_cmd.set_option_short_name(opt_file, "f"); + shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING); + + /* Add an option '--depth' */ + CommandOptionId opt_depth = shell_cmd.add_option("depth", false, "Specify the max. depth of blocks which will appear in report"); + shell_cmd.set_option_require_value(opt_depth, openfpga::OPT_INT); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command 'report_bitstream_distribution' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Report bitstream distribution"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, report_bitstream_distribution); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + /******************************************************************** * - Add a command to Shell environment: build_fabric_bitstream * - Add associated options @@ -187,6 +221,14 @@ void add_openfpga_bitstream_commands(openfpga::Shell& shell) { cmd_dependency_build_arch_bitstream.push_back(shell_cmd_repack_id); ShellCommandId shell_cmd_build_arch_bitstream_id = add_openfpga_build_arch_bitstream_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_build_arch_bitstream); + /******************************** + * Command 'report_bitstream_distribution' + */ + /* The 'report_bitstream_distribution' command should NOT be executed before 'build_architecture_bitstream' */ + std::vector cmd_dependency_report_bitstream_distribution; + cmd_dependency_build_arch_bitstream.push_back(shell_cmd_build_arch_bitstream_id); + add_openfpga_report_bitstream_distribution_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_report_bitstream_distribution); + /******************************** * Command 'build_fabric_bitstream' */ From c4ecc9ee7cf35fad710e6f61eca2d4a8f1daf07c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 11:44:01 -0600 Subject: [PATCH 096/164] [Tool] Patch data type of report bitstream distribution command-line option --- openfpga/src/base/openfpga_bitstream_command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/base/openfpga_bitstream_command.cpp b/openfpga/src/base/openfpga_bitstream_command.cpp index 80b4d8263..39545d12f 100644 --- a/openfpga/src/base/openfpga_bitstream_command.cpp +++ b/openfpga/src/base/openfpga_bitstream_command.cpp @@ -90,7 +90,7 @@ ShellCommandId add_openfpga_report_bitstream_distribution_command(openfpga::Shel /* Add an option '--depth' */ CommandOptionId opt_depth = shell_cmd.add_option("depth", false, "Specify the max. depth of blocks which will appear in report"); - shell_cmd.set_option_require_value(opt_depth, openfpga::OPT_INT); + shell_cmd.set_option_require_value(opt_depth, openfpga::OPT_STRING); /* Add an option '--verbose' */ shell_cmd.add_option("verbose", false, "Enable verbose output"); From 24f83f00588663a2ea6aced7c9dbe47ea52476c7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 11:54:33 -0600 Subject: [PATCH 097/164] [Doc] Update documentation about the new command 'report_bitstream_distribution' --- .../bitstream_distribution_file.rst | 50 +++++++++++++++++++ docs/source/manual/file_formats/index.rst | 2 + .../fpga_bitstream_commands.rst | 18 +++++++ 3 files changed, 70 insertions(+) create mode 100644 docs/source/manual/file_formats/bitstream_distribution_file.rst diff --git a/docs/source/manual/file_formats/bitstream_distribution_file.rst b/docs/source/manual/file_formats/bitstream_distribution_file.rst new file mode 100644 index 000000000..a1f3282d2 --- /dev/null +++ b/docs/source/manual/file_formats/bitstream_distribution_file.rst @@ -0,0 +1,50 @@ +.. _file_format_bitstream_distribution_file: + +Bitstream Distribution File (.xml) +---------------------------------- + +The bitstream distribution file aims to show + +- The total number of configuration bits under each block +- The number of configuration bits per block + +An example of design constraints is shown as follows. + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. option:: name="" + + The block name represents the instance name which you can find in the fabric netlists + +.. option:: number_of_bits="" + + The total number of configuration bits in this block diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index d1a77e2d0..7ed213012 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -23,3 +23,5 @@ OpenFPGA widely uses XML format for interchangable files fabric_key io_mapping_file + + bitstream_distribution_file diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst index fbe10e033..a63717e15 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst @@ -87,4 +87,22 @@ write_io_mapping Show verbose log +report_bitstream_distribution +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Output the bitstream distribution to a file + + .. option:: --file or -f + + Specify the file name where the bitstream distribution will be outputted to. + See file formats in :ref:`file_format_bitstream_distribution_file`. + + .. option:: --depth or -d + + Specify the maximum depth of the block which should appear in the block + + .. option:: --verbose + + Show verbose log + From 7dc7c1b4f5576779361791296188a49d456c1c1c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 12:05:47 -0600 Subject: [PATCH 098/164] [Script] Add example openfpga shell script showing how to use 'report_bitstream_distribution' command --- ...tream_distribution_example_script.openfpga | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga new file mode 100644 index 000000000..7fd9666f0 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga @@ -0,0 +1,54 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --absorb_buffer_luts off + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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 +# - Enabled frame view creation to save runtime and memory +# Note that this is turned on when bitstream generation +# is the ONLY purpose of the flow!!! +build_fabric --compress_routing --frame_view #--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 --write_file fabric_independent_bitstream.xml + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.txt --format plain_text +write_fabric_bitstream --file fabric_bitstream.xml --format xml + +# Report bitstream distribution to a file +report_bitstream_distribution ${OPENFPGA_REPORT_BITSTREAM_DISTRIBUTION_OPTIONS} + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory From 2baf3ddd2f9e25ba86f6c0a2905abaf331e84e95 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 12:06:24 -0600 Subject: [PATCH 099/164] [Test] Add test cases for 'report_bitstream_distribution' command --- .../custom_depth/config/task.conf | 33 +++++++++++++++++++ .../default_depth/config/task.conf | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/custom_depth/config/task.conf create mode 100644 openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/default_depth/config/task.conf diff --git a/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/custom_depth/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/custom_depth/config/task.conf new file mode 100644 index 000000000..ad24a1dd3 --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/custom_depth/config/task.conf @@ -0,0 +1,33 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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 = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_report_bitstream_distribution_options=--file bitstream_distribution.xml --depth 2 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SYNTHESIS_PARAM] +bench0_top = and2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] diff --git a/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/default_depth/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/default_depth/config/task.conf new file mode 100644 index 000000000..fcbcb6a63 --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/report_bitstream_distribution/default_depth/config/task.conf @@ -0,0 +1,33 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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 = true +spice_output=false +verilog_output=true +timeout_each_job = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/report_bitstream_distribution_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_report_bitstream_distribution_options=--file bitstream_distribution.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SYNTHESIS_PARAM] +bench0_top = and2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] From c33ca464dc87035742fefce10e0361ebfee42348 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 May 2021 12:06:46 -0600 Subject: [PATCH 100/164] [Test] Deploy new tests to regression test --- .../regression_test_scripts/fpga_bitstream_reg_test.sh | 4 ++++ 1 file changed, 4 insertions(+) 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 1f1eac786..58858e0b9 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -28,3 +28,7 @@ run-task fpga_bitstream/overload_mux_default_path --debug --show_thread_logs echo -e "Testing outputting I/O mapping result to file"; run-task fpga_bitstream/write_io_mapping --debug --show_thread_logs + +echo -e "Testing report bitstream distribution to file"; +run-task fpga_bitstream/report_bitstream_distribution/default_depth --debug --show_thread_logs +run-task fpga_bitstream/report_bitstream_distribution/custom_depth --debug --show_thread_logs From b6b98a75b8ca10c01f0317b4ebf23076a398ef8f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 May 2021 13:03:40 -0600 Subject: [PATCH 101/164] [Doc] Add example circuit model about multi-mode flip-flops; Separate data-path FF circuit model and configuration-chain FF circuit model; --- .../arch_lang/circuit_model_examples.rst | 75 ++++++++- .../figures/multi_mode_ff_circuit_model.svg | 152 ++++++++++++++++++ docs/source/overview/tech_highlights.rst | 11 +- 3 files changed, 225 insertions(+), 13 deletions(-) create mode 100644 docs/source/manual/arch_lang/figures/multi_mode_ff_circuit_model.svg diff --git a/docs/source/manual/arch_lang/circuit_model_examples.rst b/docs/source/manual/arch_lang/circuit_model_examples.rst index a7a6dba97..32bddd841 100644 --- a/docs/source/manual/arch_lang/circuit_model_examples.rst +++ b/docs/source/manual/arch_lang/circuit_model_examples.rst @@ -963,16 +963,15 @@ This example shows: .. note:: If the embedded harden logic are driven partially by LUT outputs, users may use the :ref:`file_formats_bitstream_setting` to gaurantee correct bitstream generation for the LUTs. - -Flip-Flops -~~~~~~~~~~ +Datapath Flip-Flops +~~~~~~~~~~~~~~~~~~~ Template ```````` .. code-block:: xml - + @@ -987,16 +986,14 @@ Template .. note:: FPGA-Verilog/SPICE currently support only one clock domain in the FPGA. Therefore there should be only one clock port to be defined and the size of the clock port should be 1. -.. option:: +.. option:: type="ff" - - ``type="ccff|ff"`` Specify the type of a flip-flop. ``ff`` is a regular flip-flop while ``ccff`` denotes a configuration-chain flip-flop + ``ff`` is a regular flip-flop to be used in datapath logic, e.g., a configurable logic block. .. note:: A flip-flop should at least have three types of ports, ``input``, ``output`` and ``clock``. .. note:: If the user provides a customized Verilog/SPICE netlist, the bandwidth of ports should be defined to the same as the Verilog/SPICE netlist. -.. note:: In a valid FPGA architecture, users should provide at least either a ``ccff`` or ``sram`` circuit model, so that the configurations can loaded to core logic. - .. _circuit_model_dff_example: D-type Flip-Flop @@ -1029,6 +1026,68 @@ This example shows: - The flip-flop has ``set`` and ``reset`` functionalities - The flip-flop port names defined differently in standard cell library and VPR architecture. The ``lib_name`` capture the port name defined in standard cells, while ``prefix`` capture the port name defined in ``pb_type`` of VPR architecture file +.. _circuit_model_multi_mode_ff_example: + +Multi-mode Flip-Flop +```````````````````` + +:numref:`fig_multi_mode_ff_circuit_model` illustrates an example of a flip-flop which can be operate in different modes. + +.. _fig_multi_mode_ff_circuit_model: + +.. figure:: ./figures/multi_mode_ff_circuit_model.svg + :scale: 100% + :alt: Multi-mode flip-flop example + + An example of a flip-flop which can be operate in different modes + +The code describing this FF is: + +.. code-block:: xml + + + + + + + + + +This example shows: + - A multi-mode flip-flop which is defined in a Verilog netlist ``frac_ff.v`` and a SPICE netlist ``frac_ff.sp`` + - The flip-flop has a ``reset`` pin which can be either active-low or active-high, depending on the mode selection pin ``MODE``. + - The mode-selection bit will be generated by a configurable memory outside the flip-flop, which will be implemented by a circuit model ``CCFF`` defined by users (see an example in :ref:`circuit_model_ccff_example`). + - The flip-flop port names defined differently in standard cell library and VPR architecture. The ``lib_name`` capture the port name defined in standard cells, while ``prefix`` capture the port name defined in ``pb_type`` of VPR architecture file + +Configuration Chain Flip-Flop +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Template +```````` + +.. code-block:: xml + + + + + + + + + + +.. note:: The circuit designs of configurable memory elements are highly dependent on the technology node and well optimized by engineers. Therefore, FPGA-Verilog/SPICE requires users to provide their customized FF Verilog/SPICE/Verilog netlists. A sample Verilog/SPICE netlist of FF can be found in the directory SpiceNetlists in the released package. + + The information of input and output buffer should be clearly specified according to the customized SPICE netlist! The existence of input/output buffers will influence the decision in creating SPICE testbenches, which may leads to larger errors in power analysis. + +.. note:: FPGA-Verilog/SPICE currently support only one clock domain for any configuration protocols in the FPGA. Therefore there should be only one clock port to be defined and the size of the clock port should be 1. + +.. note:: A flip-flop should at least have three types of ports, ``input``, ``output`` and ``clock``. + +.. note:: If the user provides a customized Verilog/SPICE netlist, the bandwidth of ports should be defined to the same as the Verilog/SPICE netlist. + +.. note:: In a valid FPGA architecture, users should provide at least either a ``ccff`` or ``sram`` circuit model, so that the configurations can loaded to core logic. + .. _circuit_model_ccff_example: Regular Configuration-chain Flip-flop diff --git a/docs/source/manual/arch_lang/figures/multi_mode_ff_circuit_model.svg b/docs/source/manual/arch_lang/figures/multi_mode_ff_circuit_model.svg new file mode 100644 index 000000000..de4016044 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/multi_mode_ff_circuit_model.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 18:52:28 +0000 + + multi_mode_ff + + Layer 1 + + + + + + + + + FF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + Q + + + + + RST + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Multi-mode FF + + + + + Q + + + + + D + + + + + MODE + + + + + CLK + + + + + RST_OP + + + + + diff --git a/docs/source/overview/tech_highlights.rst b/docs/source/overview/tech_highlights.rst index 95521cc29..c0d71fd15 100644 --- a/docs/source/overview/tech_highlights.rst +++ b/docs/source/overview/tech_highlights.rst @@ -42,15 +42,16 @@ Supported Circuit Designs +-----------------+--------------+-----------+-----------------------------------------------------+ | | Configurable | No | Yes | - :ref:`circuit_model_config_latch_example` | | | Memory | | | - :ref:`circuit_model_sram_blwl_example` | -| | | | - :ref:`circuit_model_dff_example` | | | | | - :ref:`circuit_model_ccff_example` | | | | | - :ref:`circuit_model_ccff_enable_example` | | | | | - :ref:`circuit_model_ccff_scanable_example` | +-----------------+--------------+-----------+-----------------------------------------------------+ -| Block RAM | No | Yes | - **Any size** | -| | | | - Single-port | -| | | | - Dual-port | -| | | | - Fracturable | +| Data Memory | No | Yes | - **Any size** | +| | | | - :ref:`circuit_model_dff_example` | +| | | | - :ref:`circuit_model_multi_mode_ff_example` | +| | | | - Single-port Block RAM | +| | | | - Dual-port Block RAM | +| | | | - Multi-mode Block RAM | +-----------------+--------------+-----------+-----------------------------------------------------+ | | Arithmetic | No | Yes | - **Any size** | | | Units | | | - Multiplier | From 21a18069a12a4941aab7511b65ae7c4ee252756f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 May 2021 14:50:55 -0600 Subject: [PATCH 102/164] [Doc] Add example circuit about dual-port RAMs to documentation; Updated technical highlights by providing links to the examples --- .../arch_lang/circuit_model_examples.rst | 84 ++++- ...i_mode_dpram128x8_memory_circuit_model.svg | 342 ++++++++++++++++++ ...e_mode_dpram128x8_memory_circuit_model.svg | 104 ++++++ docs/source/overview/tech_highlights.rst | 4 +- 4 files changed, 531 insertions(+), 3 deletions(-) create mode 100644 docs/source/manual/arch_lang/figures/multi_mode_dpram128x8_memory_circuit_model.svg create mode 100644 docs/source/manual/arch_lang/figures/single_mode_dpram128x8_memory_circuit_model.svg diff --git a/docs/source/manual/arch_lang/circuit_model_examples.rst b/docs/source/manual/arch_lang/circuit_model_examples.rst index 32bddd841..ca5c9e419 100644 --- a/docs/source/manual/arch_lang/circuit_model_examples.rst +++ b/docs/source/manual/arch_lang/circuit_model_examples.rst @@ -284,6 +284,8 @@ This example shows: SRAMs ~~~~~ +.. note:: OpenFPGA does not auto-generate any netlist for SRAM cells. Users should define the HDL modeling in external netlists and ensure consistency to physical designs. + Template ```````` @@ -966,6 +968,8 @@ This example shows: Datapath Flip-Flops ~~~~~~~~~~~~~~~~~~~ +.. note:: OpenFPGA does not auto-generate any netlist for datapath flip-flops. Users should define the HDL modeling in external netlists and ensure consistency to physical designs. + Template ```````` @@ -1036,7 +1040,7 @@ Multi-mode Flip-Flop .. _fig_multi_mode_ff_circuit_model: .. figure:: ./figures/multi_mode_ff_circuit_model.svg - :scale: 100% + :scale: 150% :alt: Multi-mode flip-flop example An example of a flip-flop which can be operate in different modes @@ -1062,6 +1066,8 @@ This example shows: Configuration Chain Flip-Flop ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. note:: OpenFPGA does not auto-generate any netlist for configuration chain flip-flops. Users should define the HDL modeling in external netlists and ensure consistency to physical designs. + Template ```````` @@ -1206,6 +1212,8 @@ The code describing this FF is: Hard Logics ~~~~~~~~~~~ +.. note:: OpenFPGA does not auto-generate any netlist for the hard logics. Users should define the HDL modeling in external netlists and ensure consistency to physical designs. + Template ```````` @@ -1248,6 +1256,78 @@ Full Adder +This example shows: + - A 1-bit full adder which is defined in a Verilog netlist ``adder.v`` and a SPICE netlist ``adder.sp`` + - The adder has three 1-bit inputs, i.e., ``a``, ``b`` and ``cin``, and two 2-bit outputs, i.e., ``cout``, ``sumout``. + + +.. _circuit_model_single_mode_dpram_example: + +Dual Port Block RAM +``````````````````` + +.. figure:: ./figures/single_mode_dpram128x8_memory_circuit_model.svg + :scale: 150% + :alt: An example of a dual port block RAM with 128 addresses and 8-bit data width. + + An example of a dual port block RAM with 128 addresses and 8-bit data width. + +The code describing this block RAM is: + +.. code-block:: xml + + + + + + + + + + + + + + +This example shows: + - A 128x8 dual port RAM which is defined in a Verilog netlist ``dpram.v`` and a SPICE netlist ``dpram.sp`` + - The clock port of the RAM is controlled by a global signal (see details about global signal definition in :ref:`annotate_vpr_arch_physical_tile_annotation`). + +.. _circuit_model_multi_mode_dpram_example: + +Multi-mode Dual Port Block RAM +`````````````````````````````` + +.. figure:: ./figures/multi_mode_dpram128x8_memory_circuit_model.svg + :scale: 150% + :alt: An example of a multi-mode dual port block RAM with 128 addresses and 8-bit data width. + + An example of a dual port block RAM which can operate in two modes: 128x8 and 256x4. + +The code describing this block RAM is: + +.. code-block:: xml + + + + + + + + + + + + + + + +This example shows: + - A fracturable dual port RAM which is defined in a Verilog netlist ``frac_dpram.v`` and a SPICE netlist ``frac_dpram.sp`` + - The dual port RAM can operate in two modes: (1) 128 addresses with 8-bit data width; (2) 256 addresses with 4-bit data width + - The clock port of the RAM is controlled by a global signal (see details about global signal definition in :ref:`annotate_vpr_arch_physical_tile_annotation`). + - The mode-selection bit will be generated by a configurable memory outside the flip-flop, which will be implemented by a circuit model ``CCFF`` defined by users (see an example in :ref:`circuit_model_ccff_example`). + Routing Wire Segments ~~~~~~~~~~~~~~~~~~~~~ @@ -1314,6 +1394,8 @@ This example shows I/O pads ~~~~~~~~ +.. note:: OpenFPGA does not auto-generate any netlist for I/O cells. Users should define the HDL modeling in external netlists and ensure consistency to physical designs. + Template ```````` diff --git a/docs/source/manual/arch_lang/figures/multi_mode_dpram128x8_memory_circuit_model.svg b/docs/source/manual/arch_lang/figures/multi_mode_dpram128x8_memory_circuit_model.svg new file mode 100644 index 000000000..ff02de2fc --- /dev/null +++ b/docs/source/manual/arch_lang/figures/multi_mode_dpram128x8_memory_circuit_model.svg @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 20:15:49 +0000 + + multi_mode_dual_port_bram 1 + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + clock + + + + + raddr[7:0] + + + + + waddr[7:0] + + + + + wen + + + + + ren + + + + + data_in[7:0] + + + + + data_out[7:0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mode + + + + + + + + + + + + + + + + clock + + + + + raddr[6:0] + + + + + waddr[6:0] + + + + + wen + + + + + ren + + + + + data_in[7:0] + + + + + data_out[7:0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mode + + + + + + + + ‘1’ + + + + + + + + + + + + + clock + + + + + raddr[7:0] + + + + + waddr[7:0] + + + + + wen + + + + + ren + + + + + data_in[3:0] + + + + + data_out[3:0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mode + + + + + + + + ‘0’ + + + + + Operating mode 2: Dual port 256 + x + 4 RAM + + + + + Operating mode 1: Dual port 128x8 RAM + + + + + Schematic: Multi-mode dual port 128x8/256x4 RAM + + + + + + + + + + + + + diff --git a/docs/source/manual/arch_lang/figures/single_mode_dpram128x8_memory_circuit_model.svg b/docs/source/manual/arch_lang/figures/single_mode_dpram128x8_memory_circuit_model.svg new file mode 100644 index 000000000..458155c98 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/single_mode_dpram128x8_memory_circuit_model.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 19:57:04 +0000 + + single_mode_dual_port_bram + + Layer 1 + + + + + + + + + + + clock + + + + + raddr[6:0] + + + + + waddr[6:0] + + + + + wen + + + + + ren + + + + + data_in[7:0] + + + + + data_out[7:0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/overview/tech_highlights.rst b/docs/source/overview/tech_highlights.rst index c0d71fd15..461be231c 100644 --- a/docs/source/overview/tech_highlights.rst +++ b/docs/source/overview/tech_highlights.rst @@ -50,8 +50,8 @@ Supported Circuit Designs | | | | - :ref:`circuit_model_dff_example` | | | | | - :ref:`circuit_model_multi_mode_ff_example` | | | | | - Single-port Block RAM | -| | | | - Dual-port Block RAM | -| | | | - Multi-mode Block RAM | +| | | | - :ref:`circuit_model_single_mode_dpram_example` | +| | | | - :ref:`circuit_model_multi_mode_dpram_example` | +-----------------+--------------+-----------+-----------------------------------------------------+ | | Arithmetic | No | Yes | - **Any size** | | | Units | | | - Multiplier | From 9b40e74e2555c80f0e0e2d1d5449d3d4b8e4ba68 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 May 2021 15:24:50 -0600 Subject: [PATCH 103/164] [Doc] Add example circuit models for multipliers and update technical highlight with links to the examples --- .../arch_lang/circuit_model_examples.rst | 63 +++++ .../figures/full_adder_1bit_circuit_model.svg | 76 ++++++ .../multi_mode_mult8x8_circuit_model.svg | 249 ++++++++++++++++++ .../single_mode_mult8x8_circuit_model.svg | 66 +++++ docs/source/overview/tech_highlights.rst | 5 +- 5 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 docs/source/manual/arch_lang/figures/full_adder_1bit_circuit_model.svg create mode 100644 docs/source/manual/arch_lang/figures/multi_mode_mult8x8_circuit_model.svg create mode 100644 docs/source/manual/arch_lang/figures/single_mode_mult8x8_circuit_model.svg diff --git a/docs/source/manual/arch_lang/circuit_model_examples.rst b/docs/source/manual/arch_lang/circuit_model_examples.rst index ca5c9e419..7113be1c0 100644 --- a/docs/source/manual/arch_lang/circuit_model_examples.rst +++ b/docs/source/manual/arch_lang/circuit_model_examples.rst @@ -1242,6 +1242,13 @@ Template Full Adder `````````` +.. figure:: ./figures/full_adder_1bit_circuit_model.svg + :scale: 200% + :alt: An example of a 1-bit full adder + + An example of a 1-bit full adder. + +The code describing the 1-bit full adder is: .. code-block:: xml @@ -1260,6 +1267,62 @@ This example shows: - A 1-bit full adder which is defined in a Verilog netlist ``adder.v`` and a SPICE netlist ``adder.sp`` - The adder has three 1-bit inputs, i.e., ``a``, ``b`` and ``cin``, and two 2-bit outputs, i.e., ``cout``, ``sumout``. +.. _circuit_model_single_mode_mult8x8_example: + +Multiplier +`````````` + +.. figure:: ./figures/single_mode_mult8x8_circuit_model.svg + :scale: 200% + :alt: An example of a 8-bit multiplier. + + An example of a 8-bit multiplier. + +The code describing the multiplier is: + +.. code-block:: xml + + + + + + + + + + +This example shows: + - A 8-bit multiplier which is defined in a Verilog netlist ``dsp.v`` and a SPICE netlist ``dsp.sp`` + +.. _circuit_model_multi_mode_mult8x8_example: + +Multi-mode Multiplier +````````````````````` + +.. figure:: ./figures/multi_mode_mult8x8_circuit_model.svg + :scale: 200% + :alt: An example of a 8-bit multiplier which can operating in two modes: (1) dual 4-bit multipliers; and (2) 8-bit multiplier + + An example of a 8-bit multiplier which can operating in two modes: (1) dual 4-bit multipliers; and (2) 8-bit multiplier + +The code describing the multiplier is: + +.. code-block:: xml + + + + + + + + + + + +This example shows: + - A multi-mode 8-bit multiplier which is defined in a Verilog netlist ``dsp.v`` and a SPICE netlist ``dsp.sp`` + - The multi-mode multiplier can operating in two modes: (1) dual 4-bit multipliers; and (2) 8-bit multiplier + - The mode-selection bit will be generated by a configurable memory outside the flip-flop, which will be implemented by a circuit model ``CCFF`` defined by users (see an example in :ref:`circuit_model_ccff_example`). .. _circuit_model_single_mode_dpram_example: diff --git a/docs/source/manual/arch_lang/figures/full_adder_1bit_circuit_model.svg b/docs/source/manual/arch_lang/figures/full_adder_1bit_circuit_model.svg new file mode 100644 index 000000000..dc7ff3837 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/full_adder_1bit_circuit_model.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 20:57:16 +0000 + + 1-bit full adder + + Layer 1 + + + + + adder + + + + + + cin + + + + + a + + + + + b + + + + + + + + + + + + + + sumout + + + + + + + + cout + + + + + + + + diff --git a/docs/source/manual/arch_lang/figures/multi_mode_mult8x8_circuit_model.svg b/docs/source/manual/arch_lang/figures/multi_mode_mult8x8_circuit_model.svg new file mode 100644 index 000000000..8c9b5f2a1 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/multi_mode_mult8x8_circuit_model.svg @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 21:09:05 +0000 + + multi_mode_mult_8x8 + + Layer 1 + + + + + + + + + + + + + MULT + 8x8 + + + + + a[7:0] + + + + + b[7:0] + + + + + + + + + + + + + + out[15:0] + + + + + + + + + + + + + MULT + 4x4 + [0] + + + + + a[7:4] + + + + + b[7:4] + + + + + + + + + + + + + + out[15:8] + + + + + + + + + + + + + MULT + 4x4 + [1] + + + + + a[3:0] + + + + + b[3:0] + + + + + + + + + + + + + + out[7:0] + + + + + + + + + + + Operating mode 1: Dual 4x4 multiplier + + + + + + + + mode + + + + + Schematic: multi-mode 8x8 multiplier + + + + + + + + + + + MULT + 8x8 + + + + + a[7:0] + + + + + b[7:0] + + + + + + + + + + + + + + out[15:0] + + + + + + + + + + + Operating mode 2: 8x8 multiplier + + + + + + + + mode=‘0’ + + + + + + + + + + + + + + + + mode=‘1’ + + + + + + + + diff --git a/docs/source/manual/arch_lang/figures/single_mode_mult8x8_circuit_model.svg b/docs/source/manual/arch_lang/figures/single_mode_mult8x8_circuit_model.svg new file mode 100644 index 000000000..41ac61370 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/single_mode_mult8x8_circuit_model.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-05-24 21:09:05 +0000 + + single_mode_mult_8x8 + + Layer 1 + + + + + MULT + 8x8 + + + + + a[7:0] + + + + + b[7:0] + + + + + + + + + + + + + + out[15:0] + + + + + + + + + + + diff --git a/docs/source/overview/tech_highlights.rst b/docs/source/overview/tech_highlights.rst index 461be231c..10e7dc1af 100644 --- a/docs/source/overview/tech_highlights.rst +++ b/docs/source/overview/tech_highlights.rst @@ -54,8 +54,9 @@ Supported Circuit Designs | | | | - :ref:`circuit_model_multi_mode_dpram_example` | +-----------------+--------------+-----------+-----------------------------------------------------+ | | Arithmetic | No | Yes | - **Any size** | -| | Units | | | - Multiplier | -| | | | - :ref:`circuit_model_full_adder_example` | +| | Units | | | - :ref:`circuit_model_full_adder_example` | +| | | | - :ref:`circuit_model_single_mode_mult8x8_example` | +| | | | - :ref:`circuit_model_multi_mode_mult8x8_example` | +-----------------+--------------+-----------+-----------------------------------------------------+ | I/O | No | Yes | - :ref:`circuit_model_gpio_example` | | | | | - Bi-directional buffer | From 16ae23f33e12f261cd5f900ce9a7545a3ea38f79 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 May 2021 16:26:59 -0600 Subject: [PATCH 104/164] [Doc] Update notes about compilation guidelines --- docs/source/tutorials/getting_started/compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/tutorials/getting_started/compile.rst b/docs/source/tutorials/getting_started/compile.rst index 3f2b45a2d..c7a5e74fc 100644 --- a/docs/source/tutorials/getting_started/compile.rst +++ b/docs/source/tutorials/getting_started/compile.rst @@ -24,7 +24,7 @@ In general, please follow the steps to compile .. note:: cmake3.12+ is recommended to compile OpenFPGA with GUI -.. note:: Recommand to use ``make -j`` to accelerate the compilation +.. note:: Recommand to use ``make -j`` to accelerate the compilation, where ```` denotes the number of cores to be used in compilation. .. note:: VPR's GUI requires gtk-3, and can be enabled with ``cmake .. -DVPR_USE_EZGL=on`` From 1fd399736d6c04a0ada47ce9dc98f8704d65e568 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 27 May 2021 10:26:20 -0600 Subject: [PATCH 105/164] [Tool] Patch FPGA-SDC to consider time unit in global port timing constraints --- openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp | 15 +++++++++++---- openfpga/src/fpga_sdc/pnr_sdc_global_port.h | 1 + openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp b/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp index 8f7c92046..156f78ff1 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp @@ -20,6 +20,7 @@ /* Headers from openfpgautil library */ #include "openfpga_port.h" #include "openfpga_digest.h" +#include "openfpga_scale.h" #include "sdc_writer_naming.h" #include "sdc_writer_utils.h" @@ -59,6 +60,7 @@ void print_pnr_sdc_clock_port(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_global_clock_ports(std::fstream& fp, + const float& time_unit, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& fabric_global_port_info, @@ -103,7 +105,7 @@ void print_pnr_sdc_global_clock_ports(std::fstream& fp, print_pnr_sdc_clock_port(fp, port_to_constrain, - clock_period); + clock_period / time_unit); } } } @@ -118,6 +120,7 @@ void print_pnr_sdc_global_clock_ports(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_global_non_clock_ports(std::fstream& fp, + const float& time_unit, const float& operating_critical_path_delay, const ModuleManager& module_manager, const ModuleId& top_module, @@ -144,7 +147,7 @@ void print_pnr_sdc_global_non_clock_ports(std::fstream& fp, print_pnr_sdc_clock_port(fp, port_to_constrain, - clock_period); + clock_period / time_unit); } } } @@ -161,6 +164,7 @@ void print_pnr_sdc_global_non_clock_ports(std::fstream& fp, * In general, we do not recommend to do this *******************************************************************/ void print_pnr_sdc_global_ports(const std::string& sdc_dir, + const float& time_unit, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& global_ports, @@ -183,12 +187,15 @@ void print_pnr_sdc_global_ports(const std::string& sdc_dir, /* Generate the descriptions*/ print_sdc_file_header(fp, std::string("Clock contraints for PnR")); - print_pnr_sdc_global_clock_ports(fp, + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + + print_pnr_sdc_global_clock_ports(fp, time_unit, module_manager, top_module, global_ports, sim_setting); if (true == constrain_non_clock_port) { - print_pnr_sdc_global_non_clock_ports(fp, + print_pnr_sdc_global_non_clock_ports(fp, time_unit, 1./sim_setting.default_operating_clock_frequency(), module_manager, top_module, global_ports); diff --git a/openfpga/src/fpga_sdc/pnr_sdc_global_port.h b/openfpga/src/fpga_sdc/pnr_sdc_global_port.h index 15808e623..ae12a5a21 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_global_port.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_global_port.h @@ -18,6 +18,7 @@ namespace openfpga { void print_pnr_sdc_global_ports(const std::string& sdc_dir, + const float& time_unit, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& global_ports, diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index 5fb902010..1bd025871 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -336,6 +336,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, /* Constrain global ports */ if (true == sdc_options.constrain_global_port()) { print_pnr_sdc_global_ports(sdc_options.sdc_dir(), + sdc_options.time_unit(), module_manager, top_module, global_ports, sim_setting, sdc_options.constrain_non_clock_global_port()); From ae6a46cd60249ccc8ae75ac0b53de57969bc5c94 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 15:37:49 -0600 Subject: [PATCH 106/164] [Tool] Add a new command write_full_testbench which outputs self-testable full testbench which loads external bitstream file; Currently only support configuration chain without fast configuration technique --- openfpga/src/base/openfpga_verilog.cpp | 50 ++ openfpga/src/base/openfpga_verilog.h | 3 + .../src/base/openfpga_verilog_command.cpp | 63 +++ openfpga/src/fpga_verilog/verilog_api.cpp | 64 ++- openfpga/src/fpga_verilog/verilog_api.h | 14 + .../fpga_verilog/verilog_top_testbench.cpp | 429 ++++++++++++++++++ .../src/fpga_verilog/verilog_top_testbench.h | 17 + .../src/fpga_verilog/verilog_writer_utils.cpp | 10 +- .../src/fpga_verilog/verilog_writer_utils.h | 3 +- 9 files changed, 648 insertions(+), 5 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index ad92228f1..6a3afdefd 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -119,4 +119,54 @@ int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, options); } +/******************************************************************** + * A wrapper function to call the full testbench generator of FPGA-Verilog + *******************************************************************/ +int write_full_testbench(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_bitstream = cmd.option("bitstream"); + CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); + CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); + CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); + CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog + * Keep it independent from any other outside data structures + */ + VerilogTestbenchOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); + options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); + options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_print_top_testbench(true); + options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init)); + + /* If pin constraints are enabled by command options, read the file */ + PinConstraints pin_constraints; + if (true == cmd_context.option_enable(cmd, opt_pcf)) { + pin_constraints = read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str()); + } + + return fpga_verilog_full_testbench(openfpga_ctx.module_graph(), + openfpga_ctx.bitstream_manager(), + openfpga_ctx.fabric_bitstream(), + g_vpr_ctx.atom(), + g_vpr_ctx.placement(), + pin_constraints, + cmd_context.option_value(cmd, opt_bitstream), + openfpga_ctx.io_location_map(), + openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.vpr_netlist_annotation(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.simulation_setting(), + openfpga_ctx.arch().config_protocol, + options); +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h index 096039aab..20ce5d858 100644 --- a/openfpga/src/base/openfpga_verilog.h +++ b/openfpga/src/base/openfpga_verilog.h @@ -21,6 +21,9 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx, int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_full_testbench(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 946f0e2bf..4518c0634 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -122,6 +122,59 @@ ShellCommandId add_openfpga_write_verilog_testbench_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_full_testbench"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for HDL netlists"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* Add an option '--bitstream'*/ + CommandOptionId bitstream_opt = shell_cmd.add_option("bitstream", true, "Specify the bitstream to be loaded in the testbench"); + shell_cmd.set_option_require_value(bitstream_opt, openfpga::OPT_STRING); + + /* Add an option '--fabric_netlist_file_path'*/ + CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "Specify the file path to the fabric HDL netlist"); + shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); + + /* Add an option '--pin_constraints_file in short '-pcf' */ + CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints"); + shell_cmd.set_option_short_name(pcf_opt, "pcf"); + shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); + + /* Add an option '--reference_benchmark_file_path'*/ + CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); + shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); + + /* Add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); + + /* Add an option '--include_signal_init' */ + shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog testbenches"); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate full testbenches for an FPGA fabric"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_full_testbench); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + void add_openfpga_verilog_commands(openfpga::Shell& shell) { /* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ const ShellCommandId& build_fabric_cmd_id = shell.command(std::string("build_fabric")); @@ -148,6 +201,16 @@ void add_openfpga_verilog_commands(openfpga::Shell& shell) { add_openfpga_write_verilog_testbench_command(shell, openfpga_verilog_cmd_class, verilog_testbench_dependent_cmds); + + /******************************** + * Command 'write_full_testbench' + */ + /* The command 'write_full_testbench' should NOT be executed before 'build_fabric' */ + std::vector full_testbench_dependent_cmds; + full_testbench_dependent_cmds.push_back(build_fabric_cmd_id); + add_openfpga_write_full_testbench_command(shell, + openfpga_verilog_cmd_class, + full_testbench_dependent_cmds); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 46a81c96e..fa0980eea 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -32,8 +32,7 @@ #include "verilog_api.h" /* begin namespace openfpga */ -namespace openfpga -{ +namespace openfpga { /******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation @@ -253,4 +252,65 @@ int fpga_verilog_testbench(const ModuleManager &module_manager, return status; } +/******************************************************************** + * A top-level function of FPGA-Verilog which focuses on full testbench generation + * This function will generate + * - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated + ********************************************************************/ +int fpga_verilog_full_testbench(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const FabricBitstream &fabric_bitstream, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const PinConstraints& pin_constraints, + const std::string& bitstream_file, + const IoLocationMap &io_location_map, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, + const SimulationSetting &simulation_setting, + const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options) { + + vtr::ScopedStartFinishTimer timer("Write Verilog full testbenches for FPGA fabric\n"); + + std::string src_dir_path = format_dir_path(options.output_directory()); + + std::string netlist_name = atom_ctx.nlist.netlist_name(); + + int status = CMD_EXEC_SUCCESS; + + /* Create directories */ + create_directory(src_dir_path); + + /* Output preprocessing flags for HDL simulations */ + print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + options); + + /* Generate full testbench for verification, including configuration phase and operating phase */ + std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_full_testbench(module_manager, + bitstream_manager, fabric_bitstream, + circuit_lib, + config_protocol, + fabric_global_port_info, + atom_ctx, place_ctx, + pin_constraints, + bitstream_file, + io_location_map, + netlist_annotation, + netlist_name, + top_testbench_file_path, + simulation_setting, + options); + + /* Generate a Verilog file including all the netlists that have been generated */ + print_verilog_testbench_include_netlists(src_dir_path, + netlist_name, + options.fabric_netlist_file_path(), + options.reference_benchmark_file_path()); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 6c3ec190e..f3f0b8321 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -57,6 +57,20 @@ int fpga_verilog_testbench(const ModuleManager& module_manager, const ConfigProtocol& config_protocol, const VerilogTestbenchOption& options); +int fpga_verilog_full_testbench(const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream, + const AtomContext& atom_ctx, + const PlacementContext& place_ctx, + const PinConstraints& pin_constraints, + const std::string& bitstream_file, + const IoLocationMap& io_location_map, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation& netlist_annotation, + const CircuitLibrary& circuit_lib, + const SimulationSetting& simulation_parameters, + const ConfigProtocol& config_protocol, + const VerilogTestbenchOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index d58a2c730..c87073c67 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -59,6 +59,10 @@ constexpr char* TOP_TB_OP_CLOCK_PORT_PREFIX = "operating_clk_"; constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock"; constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg"; constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg"; +constexpr char* TOP_TB_BITSTREAM_LENGTH_VARIABLE = "BITSTREAM_LENGTH"; +constexpr char* TOP_TB_BITSTREAM_WIDTH_VARIABLE = "BITSTREAM_WIDTH"; +constexpr char* TOP_TB_BITSTREAM_MEM_REG_NAME = "bit_mem"; +constexpr char* TOP_TB_BITSTREAM_INDEX_REG_NAME = "bit_index"; constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb"; @@ -1930,6 +1934,164 @@ void print_verilog_top_testbench_bitstream(std::fstream& fp, } } +/******************************************************************** + * Print stimulus for a FPGA fabric with a configuration chain protocol + * where configuration bits are programming in serial (one by one) + * Task list: + * 1. For clock signal, we should create voltage waveforms for two types of clock signals: + * a. operation clock + * b. programming clock + * 2. For Set/Reset, we reset the chip after programming phase ends + * and before operation phase starts + * 3. For input/output clb nets (mapped to I/O grids), + * we should create voltage waveforms only after programming phase + *******************************************************************/ +static +void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const bool& fast_configuration, + const bool& bit_value_to_skip, + const ModuleManager& module_manager, + const ModuleId& top_module, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + + /* Find the longest bitstream */ + size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream); + + /* Define a constant for the bitstream length */ + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), regional_bitstream_max_size); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), fabric_bitstream.num_regions()); + + /* Initial value should be the first configuration bits + * In the rest of programming cycles, + * configuration bits are fed at the falling edge of programming clock. + * We do not care the value of scan_chain head during the first programming cycle + * It is reset anyway + */ + ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name()); + BasicPort config_chain_head_port = module_manager.module_port(top_module, cc_head_port_id); + std::vector initial_values(config_chain_head_port.get_width(), 0); + + /* Declare local variables for bitstream loading in Verilog */ + print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----"); + fp << "reg [0:`" << TOP_TB_BITSTREAM_WIDTH_VARIABLE << " - 1] "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; + fp << std::endl; + + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); + fp << "initial begin" << std::endl; + fp << "\t"; + fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");"; + fp << std::endl; + + print_verilog_comment(fp, "----- Configuration chain default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values, true); + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " <= 0"; + fp << ";"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX), 1); + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t"; + fp << "if ("; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " >= "; + fp << "`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE; + fp << ") begin"; + fp << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t"; + std::vector config_done_final_values(config_done_port.get_width(), 1); + fp << generate_verilog_port_constant_values(config_done_port, config_done_final_values, true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "end else begin"; + fp << std::endl; + + fp << "\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, config_chain_head_port); + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[" << TOP_TB_BITSTREAM_INDEX_REG_NAME << "]"; + fp << ";" << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " <= "; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1"; + fp << ";" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + + +/******************************************************************** + * Generate the stimuli for the full testbench + * The simulation consists of two phases: configuration phase and operation phase + * Configuration bits are loaded serially. + * This is actually what we do for a physical FPGA + *******************************************************************/ +static +void print_verilog_full_testbench_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const e_config_protocol_type& config_protocol_type, + const bool& fast_configuration, + const bool& bit_value_to_skip, + const ModuleManager& module_manager, + const ModuleId& top_module, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream) { + + /* Branch on the type of configuration protocol */ + switch (config_protocol_type) { + case CONFIG_MEM_STANDALONE: + break; + case CONFIG_MEM_SCAN_CHAIN: + print_verilog_full_testbench_configuration_chain_bitstream(fp, bitstream_file, + fast_configuration, + bit_value_to_skip, + module_manager, top_module, + bitstream_manager, fabric_bitstream); + break; + case CONFIG_MEM_MEMORY_BANK: + break; + case CONFIG_MEM_FRAME_BASED: + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid configuration protocol type!\n"); + exit(1); + } +} + + /******************************************************************** * Connect proper stimuli to the reset port * This function is designed to drive the reset port of a benchmark module @@ -2017,6 +2179,8 @@ void print_verilog_top_testbench_check(std::fstream& fp, } /******************************************************************** + * TODO: This top function will be deprecated!!! The only top function is + * print_verilog_full_testbench() * The top-level function to generate a testbench, in order to verify: * 1. Configuration phase of the FPGA fabric, where the bitstream is * loaded to the configuration protocol of the FPGA fabric @@ -2275,4 +2439,269 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, fp.close(); } +/******************************************************************** + * The top-level function to generate a full testbench, in order to verify: + * 1. Configuration phase of the FPGA fabric, where the bitstream is + * loaded to the configuration protocol of the FPGA fabric + * 2. Operating phase of the FPGA fabric, where input stimuli are + * fed to the I/Os of the FPGA fabric + * +----------+ + * | FPGA | +------------+ + * +----->| Fabric |------>| | + * | | | | | + * | +----------+ | | + * | | Output | + * random_input_vectors -----+ | Vector |---->Functional correct? + * | | Comparator | + * | +-----------+ | | + * | | Input | | | + * +----->| Benchmark |----->| | + * +-----------+ +------------+ + * + *******************************************************************/ +int print_verilog_full_testbench(const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream, + const CircuitLibrary& circuit_lib, + const ConfigProtocol& config_protocol, + const FabricGlobalPortInfo& global_ports, + const AtomContext& atom_ctx, + const PlacementContext& place_ctx, + const PinConstraints& pin_constraints, + const std::string& bitstream_file, + const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& circuit_name, + const std::string& verilog_fname, + const SimulationSetting& simulation_parameters, + const VerilogTestbenchOption& options) { + + bool fast_configuration = options.fast_configuration(); + bool explicit_port_mapping = options.explicit_port_mapping(); + + std::string timer_message = std::string("Write autocheck testbench for FPGA top-level Verilog netlist for '") + circuit_name + std::string("'"); + + /* Start time count */ + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fname.c_str(), fp); + + /* Generate a brief description on the Verilog file*/ + std::string title = std::string("FPGA Verilog full testbench for top-level netlist of design: ") + circuit_name; + print_verilog_file_header(fp, title); + + /* Find the top_module */ + ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name()); + VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + + /* Preparation: find all the clock ports */ + std::vector clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); + + /* Preparation: find all the reset/set ports for programming usage */ + std::vector global_prog_reset_ports = find_fabric_global_programming_reset_ports(global_ports); + std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); + + /* Identify if we can apply fast configuration */ + bool apply_fast_configuration = fast_configuration; + if ( (global_prog_set_ports.empty() && global_prog_reset_ports.empty()) + && (true == fast_configuration)) { + VTR_LOG_WARN("None of global reset and set ports are defined for programming purpose. Fast configuration is turned off\n"); + apply_fast_configuration = false; + } + bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), + apply_fast_configuration, + global_prog_reset_ports, + global_prog_set_ports, + bitstream_manager, fabric_bitstream); + + /* Start of testbench */ + print_verilog_top_testbench_ports(fp, module_manager, top_module, + atom_ctx, netlist_annotation, + clock_port_names, + pin_constraints, + simulation_parameters, config_protocol, + circuit_name); + + /* Find the clock period */ + float prog_clock_period = (1./simulation_parameters.programming_clock_frequency()); + float default_op_clock_period = (1./simulation_parameters.default_operating_clock_frequency()); + float max_op_clock_period = 0.; + for (const SimulationClockId& clock_id : simulation_parameters.clocks()) { + max_op_clock_period = std::max(max_op_clock_period, (float)(1./simulation_parameters.clock_frequency(clock_id))); + } + + /* Estimate the number of configuration clock cycles */ + size_t num_config_clock_cycles = calculate_num_config_clock_cycles(config_protocol.type(), + apply_fast_configuration, + bit_value_to_skip, + bitstream_manager, + fabric_bitstream); + + /* Generate stimuli for general control signals */ + print_verilog_top_testbench_generic_stimulus(fp, + simulation_parameters, + num_config_clock_cycles, + prog_clock_period, + default_op_clock_period, + VERILOG_SIM_TIMESCALE); + + /* Generate stimuli for programming interface */ + print_verilog_top_testbench_configuration_protocol_stimulus(fp, + config_protocol.type(), + module_manager, top_module, + prog_clock_period, + VERILOG_SIM_TIMESCALE); + + /* Identify the stimulus for global reset/set for programming purpose: + * - If only reset port is seen we turn on Reset + * - If only set port is seen we turn on Reset + * - If both reset and set port is defined, + * we pick the one which is consistent with the bit value to be skipped + */ + bool active_global_prog_reset = false; + bool active_global_prog_set = false; + + if (!global_prog_reset_ports.empty()) { + active_global_prog_reset = true; + } + + if (!global_prog_set_ports.empty()) { + active_global_prog_set = true; + } + + /* Ensure that at most only one of the two switches is activated */ + if ( (true == active_global_prog_reset) + && (true == active_global_prog_set) ) { + /* If we will skip logic '0', we will activate programming reset */ + active_global_prog_reset = !bit_value_to_skip; + /* If we will skip logic '1', we will activate programming set */ + active_global_prog_set = bit_value_to_skip; + } + + /* Generate stimuli for global ports or connect them to existed signals */ + print_verilog_top_testbench_global_ports_stimuli(fp, + module_manager, top_module, + pin_constraints, + global_ports, + simulation_parameters, + active_global_prog_reset, + active_global_prog_set); + + /* Instanciate FPGA top-level module */ + print_verilog_testbench_fpga_instance(fp, module_manager, top_module, + std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), + explicit_port_mapping); + + /* Connect I/Os to benchmark I/Os or constant driver */ + print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module, + atom_ctx, place_ctx, io_location_map, + netlist_annotation, + std::string(), + std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), + (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); + + /* Instanciate input benchmark */ + print_verilog_top_testbench_benchmark_instance(fp, + circuit_name, + atom_ctx, + netlist_annotation, + explicit_port_mapping); + + /* Print tasks used for loading bitstreams */ + print_verilog_top_testbench_load_bitstream_task(fp, + config_protocol.type(), + module_manager, top_module); + + /* load bitstream to FPGA fabric in a configuration phase */ + print_verilog_full_testbench_bitstream(fp, + bitstream_file, + config_protocol.type(), + apply_fast_configuration, + bit_value_to_skip, + module_manager, top_module, + bitstream_manager, fabric_bitstream); + + /* Add signal initialization: + * Bypass writing codes to files due to the autogenerated codes are very large. + */ + if (true == options.include_signal_init()) { + print_verilog_testbench_signal_initialization(fp, + std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), + circuit_lib, + module_manager, + top_module); + } + + + /* Add stimuli for reset, set, clock and iopad signals */ + print_verilog_top_testbench_reset_stimuli(fp, + atom_ctx, + netlist_annotation, + module_manager, + global_ports, + pin_constraints, + clock_port_names); + print_verilog_testbench_random_stimuli(fp, atom_ctx, + netlist_annotation, + module_manager, + global_ports, + pin_constraints, + clock_port_names, + std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), + std::vector(1, BasicPort(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1))); + + /* Add output autocheck */ + print_verilog_testbench_check(fp, + std::string(AUTOCHECKED_SIMULATION_FLAG), + std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), + std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), + std::string(TOP_TESTBENCH_ERROR_COUNTER), + atom_ctx, + netlist_annotation, + clock_port_names, + std::string(TOP_TB_OP_CLOCK_PORT_NAME)); + + /* Add autocheck for configuration phase */ + print_verilog_top_testbench_check(fp, + std::string(AUTOCHECKED_SIMULATION_FLAG), + std::string(TOP_TB_CONFIG_DONE_PORT_NAME), + std::string(TOP_TESTBENCH_ERROR_COUNTER)); + + /* Find simulation time */ + float simulation_time = find_simulation_time_period(VERILOG_SIM_TIMESCALE, + num_config_clock_cycles, + 1./simulation_parameters.programming_clock_frequency(), + simulation_parameters.num_clock_cycles(), + 1./simulation_parameters.default_operating_clock_frequency()); + + + /* Add Icarus requirement: + * Always ceil the simulation time so that we test a sufficient length of period!!! + */ + print_verilog_timeout_and_vcd(fp, + std::string(ICARUS_SIMULATOR_FLAG), + std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)), + std::string(circuit_name + std::string("_formal.vcd")), + std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), + std::string(TOP_TESTBENCH_ERROR_COUNTER), + std::ceil(simulation_time)); + + + /* Testbench ends*/ + print_verilog_module_end(fp, std::string(circuit_name) + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)); + + /* Close the file stream */ + fp.close(); + + return 0; +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.h b/openfpga/src/fpga_verilog/verilog_top_testbench.h index ca36d61e9..1704d0dd5 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.h +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.h @@ -42,6 +42,23 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, const SimulationSetting& simulation_parameters, const VerilogTestbenchOption& options); +int print_verilog_full_testbench(const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream, + const CircuitLibrary& circuit_lib, + const ConfigProtocol& config_protocol, + const FabricGlobalPortInfo& global_ports, + const AtomContext& atom_ctx, + const PlacementContext& place_ctx, + const PinConstraints& pin_constraints, + const std::string& bitstream_file, + const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& circuit_name, + const std::string& verilog_fname, + const SimulationSetting& simulation_parameters, + const VerilogTestbenchOption& options); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp index fbf69c16d..9a55968b0 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp @@ -724,14 +724,20 @@ std::string generate_verilog_constant_values(const std::vector& const_va * Generate a verilog port with a deposite of constant values ********************************************************************/ std::string generate_verilog_port_constant_values(const BasicPort& output_port, - const std::vector& const_values) { + const std::vector& const_values, + const bool& is_register) { std::string port_str; /* Must check: the port width matches */ VTR_ASSERT( const_values.size() == output_port.get_width() ); port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port); - port_str += " = "; + if (is_register) { + port_str += " <= "; + } else { + VTR_ASSERT_SAFE(!is_register); + port_str += " = "; + } port_str += generate_verilog_constant_values(const_values); return port_str; } diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.h b/openfpga/src/fpga_verilog/verilog_writer_utils.h index 61f13385e..09a5dba7a 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.h +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.h @@ -108,7 +108,8 @@ std::string generate_verilog_constant_values(const std::vector& const_va const bool& short_constant = true); std::string generate_verilog_port_constant_values(const BasicPort& output_port, - const std::vector& const_values); + const std::vector& const_values, + const bool& is_register = false); void print_verilog_wire_constant_values(std::fstream& fp, const BasicPort& output_port, From 51ca62a464a7315353927027281cf2f465435426 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 15:48:59 -0600 Subject: [PATCH 107/164] [Script] Add example script for write_full_testbench command --- ...ite_full_testbench_example_script.openfpga | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga new file mode 100644 index 000000000..6764208e8 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga @@ -0,0 +1,74 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route + +# Read OpenFPGA architecture definition +read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} + +# Read OpenFPGA simulation settings +read_openfpga_simulation_setting -f ${OPENFPGA_SIM_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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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 + +# Write the fabric hierarchy of module graph to a file +# This is used by hierarchical PnR flows +write_fabric_hierarchy --file ./fabric_hierarchy.txt + +# 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 --write_file fabric_independent_bitstream.xml + +# Build fabric-dependent bitstream +build_fabric_bitstream --verbose + +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text + +# Write the Verilog netlist for FPGA fabric +# - Enable the use of explicit port mapping in Verilog netlist +write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose + +# Write the Verilog testbench for FPGA fabric +# - We suggest the use of same output directory as fabric Verilog netlists +# - Must specify the reference benchmark file if you want to output any testbenches +# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA +# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase +# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit + +# Write the SDC files for PnR backend +# - Turn on every options here +write_pnr_sdc --file ./SDC + +# Write SDC to disable timing for configure ports +write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc + +# Write the SDC to run timing analysis for a mapped FPGA fabric +write_analysis_sdc --file ./SDC_analysis + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory From 1e9f6eb4391644e17f25c12204dbac74bfb09267 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 15:53:27 -0600 Subject: [PATCH 108/164] [Test] update configuration chain test to use new testbench --- .../full_testbench/configuration_chain/config/task.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf index 7ae2d1334..11f45da04 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf @@ -16,7 +16,7 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml From 9bcaa820ae850b785f21ba5b4f0d12749041f254 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 16:18:07 -0600 Subject: [PATCH 109/164] [Doc] Update documentation for the new command 'write_full_testbench' --- .../manual/file_formats/fabric_bitstream.rst | 2 +- .../fpga_verilog_commands.rst | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/file_formats/fabric_bitstream.rst b/docs/source/manual/file_formats/fabric_bitstream.rst index 11faca959..92e11f24e 100644 --- a/docs/source/manual/file_formats/fabric_bitstream.rst +++ b/docs/source/manual/file_formats/fabric_bitstream.rst @@ -5,7 +5,7 @@ Fabric-dependent Bitstream .. _file_formats_fabric_bitstream_plain_text: -Plain text (.txt) +Plain text (.bit) ~~~~~~~~~~~~~~~~~ This file format is designed to be directly loaded to an FPGA fabric. diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index 11c64b45f..c22d5aa73 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -87,3 +87,39 @@ write_verilog_testbench .. option:: --explicit_port_mapping Use explicit port mapping when writing the Verilog netlists + +write_full_testbench +~~~~~~~~~~~~~~~~~~~~~~~ + + Write the full testbench for FPGA fabric in Verilog format + + .. option:: --file or -f + + The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + + .. option:: --bitstream + + The bitstream file to be loaded to the full testbench, which should be in the same file format that OpenFPGA can outputs (See detailes in :ref:`file_formats_fabric_bitstream_plain_text`). For example, ``--bitstream and2.bit`` + + .. option:: --fabric_netlist_file_path + + Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v`` + + .. option:: --reference_benchmark_file_path + + Must specify the reference benchmark Verilog file if you want to output any testbenches. For example, ``--reference_benchmark_file_path /temp/benchmark/counter_post_synthesis.v`` + + .. option:: --pin_constraints_file or -pcf + + Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` + Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. + + .. option:: --explicit_port_mapping + + Use explicit port mapping when writing the Verilog netlists + + .. option:: --include_signal_init + + Output signal initialization to Verilog testbench to smooth convergence in HDL simulation + + From b83d8826fb87739b464401979d15937199175799 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 16:54:13 -0600 Subject: [PATCH 110/164] [Doc] Update documentation on the testbench organization/waveforms --- .../figures/full_testbench_block_diagram.svg | 102 + .../figures/full_testbench_waveform.svg | 2160 +++++++++++++++++ .../verilog_testbench_organization.png | Bin 290654 -> 0 bytes docs/source/manual/fpga_verilog/testbench.rst | 20 +- 4 files changed, 2276 insertions(+), 6 deletions(-) create mode 100644 docs/source/manual/fpga_verilog/figures/full_testbench_block_diagram.svg create mode 100644 docs/source/manual/fpga_verilog/figures/full_testbench_waveform.svg delete mode 100644 docs/source/manual/fpga_verilog/figures/verilog_testbench_organization.png diff --git a/docs/source/manual/fpga_verilog/figures/full_testbench_block_diagram.svg b/docs/source/manual/fpga_verilog/figures/full_testbench_block_diagram.svg new file mode 100644 index 000000000..4e528898b --- /dev/null +++ b/docs/source/manual/fpga_verilog/figures/full_testbench_block_diagram.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-06-03 22:42:19 +0000 + + block_diagram + + + Layer 1 + + + + + Bitstream file + + + + + + + + block_diagram + + + + + FPGA + Fabric + + + + + + + User’s + Design + + + + + + + Output + Checker + + + + + Input stimulus + + + + + + + + + + + + + + Number of + errors + + + + + FPGA output vectors + + + + + Expected output vectors + + + + + + + + + + + diff --git a/docs/source/manual/fpga_verilog/figures/full_testbench_waveform.svg b/docs/source/manual/fpga_verilog/figures/full_testbench_waveform.svg new file mode 100644 index 000000000..3d8e8acb3 --- /dev/null +++ b/docs/source/manual/fpga_verilog/figures/full_testbench_waveform.svg @@ -0,0 +1,2160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-06-03 22:42:19 +0000 + + waveform + + + Layer 1 + + + Operating Phase + + + + + Configuration Phase + + + + + + + + + + + + + + + + waves_0 + + lanes_0 + + gmarks_0 + + + gmark_0_0 + + + + gmark_1_0 + + + + gmark_2_0 + + + + gmark_3_0 + + + + gmark_4_0 + + + + gmark_5_0 + + + + gmark_6_0 + + + + gmark_7_0 + + + + gmark_8_0 + + + + gmark_9_0 + + + + gmark_10_0 + + + + gmark_11_0 + + + + gmark_12_0 + + + + + + Text + + 0 + + + + Text + + 1 + + + + Text + + 2 + + + + Text + + 3 + + + + Text + + 4 + + + + Text + + 5 + + + + Text + + 6 + + + + Text + + 7 + + + + Text + + 8 + + + + Text + + 9 + + + + Text + + 10 + + + + Text + + 11 + + + + Text + + 12 + + + + + + wavelane_0_0 + + Text + + prog_clk + + + + wavelane_draw_0_0 + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + + + wavelane_1_0 + + Text + + prog_rst + + + + wavelane_draw_1_0 + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 1m0 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + + + wavelane_2_0 + + Text + + clk + + + + wavelane_draw_2_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0m0 + + + + + + 000 + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + Pclk + + + + + + + + + nclk + + + + + + + + wavelane_3_0 + + Text + + rst + + + + wavelane_draw_3_0 + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 111 + + + + + + 1m0 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + + + wavelane_4_0 + + Text + + prog_input + + + + wavelane_draw_4_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0mv-3 + + + + + + + + + + + + vvv-3 + + + + + + + + + + + + vvv-3 + + + + + + + + + + + + vvv-3 + + + + + + + + + + + + vvv-3 + + + + + + + + + + + + vvv-3 + + + + + + + + + + + + vm0-3 + + + + + + + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + Text + + Bitstream + + + + + + wavelane_5_0 + + Text + + Input stimulus + + + + wavelane_draw_5_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0mv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + vvv-4 + + + + + + + + + + + + Text + + Random vectors + + + + + + wavelane_6_0 + + Text + + FPGA outputs + + + + wavelane_draw_6_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0mv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + Text + + FPGA stimulis response + + + + + + wavelane_7_0 + + Text + + Expected outputs + + + + wavelane_draw_7_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0mv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + vvv-5 + + + + + + + + + + + + Text + + Benchmark stimulis response + + + + + + wavelane_8_0 + + Text + + Errors + + + + wavelane_draw_8_0 + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 000 + + + + + + 0mv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + vvv-6 + + + + + + + + + + + + Text + + Number of errors + + + + + + wavegaps_0 + + wavegap_0_0 + + gap + + + + + + + + + + + + + wavegap_1_0 + + gap + + + + + + + + + + + + + wavegap_2_0 + + gap + + + + + + + + + + + + + wavegap_3_0 + + gap + + + + + + + + + + + + + wavegap_4_0 + + gap + + + + + + + + + + + + + wavegap_5_0 + + gap + + + + + + + + + + + + + wavegap_6_0 + + gap + + + + + + + + + + + + + wavegap_7_0 + + gap + + + + + + + + + + + + + wavegap_8_0 + + gap + + + + + + + + + + + + + + + + + diff --git a/docs/source/manual/fpga_verilog/figures/verilog_testbench_organization.png b/docs/source/manual/fpga_verilog/figures/verilog_testbench_organization.png deleted file mode 100644 index 06f6d58fa5f4d7ab37289cb91448092464ec2102..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290654 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

`})bP1+w#SIC%HU&g@-*z!u(Eri^gelzgge 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 j6~jZCOrkEfVPS({5%EAf{gH%ddl-7&W11k%9k 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�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

@aNoW@T&Jdq>cpzE8fDN=8DId@hYOc)g`@ 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 z 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

v(0U&o?1}({jF{6#m!YQ{?}H zNrqxcIk{G2Nd-u%udlDm6>RuGF+3YJ|5%OdzstMdPF5@vDmm|W#P;FgZKQs*_=dI3 zZMrCyPC!(@4%zwY+~-wv@Et1eJB)NGdpiGHNB(n{H?6n;e$cD_dPiOXd2 zH{}7YLjJfZ5wQMZbkr)w+QBLB?e7f9c~ZW;6%pupiaGwTtr@Yu*Uz9R?M{?jGtusw zV;v5RbQWsZU|kMz(A4^z>kMgq8g?fP@Q1vh5rEFnip#`fgz~Z}xfw>j>SdZh%roU-Z-XKodIPg%>1L>@@a$qQo>0G5} zKEh^FOskY)A&=4`Tbb7d3J9%H~7D zSY_$gu;;_-=Dz1YC5HmzKNmvlh2u?=9FZ7O@&Lqi#c)?TlQ^aAp#uPbhdbadu9i_fP#Sk569axRt zmUJ)}<4;1ENdQhdE-Sw<)OE?p%rT57Y}81|#yR~vAnW$osUyB$;-`tV8qY8W8`sM_x}9|dy2V7Y4}j2n6_Hhv6c1tSu+FIUi5ek z{rc8EA5T}jxi5KBdghSBntE>`5-f$0hZ8{r?4{nZrU&;6dlMQ(Xob8T%y;c2yzU@KQ)JZuBhYB@DzY zH4G=Ww$%2|FXA`%!fUXQ)PF9MhX4J;_iFKb#3G(_b~{mcd(en03BY&+hXY|I$R)|v z<0eI|mmc%evef3_znBF*EOc=Hm2Lxvf|xyG@Eo=mXNtyG+rG`vbAtBo5c7<|jOhA*_GG3j?cGFkZph z8PW340^_aNs*HvM}eK=Vwo&k=s}QqJnj_J<>=tv`QKHkiXI zvMbRtE7J53BSbEw>cVo}pD>F+ghdxpKe!O10C5 zG#Itt-mmTI99s&4hj9?SvGUr9bJH4b()m;^6OXV|3B}$^!||Z(r^f#olQ5fHY|~D0 z7T#No_Qup=T%`f zgeENm13J{{x?IRQ30k-nemp|%CTkPiYG|ece(HKnOy>EtC*DSF-o@E98^NoyWo1QH z`6!)m((fbNj=mO^GDNuuX*ptX3FZ!w=)*@3(S8VzUXfdxFx%+(0VD`H$eO2A<_$tw zIzSc}dw6y_Q}F-LHu~@c={ytanCPt_n)qBmNR!VjMy$`KL^;5iRU$*NRWKUjf-GK0 zvLi z&*wXGQ8omb>*9sHu6Se(g|6$qje2z~ot5C1o({;{Za~qUG;y(rgnW+l|+0!CwG>@cz5_-``zp1whk%VGWq=l@faminTI!_#fEbS z*2aYK0Y7aHPt}p2ayg<3-p=+!p1m?LyW()$=b4WgcXGh*VpP{YK}dJ1phc zHr%A61k~tV-R#Sfzny4%*|n4tz&p|+i@Au^63<;fotDTp4YAYfKgTWddUyGGuX7?oxxa&v^Y7$>i1)pTub@2xc!P=m8Pqe_bgDP&=~ zUQiOMexGveHqTErug#XfkE!^ubjW^pS4-)zThZ=nwv-SJi8M~XPPaa?8?%sE70KJ@ zw9@AWJ5#^(4p|(90VRr^1XfmV_9XRIPskUC95n(?SJO9 z(=Tq@zkNhRg%j_7Ir;x=s4?}w4?c4s*tlCd>%F;hg^o9q74JEqeGyZ^or}R6jS{7r zEWfU7C{>sUDKBU(n_GXcYDqcD8~78m@-AkVnkd0zGQns9ZU4*ZOmNnW%tU>LjatMI zKW(LBbCuG3lH>&_0*lYfr8gmdLax(L<;&@xTCCzFalB;}p5x(0KA-Q6S4`92etM~G zczQ`hNXSS~1y9OFQ2ou5SWM-Echrk$IQ~|KAz-=NjLwK8ii`!gbKhGkQFl6BRz1P> z?&9S;ItuO2)WXKc268cq;+FBNy72lq&SAx6O&!M_UhS%31R#K}cnkMzBl;|A?=j^S zk1ox3k-yxun|T`52L6)lSQaT!t!qe1UN1;XB5>)S``Yr?SGxhX+yJkE>IA-8_2giG z1DVg)Q)6U}kk4BoLVBQCGPhD?r`A9-MiIivPe13a${yq_al$WTNH?3kJ^p0!=mFC* z|Doj?^eE0kuZq4!3$A`2;Y&NZC*r$?9255V*pZ@zwU-Y2WkWEd{L5p9ZHBx0lJhOu zLG#oJ^aoZWew^!)Ud2~Twu(zm48h4p-{7HDbJ9+_@*WWI3FJR<3^jq_)A_YnW-wqR z6#2YX-Tb0KT4}6?iIlQ1G6mXl7y3q;OiX8694qfEWD)7gU@s5bD3j~cTaVM`7Fk)z zaleDBWgE?FMLr0HtweGnrkNl@t^1!)PcA1GSI|HSBkPT#z>bRw@>pstYJ?v0F{h-O zx+;@Eqo&Q=0S7G^SMI`1f1KCeVeT~_m!+m<#IJI*^D6xe5}UAAXR#3}jVM-9!nvnt z0WOoFqPKU$9S%g}g}n_<26#b1eGF{$27G>ZCpOXwYV<`Tm1W=73MCA~n8<7{<#5tO zjl`DLzm*ImWCT$Q#a`LX@?V!^)Jv{*xoOAr0XF06R~i+NFORG>Q#`JBnguV#y86t^ zvHg*5xaBl8BQPn6)h{^ZcJh3^PbN(7rlTI~+^j3@H4DwM8-qF%4oVL^xxQSdeQFfD z$ct=;48AFbepuj6IylcHdS!|`qdlVJpq;w2q*2{DjEgguMTu?Pt*@_dIPLPb05h-J zPM0oTk&0xH(@{X}=ll`{?;fV54{16+oL3LGPKn#zt?7#ZA0B15M0r6FMr*e%>yO7&yrEH8eZRzu9cA zCJ%`^ZD4Nd*l2JM_O$luP#G@zK6byoXuEn`xRW&YKBQ)G%+hbM?!)TwKGGwmSBDOE zoHyZMHkt&s)wpyVhQM=xYgu>DlK3*fB@7!KAAP_ix9l~a7{IKm%@Cn5J~`v-cBba< zZQRJd{8>4JQsqxR=riR;Bb6;J9L)7%c%f{bw`8iX3ljCxj4xZ)s56@87_ZoZhGqV; zy?XT_T~XWp zYu8V8TV?jh%tph~DnElGpGq=;uK{of=!{m4yFSjsj*G8Zx9#j~s9#A4#cU;Y*q_{_ z-8=t1+^)`)$5E-O_C=vqQDSnidb3^KSWG2PV!(iy>$4qDk}6J)gK+V^cnJr+W_r?j zzHZ@L8K`ZbAp?QTmIL}a;mLVKOoBsf@2&!x3vJKE>;rUO4R8#r69fE^Og<_rV>;+# zPj40n%fzE7R8f-LB~UoRNqz=?X}jc^AugxW7rEfsXnY>VSO}@nIzWmzZ&)&76&&n* zqhBtBZ5jtt<^dkP))PqH=U|~J$UT<)PJ#(XyiV~{oIuj*$cTrv(nF_!zUI0X9n$pz zUmz-{vHQRRh(lV%EMv9Yb}-3WdY;F;659~dH=>)~R|haTQxN>Fe;du~Xpb&t0~N02 z*pQ1QS*{D)f++YarNe2726HnY6f<^T(x~Gb@S4DfBSEDsv_853yYo99#oIKDOog1=x=@3ici9_@|BK? zNaHIF)Hj?5kOQ#>sA+a%9m;Gq#BgT8h*%5{Iih5#2lka)8uP#l=O& zpZsUBi)SV3iNACn`Na@%g!Bu_edLg(WYe0CvHY^5$&bcl2av@s3Nog}t2Rm_r&%dQqmLJ4NC=%`)?AEtf<1f{zYl>uQUIE^>&8_o;6h1T0AdGE%H_h!=lw;pnAeN0fb*V0J%lBen#36;khnH!3_(Oi;W-rUHr~6hn7T90}(= zJ(1>ezb6(VT1C)>bHlIvV%m1!%x11~1gNh}rV1zfzgN6(R_FbWn_80IRK~0+tMjvy zBMO(=n;*M9)Kk@Nrr42Inn1ovXhnH==276-@KqXdRe8$sx49#}=^{<8(the5-zoY;aZq zcO1CJ8voDGVCTLcPyz$MOB)+Gc_cJ(&nbjviQYvl2BfF|*TB>mVY)v|5A8?zL5We; z4?PZR&hJy`pO|v`sl}w=V^B+UnXN`I<-Xh8;z-kAPrMs2`d9{h{&DZ8k(kg;JF3X1 zhJs@j-aq=5i!oZlKlBgPFmgh5L?GHSOlbPnc=${=qA#i{3)f0vqv=hJQvEY?(PK-AMsrR5wt zoE~qZctKf+ttMpqdK;vT`ytx=z>Jod$O%A!^Dcp*NZRc9Ni)rOk;isd!rlUy255P@ zyOI!kRbH&pgf?gK5av;|5W0 zvY6hn`+$V@=EtOwvU0^WqNmRs*Mk0tRRWe}g6*J4*JZsmEuwh7GG|qEG9RFHryE8m z@NFLo$@=Z@i;j^6^E>mC(=L%`DsDy#q@2l8&DMeDv1p>Yn#k#?=jnegzx4ki1lkNY zsWFGitcaI=``h=IKjLif&Zq4dyJ%iJV*0bL3xBm*!yYT?=dY@Dn<^nppc9D#-~B&7 z)W5)rIK$WIHL`gNMy2ja6ib*MlG`OA$lx@J&@qbxEr>&@>R^5vrxt@|F)>cUNGeg= zXQ6VwgE;hO>k=N93rFKRtspCq^}!W}kGHo0EE=4hZmq7K)F*g}^Ub;tP;-c6?ftM_ z&{9?9SSYy+l&9#v4{(yeR@&IT2>BkPZ)}`v(CjQanIqJ;)s`>;{FrbsQz*}Z;bSVE zokgGF$2>JOI5?MKGUo#dYN(tyl?rOefFOuPHw1CfI6evYT?sxH)@jxI`t#*Rxaf*p z#7W!|IaCvnncLQM82LIE;ltcM#&Pp0wM z_|r_bgdN#CNqHahg|&`aXHR!J1p{j=6M`_eEp@_(U27m+@1z!ooVgtTChCgpe1GQ_ zP7R>Jjlm>pPkcTd4q8c|7TGBr!%6L&riTe_T&S^#j<`;J(t*M)zI_%^_jf+D$?v_? zCdbn5(dn~|izsoNT(=Dw9u8g|=V?Lph39~O9P`}PVw~j}MDS*KjtzOf0kH+{ zkz}5{dASaLj*GFAQ`_0oHc4|XcEhpir5ilG(n6OJ1fc{%sr(!S28YbldVu6s+{ zsF4onY0wC~?L(pxrmzW8Hi?u4r)3~(Ykf{WCQ``55!Lg~`I7+UjF0i`ZFw&r*S?)) zC#)*pvY=a~+L-TfbF=%EM3ZYbY3h2EQS;-|--95QqmRN=ne%a0g*$5vrK4Cq0$iiF z$+3qC#;9ZE(qZ_m-8*oMvd-x(0Ut0; zPDqJ}zQ{>w1bW?^x*l=i#!LsV9Idqgm1LWN2TG-tm9Aw&pUDJx{wG2F7hR;lfrsgQ zLqlx1I>#g_L@0~gR!Si~3CH*cXx@cth$yBY8bcKTb>5 zHZFv6&wOrg(ONQOB+ApqI63Xg1Db%Z@LrWKUsNOOQe22Zq-?)zS@f4jM%#lxIQTT&;7xNh=9Yc;M|!>|m_((3BPsj9UH(#iP`wI12;X$V+{c1ea>I^4%l&Z>r5&-IMlnYm$P%!o#$lO;MQ!enLN2xj$$)! zBxeBgxHlNbz1DiBl$P=|>yWeEJiH*70?v|xx40pOz>B_6GN|G={-bcJ*gK{7+e;!P zot<-2KlSMO%xd-U5*vsF->C0_3x*dcGqT1wkl{J=;CukDf>|do*Kz6^^5sUG@W%p< zkJfasJhHZUYa*%3qDcs8m!=vJ;A{0OE!aWeEOCBp`=Ta4J?a2@85u6;Z;D8Z3AKa= zD2cV8$1m!&QkP1p_?OBdxol7!uym77Cv2IBhQC;^jK_I}Y7@#OSsq|ol{LP5bYvUG zlco=ID91=lqH0;*=w|!@8Q}4BPDd8|c3=_T~MEWBxw}91byd z#C;RLpef@d5mn?PKsrBW-DfWLI9-w3pA<^RL!J^F*PeHTnhPi|ga#H`+DEdsjW{-9 z^#klx)4l8-!$^)hcHAhZW@%iP6fMLmd3bh;L0V$tt4|Qh7W~is;Am|ZdiN1Y+Risz zJCNVQ6(}4*7~R3fgKTA@B{h0z&>0F z*pmNXkp{Cq=cYR%D!q%1E_Pde!Ty*qQ`LQsHMAvELZT`_CgN+*+SLr-K@BI6iy+R1e%I<$5yT7aq)PI;KK@0o zjwKkcips2F0#Jg_@7$keg2zi|V8BU1u?ojlX50lyL)QHWnpTEvU7$b09GLOx+t29W zthd|A#I9sdG?c_hp+#Ax=l%WdiBU;%Oh+C3$>~H2lB-et@eJ#!nlXGOZ+n$ zbSvQ;%dSFlhsEThk%c)9-ybZ+2^h!NDxqI92qMD=!{ds?AF#%ag^VS!+@iJ|K;&7M zFins+?Q!EuC@JYi{6saA5%Uh!&2aXaiojd$pq*wj-fRkILa1etzEivL25?K*13Wy7 zqP(#9ab)Ou`T&1pHR2ySa7+q^t07@Z!%s0iapw}EU8ja=eVC>%p7SO5^YnU#)cVA3|~t_M)|AptJdhr42@IHy99b)*n%mp&)0{J#ewYy*K{^c zrOrj2`L>-XgpfY#hb$sTFd)ULK~(U7^>Tf75pO9S#^9^4igk;_hgbCU@0 zvHU-6U3FO0Tenu>5Q5Sj15$!?N=rzG-7wNH zBVG5$x!<|pckgrOk9nr{vupNVYrXHg*G}_iU+d+wOc>Ura38oIl87hrwTkAWdvenZ|3&uaKsS)wv z`9eP6bQ_dCd&$yHtX0}o7Z zcvd~UJO2an46T1etiD2W*F1x3^969b*0;R&Mp6TwtseCc9uBKMCd*k<@?T7L@hlKraQ zY7QAb%X{~hg|Fi^t%$20RtNY%A>!3fRh&*?&x}I5SxWRL-2zzc#1@uLFTM~)hk1djG?2m*rS1OM?KS+civQ+gWi=Rh2z0j;= zNp7?vGuu4$D`8&Q2MAl!&zK+N-!~Jkrn5$h=F)SrQdoK|jFNqKf)Bki7#Yody)8~O z3ozP+sde!PE%&guc}QTB!XA!^0NgAVVez(QRL$;@`$HGCP)3NUtZqJH^DkguCm()J zfbq!Oztb?a-3^#fIpmD~USGp`qwX0M(QA+XhMSAdfw-=KWqYi+_VB>4FK;hP%D5Bl z*vc7wLz)1vHrDgo7QkOkKij^U)(u!2VCVC)Xsb4COXg%QvM~yWlVO&==I#1mJ47VL z^BR4Glf;#2oWvffmF|V?^NX$@K+rrgF{LZR?O@MRg z&$_=8IAisET^njb8WZ!hEiV0q5K&1< z3H@Q8QaBJM?xv)q?8)4H{`6#R&E%NHe7+`41lygwEa}RiD4?f;2XuGRDeR#@ov~!P zWW!WEZyVcT@GE~iK^LsA_N1vGN7t?HX#|k*X44MR+VbIdfSOXAt$$Yq>dswYTJLmm ze*{7mN~ZP4(ZH-@+fa6Qxxmt%ClQDO@6ke@%*Xz~wirr=9?ig%5Ge*q|KLw8zG++e z@QCWjDAtgEEAXy73G&-kM<##(>Y^)+Rn-F(6YF6&r2SF`k5eHu6#e()=~G$LRntaL z>k|D^eS#Xb%*1}VV0Uz=Zj|%Q~npv4X>1MA|<%2G8w zI;B~ra|f{kNc1s&k^|bBR9Ym*frj|YPT6SRM9hON<$GviCY+nt)3B})7Hq*QULE&J zi`lTSDwQC`trI!}b<$wva72{SO8E<~Vx|6y@ph4iAeYFzC&mIjPq1T%TrXy&`?_)x-UH-`gAf@11HGh_`}daU)4CR^>DCSp-A+z$Nbc+OW=CBQEh9;8 zpo2tcD+`7bk$Cuw2}5wv*)4^4MxG+|1Sp6SY{!u?{eizd2k`PVnO5+bz*dKM4Zg$mzM z1XN;+7k7a@ig6&E2AGulRNo`o1zG##lHGA#zoqfr9D6mY=B5q9)zWZv1u|a@?dr9~ zVhc={$*^;Mp10~BZWRW-_I*$ zrMw;;ZcL}7F4;a?8s`mQie$xb_LZY!o{2c@f)Lgyaf@@~-KZglCa}AfB@;=Vw*dkr zTicnTA|{$FagRdCC#+%c$%jS{)7s9}=(b8z{6%9_R#mO>&~8oalJ>K7(}%*}fcp~s zPFiuOT>SMl3-Qwx#5L9W499$qAjY49S~2rO!^yNsn=%i&uy`dWKpeh*wgMW}{ctNv zJ=Aol*vuqVf|%|y6S8;Gs{BUvDw8jgqU^~mvt&LVs~vo||JxOq!o$n=mGat3&X)Ai zLXMG&d;X5!S!kKfxwx)IJoQ5tm^J&3n#;afdh5K{I-dZQfigV!H%<4C1?$5Jog#x0 z>&C0azy5aLURcc?it{+MUfvw{EbAmb`X;^5{5RVQ`;w56`^X*TQ z7$Xt%P`ok@>s2Eo6kRj7HcyCt{J<@fH-RXH1^;r4?P{T9_4CP}Zt7<-4@>xlNgkA7 zZ8jD+jFH5D0Bw`WIXGM-pNG>^mw0X=Obo_o*3`F+EtZ03%5-oEN=6xp3ICOx?fsiIvG&SRz!NZQ@r_VCCD zo`uPAU2pTplQDwU3#xm4#tU zHgU=ZSDm+&PdiSGB4G%p?`PR<;#NQzK;h;gQfI9#t#O+K+f=?CtqOP-{u&i=5-rUeBy{Nmyw}AHh7C({cHALvmjP z_IvW@2mm-E%oG#uKg-7{6c6#6;}oDdQDS?3YY&z=2xSfbbX*!mJbS9ce)?+cLz_5W z-0y6B@OBS(hF?sZ%)y2&C;h!LP;Ko8kk87J2XQxxQ?Fi$|6HS2>CRN8!p!U}%y^cG zLq{_AshfP1jiwB`>(f+e|WBTjX@R#2A1!l9xMMdL6PVZwMRau6QSvj@X2gG6`jNJ@u)x=Z(6>N;L@QV~) zCmePAtAD5dY^3nM26Sig_{z%U+fP~b(^Ix^F|D(vHa8U}etWe~(5qAzIVFb~s0;IF z`HESv%u>kg;ti3>dP|+KWM2Adcb9U}VVoPaCL|mPxw=U14t|B1HLL`R`6uPa#XX#E z@_|8kZCL%zFD`5y9QqQH;$zpCPYKto^(kn2WlXaEGl2TLrA)zi)Q8k<0;;o*R7AKl z)W@@%nw_s6WO-{+h-|4>25W_n>({@PC!_5Mw2+k9r!R%z4C$A5*zs?^HXwz|eR+d) zIH2P{bjqn&T3>o(*{6EkEs1NztBcK^iw$R@nT)=Z5^fr-rSLT3`T`;6LC}Z?Ju(?~ zyoM_&DcJzW#1cLN-qNtJ_zZhqWS|)b(Z&RE%4nb)1L5Su^JiS|j(aG2ooCij!Vp|Y zRN))EK}#=nvI?9h(=1D<+Hw$M0>3hVwReb^7YE;(T?UDUmW@jI5ldY?dzxK9i;d!s zmJ<&Na6rpZuT@0ng4)BVJ2orFLk0k}qpOL4tbuP{Mn;>(86N?(=Np~VpR7xSqgAEj zs4pDM3i}8FNkz`CNN+$1xP!%wO<$D%y!zHxOaZR!RE`oB5t*i%c@{M?IM}zel)to3 zbi?3=mZSbQ05Rm=#x+W!rKO$ZPA7V|x(b!uBZt;}sjZ#(E9S#JgYFU@iVNph+D$aaJvKyddz=7m=*=KS2|FwV29VJ)Ww7+c6k6TF ztJF0}a_4{+$P(Up&#}MUa!b)c{mxN^BcPDC4rsX~bdGAt-R;K+c^j%z;<4TvCKs=E zEGEmy%=IAYK%oqdp30*1jnlJM_Y>dU7Y5BD4NuI5BTez0o&*5>eA4N{rhRtQDVx>YhDPqz z`(r!9>ih|-IXgl)tX@~chM(uturOB3EuhQr?-iDWvEpe7zqHNe`!R|PxIA?`L#MUv zB>R$)kr5QNUkB4e5k=Jtf#Pv4vG5OkmE4{+Z7)>9<-0Q-FQmzsH5M|JLTi0akuAJ^b|c>`KJWjXuR1GC?mQ(UCjOdI%c zQGHGT(T2{Rk`E2`e*s-N^q^MUJ68ust#_bqj@FIdib$uM)#r_YZjBq2$1Ysh*^Z^V zN0(6%vgfzM^|CUq0t9J5nriV~o$EM204yq1sQ<~xKy&*#DWq3{NSYNianSYh**S3Dj`DsxL{&CmBc0>Y&&l5S!G3uYi~!@*0V$5}d*;x}?ekDm%rNlivf z*=2#2Z!%Z;WgW2YzEVkl;W}Mq`*Oh_JH4&U4Uj^*0~gkzh{iS|l{w}F9%8H;S}-oW z1!r=gpeCeyIb-wXd-yvrrRw}3x%oQpVen=}Eqln_g_^z}oxCVdMF-jjKsyo-*<;v?>hbcgk3UEDYC z6|A-=%hA&#!G^%WyPSL9h$#sv2}C~?{PxSn9jOyKqeh8xtZvV~c6k{7jC}F8U;2!@ zo>NWEC}u7>w}E^-8p!Dorx>_dn3g?*2C{d=4`qopsZ7*#qvYmK(F1&vpAUhPJ8t(i zCy5pB? z#>6}*BzH#+fl{US8wJ)I+xXw?6wrsPHC^xis5H`)d40WNPr1;0)McF&7y}6iY6=YS z14#qSg>MX8$e*znelB|E%?`S{s%H!eaJ2j*Rk5r2+Wu2b!;4vlZZUrgJWOXD60|x8 zOwbt}!-fKo$VN{JeZ@?*hZW;HxOi%;`Pav1KoXKbbW)yY`H781sCRd!3xF2U-MJe~ ze;AtihGH2@RQjMDi#K0GSvm4z1zP~oB4j_@XAPC!2&@m>u?Bgxy-BpMWv8IKEFsTf2GHH0=k^y8^GPWf_^&xJryBkW)O>_I37kGF}zwjwo-aQIt&=EHTgCz^C0b)TY ztG(CI$EPkE@UYDLj-CoR2Tyn5vUb?>!`kbj*#I)eh~}Ox1#L`4^V9X^%ozYlE*aH0 z5~V*l*z-xjmYpiXh_s$4mH6!wSR`iD(EG)`h(qSATbu zGW0!z7~#Eiby>{H$t`GSqvw%!i4FiIHfHubzko_a*;w{aML14~36LCZ?7TlJSvIS= zBm33Od#CVEw2YM)IOc7yM4m^cTcM18be5=#d%+{rs6%;7(@6MOC2&JjhOQ!}to|3| zfC&r9y{GXm=rc6HHPSX=C|Z1M+N)_rx;p%`cXJ(}J72LVg#P7O+D#mVdk=Pb*^7*it10<<6LvY_b7K^I zL1w8ss}gLSk)HmY&(IO*`SW1SP`pl!fre@$`er2SiF7v zW_xGH6Q)67m&Ea_+O1_cjeF85>lffv)MnI%`^8qfnU-R$lpBp0e49=rg$gFneq*;9 zqN{KK5Fq7?pR@72Otm&d?Q8K>36g5`8kiJ>gk9&}(sFDvr4cW=*=n z8uMCY&}|EfhSZQi&uAn|+1T;uWj$D3)Z_AGvX2|h|33liKhCTXeJJUbj(@MRYAs1vL8$V|obFTX%k#Up`pixmXJc8T zuN`yWA4b(SIz6V%iGL(g%bS@+1WriVnY0I-zdXD$s^D@UHZW?wGI_87gt!W~y6Nwl zywflc9I39vUblaBo-ojoO9E|RV0q%g@TeVRrl>bG4r6X2Yn_VwI-qbk>4l-HS9#v4_+GoyNQlZh{LggO$wtS+Y)4ezma6riA8yZ{# zTIQD0H96tIHsQLj(SV00_S8;Kt>VOANy;v~aozX?4=@4!+Ljj`AJ2_VblC&o4DmhZ zlN8cz;|eI=HXamTUUI`(Lt$0LR_hUqd8?CNhEM#wnJLd13$$|O&CJn#e+1l| z@|0KKJ-cQfWL}5~-(I5gB3!20FQ{S2S2Vr+Chl5ej zo9T}MriLcGd49ju(MuOHEh~bFpsxKj(Dj)sVm0IjiKq)}sSCDwKiR_~B&t4N>6b#o zoCVIPG~S9EDjmd|Ztjm6()Do{fk~LuAqp3&IVZeo*gs1a&DHzgt>QKB_mnnY&_FH( z|8es>C$o&-Tprahb`K63h+KFAIzI*%ma+Rx5>7%>h>~KJ?RK0kY|C>CeK&kNX9ZMaBl(r%jAR8_Y*!O#vI_U6fR@GsO`hWR>fJD~d-t!Z8ZyBFqvjr-Ng2SEQYO&KUy zePKEHk?_?Wrvn3%&8V(QHH|-Fp%9>h>T9G1bZ;1Ny%L4#3K;KEWPyvLrkmBeZ5qIa zlCalJn?dLlzCiWYEr-DkuP@}zYKcOWTG3~17RjD}Tojpof1yImGmrOE;j=`DE6W>} z>61-MOdZ&>`CyPhP)M}$vpx9Y=k)%~yi>H1@4|WIo$im?&QiLcs{K+vBHUq9&1%mF zf$G@@T7W+gMWuWW*v=_OBGucLf#v7ePsSP_krXfSCvA;fKDIn{I+IZSE}-=PX{fVB z;Uhbsbke#7qPy9DBs3rb=}u;c@VQRo`*_f=!v%v4>8K9y6>&#;naZX@L(pWRjEJZx zpz_;({zq}(5BJgN)9@2LsiL?-bK_pTIWJ5kV2xN*i0HOW))!1=GpnmW^*5Gwg)x1` z5nzI<-~#3G9u{fdX^UFF6h_X!SJH9GQ0X5PU=*S1aIl9otg2khrnTZ?KfWiZN!~D_ zatAB#3`MigbMpSkx*@7?qwY|p8(2C241xeK2kG1=ABRfyyY72gPz*uTcH_(iuqXNM z+4JSZ%Lc9atoK!-Z2j&0Cm48mI8&~gf>nbtpZErjhZdhI&Ud=|vx~}H> Date: Thu, 3 Jun 2021 17:08:08 -0600 Subject: [PATCH 111/164] [Script] Update write_full_testbench example script to support custom device layout in VPR --- .../write_full_testbench_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga index 6764208e8..2a001ad30 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga @@ -1,6 +1,6 @@ # Run VPR for the 'and' design #--write_rr_graph example_rr_graph.xml -vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route ${OPENFPGA_VPR_DEVICE_LAYOUT} # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} From ebe30fc0703fcd670d2eacf12c07d8522352f09f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 3 Jun 2021 17:08:33 -0600 Subject: [PATCH 112/164] [Test] Deploy write full testbench to multi-head configuration chain test case --- .../full_testbench/configuration_chain/config/task.conf | 1 + .../multi_region_configuration_chain/config/task.conf | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf index 11f45da04..f11b117cd 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf @@ -19,6 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf index eff60d97e..ebc47d3b6 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf @@ -16,10 +16,10 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -openfpga_vpr_device_layout=2x2 +openfpga_vpr_device_layout=--device 2x2 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From adb18d28b8f77538071340cac45d76a3a95de297 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 10:37:28 -0600 Subject: [PATCH 113/164] [Tool] Remove unused arguments --- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index c87073c67..13cfb52af 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1953,7 +1953,6 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp const bool& bit_value_to_skip, const ModuleManager& module_manager, const ModuleId& top_module, - const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream) { /* Validate the file stream */ valid_file_stream(fp); @@ -2078,7 +2077,7 @@ void print_verilog_full_testbench_bitstream(std::fstream& fp, fast_configuration, bit_value_to_skip, module_manager, top_module, - bitstream_manager, fabric_bitstream); + fabric_bitstream); break; case CONFIG_MEM_MEMORY_BANK: break; From 98308133c1f558bc35e973ee2f964861d064f19a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:24:05 -0600 Subject: [PATCH 114/164] [Tool] Add configuration skip capability to top testbench which loads external bitstream file --- .../fpga_verilog/verilog_top_testbench.cpp | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 13cfb52af..fb88e2bbf 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -63,6 +63,8 @@ constexpr char* TOP_TB_BITSTREAM_LENGTH_VARIABLE = "BITSTREAM_LENGTH"; constexpr char* TOP_TB_BITSTREAM_WIDTH_VARIABLE = "BITSTREAM_WIDTH"; constexpr char* TOP_TB_BITSTREAM_MEM_REG_NAME = "bit_mem"; constexpr char* TOP_TB_BITSTREAM_INDEX_REG_NAME = "bit_index"; +constexpr char* TOP_TB_BITSTREAM_ITERATOR_REG_NAME = "ibit"; +constexpr char* TOP_TB_BITSTREAM_SKIP_FLAG_REG_NAME = "skip_bits"; constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb"; @@ -1983,7 +1985,12 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp fp << std::endl; fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; - + + BasicPort bit_skip_reg(TOP_TB_BITSTREAM_SKIP_FLAG_REG_NAME, 1); + print_verilog_comment(fp, "----- Registers used for fast configuration logic -----"); + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << ";" << std::endl; + fp << generate_verilog_port(VERILOG_PORT_REG, bit_skip_reg) << ";" << std::endl; + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); fp << "initial begin" << std::endl; fp << "\t"; @@ -2001,6 +2008,62 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp fp << ";"; fp << std::endl; + std::vector bit_skip_values(bit_skip_reg.get_width(), fast_configuration ? 1 : 0); + fp << "\t"; + fp << generate_verilog_port_constant_values(bit_skip_reg, bit_skip_values, true); + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << "for (" << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << " = 0; "; + fp << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << " < `" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " + 1; "; + fp << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << " = " << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << " + 1)"; + fp << " begin"; + fp << std::endl; + + fp << "\t\t"; + fp << "if ("; + fp << generate_verilog_constant_values(std::vector(fabric_bitstream.num_regions(), bit_value_to_skip)); + fp << " == "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[" << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << "]"; + fp << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t\t\t"; + fp << "if ("; + fp << generate_verilog_constant_values(std::vector(bit_skip_reg.get_width(), 1)); + fp << " == "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, bit_skip_reg) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t\t\t\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " <= "; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1"; + fp << ";" << std::endl; + + fp << "\t\t\t"; + fp << "end"; + fp << std::endl; + + fp << "\t\t"; + fp << "end else begin"; + fp << std::endl; + + fp << "\t\t\t"; + fp << generate_verilog_port_constant_values(bit_skip_reg, std::vector(bit_skip_reg.get_width(), 0), true); + fp << ";" << std::endl; + + fp << "\t\t"; + fp << "end"; + fp << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + fp << "end"; fp << std::endl; From 81048d3698ec78d911239ddbefbc7f1e17c62d64 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:26:39 -0600 Subject: [PATCH 115/164] [Tool] Add option '--fast_configuration' to 'write_full_testbench' command --- openfpga/src/base/openfpga_verilog.cpp | 2 ++ openfpga/src/base/openfpga_verilog_command.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 6a3afdefd..6cfb9879a 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -130,6 +130,7 @@ int write_full_testbench(OpenfpgaContext& openfpga_ctx, CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); + CommandOptionId opt_fast_configuration = cmd.option("fast_configuration"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -141,6 +142,7 @@ int write_full_testbench(OpenfpgaContext& openfpga_ctx, options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); + options.set_fast_configuration(cmd_context.option_enable(cmd, opt_fast_configuration)); options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_print_top_testbench(true); diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 4518c0634..431eac47f 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -155,6 +155,9 @@ ShellCommandId add_openfpga_write_full_testbench_command(openfpga::Shell Date: Fri, 4 Jun 2021 11:28:10 -0600 Subject: [PATCH 116/164] [Script] Update openfpga shell script with fast configuration option --- .../write_full_testbench_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga index 2a001ad30..8a87a0a08 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit ${OPENFPGA_FAST_CONFIGURATION} # Write the SDC files for PnR backend # - Turn on every options here From aa4e1f5f9a228f0c83769f171d3851aaea3cd6d2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:29:43 -0600 Subject: [PATCH 117/164] [Test] Update test case which uses write_full_testbench openfpga shell script --- .../full_testbench/configuration_chain/config/task.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf index f11b117cd..1f818415a 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf @@ -20,6 +20,7 @@ openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scrip openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 2068291de03695a57f1e3d98b14c81024b3c6345 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:32:49 -0600 Subject: [PATCH 118/164] [Test] Now deploy the 'write_full_testbench' openfpga shell script to all the configuration chain test cases --- .../configuration_chain_config_enable_scff/config/task.conf | 4 +++- .../configuration_chain_use_reset/config/task.conf | 4 +++- .../configuration_chain_use_resetb/config/task.conf | 4 +++- .../configuration_chain_use_set/config/task.conf | 4 +++- .../configuration_chain_use_set_reset/config/task.conf | 4 +++- .../configuration_chain_use_setb/config/task.conf | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf index 8fd922fd9..a554331f3 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 1*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_reset/config/task.conf index 9d7ea2d8d..0707f4df2 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_resetb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_resetb/config/task.conf index 4efa817a1..627e7a9ec 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_resetb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_resetb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_resetb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set/config/task.conf index 37a1eb75b..e452a2371 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set_reset/config/task.conf index 94acf3ed8..da327282e 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_set_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_setb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_setb/config/task.conf index d3290d175..1706a4e4e 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_setb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_use_setb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_setb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From ec203d3a5cef7a5408952691ea481aea328b1a5c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:35:23 -0600 Subject: [PATCH 119/164] [Test] Deploy 'write_full_testbench' openfpga shell script to all the fast configuration chain test cases --- .../full_testbench/fast_configuration_chain/config/task.conf | 4 +++- .../fast_configuration_chain_use_set/config/task.conf | 4 +++- .../smart_fast_configuration_chain/config/task.conf | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf index 1967bd2a4..71dbae458 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain_use_set/config/task.conf index 51f2344fe..10d5bfd62 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_chain/config/task.conf index 4fac7ac49..56f90daef 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_chain/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 5f96d440ec63a57bc2ab01dc2dbd332b302c94ae Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 11:48:05 -0600 Subject: [PATCH 120/164] [Test] Deploy 'write_full_testbench' openfpga shell script to multi-headed configuration chain with auto-tuned fast configuration --- .../config/task.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_chain/config/task.conf index ed7db9ae5..a5f85fc2b 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_chain/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_cc_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 059e74b4efda3b04f207d7afcbc6fd0aa8030c99 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 15:17:00 -0600 Subject: [PATCH 121/164] [Doc] Add --fast configuration option to documentation for 'write_full_testbench' --- .../openfpga_commands/fpga_verilog_commands.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index c22d5aa73..8b559c698 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -114,6 +114,12 @@ write_full_testbench Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. + .. option:: --fast_configuration + + Enable fast configuration phase for the top-level testbench in order to reduce runtime of simulations. It is applicable to configuration chain, memory bank and frame-based configuration protocols. For configuration chain, when enabled, the zeros at the head of the bitstream will be skipped. For memory bank and frame-based, when enabled, all the zero configuration bits will be skipped. So ensure that your memory cells can be correctly reset to zero with a reset signal. + + .. note:: If both reset and set ports are defined in the circuit modeling for programming, OpenFPGA will pick the one that will bring largest benefit in speeding up configuration. + .. option:: --explicit_port_mapping Use explicit port mapping when writing the Verilog netlists From 061f83242966a56e98d0b63d198491b01faef403 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 16:23:40 -0600 Subject: [PATCH 122/164] [Tool] Enable fast configuration when writing fabric bitstream --- openfpga/src/base/openfpga_bitstream.cpp | 3 + .../src/base/openfpga_bitstream_command.cpp | 3 + .../src/fpga_bitstream/fast_configuration.cpp | 128 ++++++++++++++++++ .../src/fpga_bitstream/fast_configuration.h | 30 ++++ .../write_text_fabric_bitstream.cpp | 33 ++++- .../write_text_fabric_bitstream.h | 3 + 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 openfpga/src/fpga_bitstream/fast_configuration.cpp create mode 100644 openfpga/src/fpga_bitstream/fast_configuration.h diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp index f4ac33ab5..78e0cbddd 100644 --- a/openfpga/src/base/openfpga_bitstream.cpp +++ b/openfpga/src/base/openfpga_bitstream.cpp @@ -91,6 +91,7 @@ int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_verbose = cmd.option("verbose"); CommandOptionId opt_file = cmd.option("file"); CommandOptionId opt_file_format = cmd.option("format"); + CommandOptionId opt_fast_config = cmd.option("fast_configuration"); /* Write fabric bitstream if required */ int status = CMD_EXEC_SUCCESS; @@ -119,7 +120,9 @@ int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx, status = write_fabric_bitstream_to_text_file(openfpga_ctx.bitstream_manager(), openfpga_ctx.fabric_bitstream(), openfpga_ctx.arch().config_protocol, + openfpga_ctx.fabric_global_port_info(), cmd_context.option_value(cmd, opt_file), + cmd_context.option_enable(cmd, opt_fast_config), cmd_context.option_enable(cmd, opt_verbose)); } diff --git a/openfpga/src/base/openfpga_bitstream_command.cpp b/openfpga/src/base/openfpga_bitstream_command.cpp index 39545d12f..a59d19adf 100644 --- a/openfpga/src/base/openfpga_bitstream_command.cpp +++ b/openfpga/src/base/openfpga_bitstream_command.cpp @@ -151,6 +151,9 @@ ShellCommandId add_openfpga_write_fabric_bitstream_command(openfpga::Shell + +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" + +#include "fabric_global_port_info_utils.h" +#include "fast_configuration.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Identify if fast configuration is applicable base on the availability + * of programming reset and programming set ports of the FPGA fabric + *******************************************************************/ +bool is_fast_configuration_applicable(const FabricGlobalPortInfo& global_ports) { + /* Preparation: find all the reset/set ports for programming usage */ + std::vector global_prog_reset_ports = find_fabric_global_programming_reset_ports(global_ports); + std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); + + /* Identify if we can apply fast configuration */ + if (global_prog_set_ports.empty() && global_prog_reset_ports.empty()) { + VTR_LOG_WARN("None of global reset and set ports are defined for programming purpose. Fast configuration is not applicable\n"); + return false; + } + + return true; +} + +/******************************************************************** + * Decide if we should use reset or set signal to acheive fast configuration + * - If only one type signal is specified, we use that type + * For example, only reset signal is defined, we will use reset + * - If both are defined, pick the one that will bring bigger reduction + * i.e., larger number of configuration bits can be skipped + *******************************************************************/ +bool find_bit_value_to_skip_for_fast_configuration(const e_config_protocol_type& config_protocol_type, + const FabricGlobalPortInfo& global_ports, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream) { + /* Preparation: find all the reset/set ports for programming usage */ + std::vector global_prog_reset_ports = find_fabric_global_programming_reset_ports(global_ports); + std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); + + /* Early exit conditions */ + if (!global_prog_reset_ports.empty() && global_prog_set_ports.empty()) { + return false; + } else if (!global_prog_set_ports.empty() && global_prog_reset_ports.empty()) { + return true; + } + + /* If both types of ports are not defined, the fast configuration is not applicable */ + VTR_ASSERT(!global_prog_set_ports.empty() && !global_prog_reset_ports.empty()); + bool bit_value_to_skip = false; + + VTR_LOG("Both reset and set ports are defined for programming controls, selecting the best-fit one...\n"); + + size_t num_ones_to_skip = 0; + size_t num_zeros_to_skip = 0; + + /* Branch on the type of configuration protocol */ + switch (config_protocol_type) { + case CONFIG_MEM_STANDALONE: + break; + case CONFIG_MEM_SCAN_CHAIN: { + /* We can only skip the ones/zeros at the beginning of the bitstream */ + /* Count how many logic '1' bits we can skip */ + for (const FabricBitId& bit_id : fabric_bitstream.bits()) { + if (false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { + break; + } + VTR_ASSERT(true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); + num_ones_to_skip++; + } + /* Count how many logic '0' bits we can skip */ + for (const FabricBitId& bit_id : fabric_bitstream.bits()) { + if (true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { + break; + } + VTR_ASSERT(false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); + num_zeros_to_skip++; + } + break; + } + case CONFIG_MEM_MEMORY_BANK: + case CONFIG_MEM_FRAME_BASED: { + /* Count how many logic '1' and logic '0' bits we can skip */ + for (const FabricBitId& bit_id : fabric_bitstream.bits()) { + if (false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { + num_zeros_to_skip++; + } else { + VTR_ASSERT(true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); + num_ones_to_skip++; + } + } + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid configuration protocol type!\n"); + exit(1); + } + + VTR_LOG("Using reset will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_zeros_to_skip / (float) fabric_bitstream.num_bits(), + num_zeros_to_skip, fabric_bitstream.num_bits()); + + VTR_LOG("Using set will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_ones_to_skip / (float) fabric_bitstream.num_bits(), + num_ones_to_skip, fabric_bitstream.num_bits()); + + /* By default, we prefer to skip zeros (when the numbers are the same */ + if (num_ones_to_skip > num_zeros_to_skip) { + VTR_LOG("Will use set signal in fast configuration\n"); + bit_value_to_skip = true; + } else { + VTR_LOG("Will use reset signal in fast configuration\n"); + } + + return bit_value_to_skip; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/fast_configuration.h b/openfpga/src/fpga_bitstream/fast_configuration.h new file mode 100644 index 000000000..eb161d1b2 --- /dev/null +++ b/openfpga/src/fpga_bitstream/fast_configuration.h @@ -0,0 +1,30 @@ +#ifndef FAST_CONFIGURATION_H +#define FAST_CONFIGURATION_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "fabric_global_port_info.h" +#include "config_protocol.h" +#include "bitstream_manager.h" +#include "fabric_bitstream.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +bool is_fast_configuration_applicable(const FabricGlobalPortInfo& global_ports); + +bool find_bit_value_to_skip_for_fast_configuration(const e_config_protocol_type& config_protocol_type, + const FabricGlobalPortInfo& global_ports, + const BitstreamManager& bitstream_manager, + const FabricBitstream& fabric_bitstream); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 3fa604de1..663e679d3 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -16,6 +16,7 @@ #include "openfpga_naming.h" +#include "fast_configuration.h" #include "bitstream_manager_utils.h" #include "fabric_bitstream_utils.h" #include "write_text_fabric_bitstream.h" @@ -118,6 +119,8 @@ int write_flatten_fabric_bitstream_to_text_file(std::fstream& fp, *******************************************************************/ static int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, + const bool& fast_configuration, + const bool& bit_value_to_skip, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream) { int status = 0; @@ -125,7 +128,18 @@ int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream); ConfigChainFabricBitstream regional_bitstreams = build_config_chain_fabric_bitstream_by_region(bitstream_manager, fabric_bitstream); - for (size_t ibit = 0; ibit < regional_bitstream_max_size; ++ibit) { + /* For fast configuration, the bitstream size counts from the first bit '1' */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip); + } + VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size); + + VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_bits_to_skip / (float) regional_bitstream_max_size, + num_bits_to_skip, regional_bitstream_max_size); + + for (size_t ibit = num_bits_to_skip; ibit < regional_bitstream_max_size; ++ibit) { for (const auto& region_bitstream : regional_bitstreams) { fp << region_bitstream[ibit]; } @@ -214,7 +228,9 @@ int write_frame_based_fabric_bitstream_to_text_file(std::fstream& fp, int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, const ConfigProtocol& config_protocol, + const FabricGlobalPortInfo& global_ports, const std::string& fname, + const bool& fast_configuration, const bool& verbose) { /* Ensure that we have a valid file name */ if (true == fname.empty()) { @@ -230,6 +246,19 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage check_file_stream(fname.c_str(), fp); + bool apply_fast_configuration = is_fast_configuration_applicable(global_ports); + if (fast_configuration && apply_fast_configuration != fast_configuration) { + VTR_LOG_WARN("Disable fast configuration even it is enabled by user\n"); + } + + bool bit_value_to_skip = false; + if (apply_fast_configuration) { + bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), + global_ports, + bitstream_manager, + fabric_bitstream); + } + /* Output fabric bitstream to the file */ int status = 0; switch (config_protocol.type()) { @@ -241,6 +270,8 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage break; case CONFIG_MEM_SCAN_CHAIN: status = write_config_chain_fabric_bitstream_to_text_file(fp, + apply_fast_configuration, + bit_value_to_skip, bitstream_manager, fabric_bitstream); break; diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h index 9582a185c..17811f439 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h @@ -9,6 +9,7 @@ #include "bitstream_manager.h" #include "fabric_bitstream.h" #include "config_protocol.h" +#include "fabric_global_port_info.h" /******************************************************************** * Function declaration @@ -20,7 +21,9 @@ namespace openfpga { int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, const ConfigProtocol& config_protocol, + const FabricGlobalPortInfo& global_ports, const std::string& fname, + const bool& fast_configuration, const bool& verbose); } /* end namespace openfpga */ From e9fa44cc2520d89f7392f45d221f5f9ff29e6c71 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 16:24:56 -0600 Subject: [PATCH 123/164] [Tool] Add fast configuration to the write bitstream command in example shell script --- .../write_full_testbench_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga index 8a87a0a08..612249a15 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.bit --format plain_text +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text ${OPENFPGA_FAST_CONFIGURATION} # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist From 6e69c2d70a1f69aa960e293feec7f95a64327ab2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 16:34:55 -0600 Subject: [PATCH 124/164] [Tool] Patch fast configuration in full Verilog testbench generator --- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index fb88e2bbf..cdc820e87 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -22,6 +22,7 @@ #include "simulation_utils.h" #include "openfpga_atom_netlist_utils.h" +#include "fast_configuration.h" #include "fabric_bitstream_utils.h" #include "fabric_global_port_info_utils.h" @@ -1955,6 +1956,7 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp const bool& bit_value_to_skip, const ModuleManager& module_manager, const ModuleId& top_module, + const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream) { /* Validate the file stream */ valid_file_stream(fp); @@ -1964,8 +1966,15 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp /* Find the longest bitstream */ size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream); + /* For fast configuration, the bitstream size counts from the first bit '1' */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip); + } + VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size); + /* Define a constant for the bitstream length */ - print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), regional_bitstream_max_size); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), regional_bitstream_max_size - num_bits_to_skip); print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), fabric_bitstream.num_regions()); /* Initial value should be the first configuration bits @@ -2140,6 +2149,7 @@ void print_verilog_full_testbench_bitstream(std::fstream& fp, fast_configuration, bit_value_to_skip, module_manager, top_module, + bitstream_manager, fabric_bitstream); break; case CONFIG_MEM_MEMORY_BANK: From d98be9f87b907956b135ab71649a96a26d68c6b5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 16:45:00 -0600 Subject: [PATCH 125/164] [Tool] Remove icarus requirement on vcd writing in Verilog testbenches; Since vcd writing commands are standard Verilog --- .../verilog_formal_random_top_testbench.cpp | 1 - openfpga/src/fpga_verilog/verilog_testbench_utils.cpp | 11 ++--------- openfpga/src/fpga_verilog/verilog_testbench_utils.h | 1 - openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 2 -- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp index 848970a30..9ed4a8138 100644 --- a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp @@ -359,7 +359,6 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, /* Add Icarus requirement */ print_verilog_timeout_and_vcd(fp, - std::string(ICARUS_SIMULATOR_FLAG), std::string(circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX)), std::string(circuit_name + std::string("_formal.vcd")), std::string(FORMAL_TB_SIM_START_PORT_NAME), diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 3329d33cf..994d6f280 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -300,7 +300,6 @@ void print_verilog_testbench_connect_fpga_ios(std::fstream& fp, * Note that: these codes are tuned for Icarus simulator!!! *******************************************************************/ void print_verilog_timeout_and_vcd(std::fstream& fp, - const std::string& icarus_preprocessing_flag, const std::string& module_name, const std::string& vcd_fname, const std::string& simulation_start_counter_name, @@ -309,20 +308,14 @@ void print_verilog_timeout_and_vcd(std::fstream& fp, /* Validate the file stream */ valid_file_stream(fp); - /* The following verilog codes are tuned for Icarus */ - print_verilog_preprocessing_flag(fp, icarus_preprocessing_flag); - - print_verilog_comment(fp, std::string("----- Begin Icarus requirement -------")); + print_verilog_comment(fp, std::string("----- Begin output waveform to VCD file-------")); fp << "\tinitial begin" << std::endl; fp << "\t\t$dumpfile(\"" << vcd_fname << "\");" << std::endl; fp << "\t\t$dumpvars(1, " << module_name << ");" << std::endl; fp << "\tend" << std::endl; - /* Condition ends for the Icarus requirement */ - print_verilog_endif(fp); - - print_verilog_comment(fp, std::string("----- END Icarus requirement -------")); + print_verilog_comment(fp, std::string("----- END output waveform to VCD file -------")); /* Add an empty line as splitter */ fp << std::endl; diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h index 0eb77bda9..807d25ae5 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -55,7 +55,6 @@ void print_verilog_testbench_connect_fpga_ios(std::fstream& fp, const size_t& unused_io_value); void print_verilog_timeout_and_vcd(std::fstream& fp, - const std::string& icarus_preprocessing_flag, const std::string& module_name, const std::string& vcd_fname, const std::string& simulation_start_counter_name, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index cdc820e87..a5359f585 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2496,7 +2496,6 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, * Always ceil the simulation time so that we test a sufficient length of period!!! */ print_verilog_timeout_and_vcd(fp, - std::string(ICARUS_SIMULATOR_FLAG), std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)), std::string(circuit_name + std::string("_formal.vcd")), std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), @@ -2758,7 +2757,6 @@ int print_verilog_full_testbench(const ModuleManager& module_manager, * Always ceil the simulation time so that we test a sufficient length of period!!! */ print_verilog_timeout_and_vcd(fp, - std::string(ICARUS_SIMULATOR_FLAG), std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)), std::string(circuit_name + std::string("_formal.vcd")), std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), From 70fb3a85dc08d0365719e3613f03b52cd55477c3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 17:23:10 -0600 Subject: [PATCH 126/164] [Tool] Patch fast configuration in bitstream writing --- .../fpga_bitstream/write_text_fabric_bitstream.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 663e679d3..9a637bc72 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -132,12 +132,11 @@ int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, size_t num_bits_to_skip = 0; if (true == fast_configuration) { num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip); + VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size); + VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_bits_to_skip / (float) regional_bitstream_max_size, + num_bits_to_skip, regional_bitstream_max_size); } - VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size); - - VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", - 100. * (float) num_bits_to_skip / (float) regional_bitstream_max_size, - num_bits_to_skip, regional_bitstream_max_size); for (size_t ibit = num_bits_to_skip; ibit < regional_bitstream_max_size; ++ibit) { for (const auto& region_bitstream : regional_bitstreams) { @@ -246,7 +245,7 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage check_file_stream(fname.c_str(), fp); - bool apply_fast_configuration = is_fast_configuration_applicable(global_ports); + bool apply_fast_configuration = is_fast_configuration_applicable(global_ports) && fast_configuration; if (fast_configuration && apply_fast_configuration != fast_configuration) { VTR_LOG_WARN("Disable fast configuration even it is enabled by user\n"); } From 27fa15603ace0281fef84083e384fa7fe498a455 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 18:17:47 -0600 Subject: [PATCH 127/164] [Tool] Patch test case due to changes in the template script --- .../multi_region_configuration_chain/config/task.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf index ebc47d3b6..dd4bea019 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_chain/config/task.conf @@ -20,6 +20,7 @@ openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scrip openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout=--device 2x2 +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From cf7addb1a6847fc24603cdaf37bab8bfb6988806 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 19:48:48 -0600 Subject: [PATCH 128/164] [Tool] Add heads to bitstream plain text file --- .../write_text_fabric_bitstream.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 9a637bc72..5be5d04cc 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -13,6 +13,7 @@ /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "openfpga_version.h" #include "openfpga_naming.h" @@ -24,6 +25,21 @@ /* begin namespace openfpga */ namespace openfpga { +/******************************************************************** + * This function write header information to a bitstream file + *******************************************************************/ +static +void write_fabric_bitstream_text_file_head(std::fstream& fp) { + valid_file_stream(fp); + + auto end = std::chrono::system_clock::now(); + std::time_t end_time = std::chrono::system_clock::to_time_t(end); + + fp << "// Fabric bitstream" << std::endl; + fp << "// Version: " << openfpga::VERSION << std::endl; + fp << "// Date: " << std::ctime(&end_time) << std::endl; +} + /******************************************************************** * Write a configuration bit into a plain text file * The format depends on the type of configuration protocol @@ -138,6 +154,11 @@ int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, num_bits_to_skip, regional_bitstream_max_size); } + /* Output bitstream size information */ + fp << "// Bitstream length: " << regional_bitstream_max_size - num_bits_to_skip << std::endl; + fp << "// Bitstream width (LSB -> MSB): " << fabric_bitstream.num_regions() << std::endl; + + /* Output bitstream data */ for (size_t ibit = num_bits_to_skip; ibit < regional_bitstream_max_size; ++ibit) { for (const auto& region_bitstream : regional_bitstreams) { fp << region_bitstream[ibit]; @@ -258,6 +279,9 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage fabric_bitstream); } + /* Write file head */ + write_fabric_bitstream_text_file_head(fp); + /* Output fabric bitstream to the file */ int status = 0; switch (config_protocol.type()) { From c30be6e95ee9ca4cf495868e6c627dd0d3dc06be Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 20:00:28 -0600 Subject: [PATCH 129/164] [Doc] Update documentation about the fast configuration for write bitstream command --- .../openfpga_commands/fpga_bitstream_commands.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst index a63717e15..12c4d8ff5 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst @@ -69,6 +69,15 @@ write_fabric_bitstream Specify the file format [``plain_text`` | ``xml``]. By default is ``plain_text``. See file formats in :ref:`file_formats_fabric_bitstream_xml` and :ref:`file_formats_fabric_bitstream_plain_text`. + .. option:: --fast_configuration + + Reduce the bitstream size when outputing by skipping dummy configuration bits. It is applicable to configuration chain, memory bank and frame-based configuration protocols. For configuration chain, when enabled, the zeros at the head of the bitstream will be skipped. For memory bank and frame-based, when enabled, all the zero configuration bits will be skipped. So ensure that your memory cells can be correctly reset to zero with a reset signal. + + .. warning:: Fast configuration is only applicable to plain text file format! + + .. note:: If both reset and set ports are defined in the circuit modeling for programming, OpenFPGA will pick the one that will bring largest benefit in speeding up configuration. + + .. option:: --verbose Show verbose log From 618b04568f09f21024551c9d27866b629a1bece7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 4 Jun 2021 20:07:42 -0600 Subject: [PATCH 130/164] [Tool] Remove unnecessary new line in bitstream file --- openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 5be5d04cc..1fb86c574 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -37,7 +37,7 @@ void write_fabric_bitstream_text_file_head(std::fstream& fp) { fp << "// Fabric bitstream" << std::endl; fp << "// Version: " << openfpga::VERSION << std::endl; - fp << "// Date: " << std::ctime(&end_time) << std::endl; + fp << "// Date: " << std::ctime(&end_time); } /******************************************************************** @@ -163,7 +163,9 @@ int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, for (const auto& region_bitstream : regional_bitstreams) { fp << region_bitstream[ibit]; } - fp << std::endl; + if (ibit < regional_bitstream_max_size - 1) { + fp << std::endl; + } } return status; From d644b8f22d7b2386fcf59e471f65c13f83c996cf Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 11:55:11 -0600 Subject: [PATCH 131/164] [Tool] Support external bitstream file when generating full testbench for frame-based decoder --- .../write_text_fabric_bitstream.cpp | 37 ++++- .../fpga_verilog/verilog_top_testbench.cpp | 128 ++++++++++++++++++ 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 1fb86c574..0e8a435e7 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -215,15 +215,48 @@ int write_memory_bank_fabric_bitstream_to_text_file(std::fstream& fp, *******************************************************************/ static int write_frame_based_fabric_bitstream_to_text_file(std::fstream& fp, + const bool& fast_configuration, + const bool& bit_value_to_skip, const FabricBitstream& fabric_bitstream) { int status = 0; FrameFabricBitstream fabric_bits_by_addr = build_frame_based_fabric_bitstream_by_address(fabric_bitstream); + /* The address sizes and data input sizes are the same across any element, + * just get it from the 1st element to save runtime + */ + size_t addr_size = fabric_bits_by_addr.begin()->first.size(); + size_t din_size = fabric_bits_by_addr.begin()->second.size(); + + /* Identify and output bitstream size information */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); + VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_bits_to_skip / (float) fabric_bits_by_addr.size(), + num_bits_to_skip, fabric_bits_by_addr.size()); + } + + /* Output information about how to intepret the bitstream */ + fp << "// Bitstream length: " << fabric_bits_by_addr.size() - num_bits_to_skip << std::endl; + fp << "// Bitstream address size (LSB -> MSB): " << addr_size << std::endl; + fp << "// Bitstream data input size (LSB -> MSB): " << din_size << std::endl; + for (const auto& addr_din_pair : fabric_bits_by_addr) { + /* When fast configuration is enabled, + * the rule to skip any configuration bit should consider the whole data input values. + * Only all the bits in the din port match the value to be skipped, + * the programming cycle can be skipped! + */ + if (true == fast_configuration) { + if (addr_din_pair.second == std::vector(addr_din_pair.second.size(), bit_value_to_skip)) { + continue; + } + } + /* Write address code */ fp << addr_din_pair.first; - fp << " "; /* Write data input */ for (const bool& din_value : addr_din_pair.second) { @@ -306,6 +339,8 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage break; case CONFIG_MEM_FRAME_BASED: status = write_frame_based_fabric_bitstream_to_text_file(fp, + apply_fast_configuration, + bit_value_to_skip, fabric_bitstream); break; default: diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index a5359f585..951611c39 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2122,6 +2122,128 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); } +/******************************************************************** + * Print stimulus for a FPGA fabric with a frame-based configuration protocol + * where configuration bits are programming in serial (one by one) + *******************************************************************/ +static +void print_verilog_full_testbench_frame_decoder_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const bool& fast_configuration, + const bool& bit_value_to_skip, + const ModuleManager& module_manager, + const ModuleId& top_module, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Reorganize the fabric bitstream by the same address across regions */ + FrameFabricBitstream fabric_bits_by_addr = build_frame_based_fabric_bitstream_by_address(fabric_bitstream); + + /* For fast configuration, identify the final bitstream size to be used */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + } + VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); + + /* Feed address and data input pair one by one + * Note: the first cycle is reserved for programming reset + * We should give dummy values + */ + ModulePortId addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); + std::vector initial_addr_values(addr_port.get_width(), 0); + + ModulePortId din_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort din_port = module_manager.module_port(top_module, din_port_id); + std::vector initial_din_values(din_port.get_width(), 0); + + /* Define a constant for the bitstream length */ + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), fabric_bits_by_addr.size() - num_bits_to_skip); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), addr_port.get_width() + din_port.get_width()); + + /* Declare local variables for bitstream loading in Verilog */ + print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----"); + fp << "reg [0:`" << TOP_TB_BITSTREAM_WIDTH_VARIABLE << " - 1] "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; + fp << std::endl; + + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); + fp << "initial begin" << std::endl; + fp << "\t"; + fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");"; + fp << std::endl; + + print_verilog_comment(fp, "----- Address port default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(addr_port, initial_addr_values); + fp << ";"; + fp << std::endl; + + print_verilog_comment(fp, "----- Data-input port default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(din_port, initial_din_values); + fp << ";"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX), 1); + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t"; + fp << "if ("; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " >= "; + fp << "`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE; + fp << ") begin"; + fp << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t"; + std::vector config_done_final_values(config_done_port.get_width(), 1); + fp << generate_verilog_port_constant_values(config_done_port, config_done_final_values, true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "end else begin"; + fp << std::endl; + + fp << "\t\t"; + fp << "{"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); + fp << ", "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, din_port); + fp << "}"; + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[" << TOP_TB_BITSTREAM_INDEX_REG_NAME << "]"; + fp << ";" << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " <= "; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1"; + fp << ";" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} /******************************************************************** * Generate the stimuli for the full testbench @@ -2155,6 +2277,12 @@ void print_verilog_full_testbench_bitstream(std::fstream& fp, case CONFIG_MEM_MEMORY_BANK: break; case CONFIG_MEM_FRAME_BASED: + print_verilog_full_testbench_frame_decoder_bitstream(fp, bitstream_file, + fast_configuration, + bit_value_to_skip, + module_manager, top_module, + fabric_bitstream); + break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, From af298de12143acfbc9cbd6e85664f7ef56183e15 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 13:53:32 -0600 Subject: [PATCH 132/164] [Tool] Patch bugs in the full testbench writing using external bitstream file for frame-based configuration protocol --- openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp | 3 +-- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 0e8a435e7..57d8d92e1 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -240,8 +240,7 @@ int write_frame_based_fabric_bitstream_to_text_file(std::fstream& fp, /* Output information about how to intepret the bitstream */ fp << "// Bitstream length: " << fabric_bits_by_addr.size() - num_bits_to_skip << std::endl; - fp << "// Bitstream address size (LSB -> MSB): " << addr_size << std::endl; - fp << "// Bitstream data input size (LSB -> MSB): " << din_size << std::endl; + fp << "// Bitstream width (LSB -> MSB):

" << std::endl; for (const auto& addr_din_pair : fabric_bits_by_addr) { /* When fast configuration is enabled, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 951611c39..20f3a5bcb 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2191,6 +2191,11 @@ void print_verilog_full_testbench_frame_decoder_bitstream(std::fstream& fp, fp << ";"; fp << std::endl; + fp << "\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " <= 0"; + fp << ";"; + fp << std::endl; + fp << "end"; fp << std::endl; From a67196178e179b346c57c6e5e1dc820a1276d179 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 13:58:15 -0600 Subject: [PATCH 133/164] [Test] Now use 'write_full_testbench' in configuration frame test cases --- .../full_testbench/configuration_frame/config/task.conf | 4 +++- .../full_testbench/configuration_frame_ccff/config/task.conf | 4 +++- .../full_testbench/configuration_frame_scff/config/task.conf | 4 +++- .../configuration_frame_use_reset/config/task.conf | 4 +++- .../configuration_frame_use_resetb/config/task.conf | 4 +++- .../configuration_frame_use_set/config/task.conf | 4 +++- .../configuration_frame_use_set_reset/config/task.conf | 4 +++- .../configuration_frame_use_setb/config/task.conf | 4 +++- .../full_testbench/fast_configuration_frame/config/task.conf | 4 +++- .../fast_configuration_frame_use_set/config/task.conf | 4 +++- .../multi_region_configuration_frame/config/task.conf | 4 +++- .../smart_fast_configuration_frame/config/task.conf | 4 +++- .../config/task.conf | 4 +++- 13 files changed, 39 insertions(+), 13 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf index aaee4c7c9..0389fee37 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_ccff/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_ccff/config/task.conf index 37d68988c..576a1888b 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_ccff/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_ccff/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_ccff_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_scff/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_scff/config/task.conf index 2a8cded3f..7daaf775c 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_scff/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_scff/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_scff_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_reset/config/task.conf index 40b3c6f9b..27b6005e8 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_resetb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_resetb/config/task.conf index d6f4a1812..3d3c4acdd 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_resetb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_resetb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_resetb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set/config/task.conf index 86edcefec..81d1067d9 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set_reset/config/task.conf index e91cbd103..0a19f0e50 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_set_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_setb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_setb/config/task.conf index 63baba79e..1247d8f78 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_setb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame_use_setb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_setb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf index 14425886d..f9b1656f1 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame_use_set/config/task.conf index 2ee58fbf2..afd572370 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_frame/config/task.conf index 85771410c..b3c193558 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_configuration_frame/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_frame/config/task.conf index d4a77cd79..38e2e9556 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_configuration_frame/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_frame/config/task.conf index 4f9b28e27..13da04cef 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_configuration_frame/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_frame_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 0fee74100865bea508fcf3e87ccec542bad8fc7e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 14:22:35 -0600 Subject: [PATCH 134/164] [Doc] Update documentation on the minor changes on fabric bitstream file format --- .../manual/file_formats/fabric_bitstream.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/source/manual/file_formats/fabric_bitstream.rst b/docs/source/manual/file_formats/fabric_bitstream.rst index 92e11f24e..46a7b852a 100644 --- a/docs/source/manual/file_formats/fabric_bitstream.rst +++ b/docs/source/manual/file_formats/fabric_bitstream.rst @@ -63,7 +63,14 @@ The information depends on the type of configuration procotol. .. option:: frame_based - Multiple lines will be included, each of which is organized as
. + Multiple lines will be included, each of which is organized as ``
``. + The size of address line and data input bits are shown as a comment in the bitstream file, which eases the development of bitstream downloader. + For example + + .. code-block:: verilog + + // Bitstream width (LSB -> MSB):
+ Note that the address may include don't care bit which is denoted as ``x``. .. note:: OpenFPGA automatically convert don't care bit to logic ``0`` when generating testbenches. @@ -72,10 +79,10 @@ The information depends on the type of configuration procotol. .. code-block:: xml - - + + ... - + .. note:: When there are multiple configuration regions, each ```` may consist of multiple bits. For example, ``0110`` represents the bits for 4 configuration regions, where the 4 digits correspond to the bits from region ``0, 1, 2, 3`` respectively. From 1a5902ca74758ad8436d94349170bc5d359efb54 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 14:32:56 -0600 Subject: [PATCH 135/164] [Tool] Bug fix in finding pruned bitstream for frame-based protocol when fast configuration is enabled --- openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp | 2 +- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 57d8d92e1..b887bd861 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -231,7 +231,7 @@ int write_frame_based_fabric_bitstream_to_text_file(std::fstream& fp, /* Identify and output bitstream size information */ size_t num_bits_to_skip = 0; if (true == fast_configuration) { - num_bits_to_skip = find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + num_bits_to_skip = fabric_bits_by_addr.size() - find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", 100. * (float) num_bits_to_skip / (float) fabric_bits_by_addr.size(), diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 20f3a5bcb..3b82e92c2 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2143,7 +2143,7 @@ void print_verilog_full_testbench_frame_decoder_bitstream(std::fstream& fp, /* For fast configuration, identify the final bitstream size to be used */ size_t num_bits_to_skip = 0; if (true == fast_configuration) { - num_bits_to_skip = find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + num_bits_to_skip = fabric_bits_by_addr.size() - find_frame_based_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); } VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); From ba75c18378277ff5e9882ef9e4374ec561f79841 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 17:40:07 -0600 Subject: [PATCH 136/164] [Tool] Now 'write_full_testbench' supports memory bank configuration protocol --- .../write_text_fabric_bitstream.cpp | 46 +++++- .../fpga_verilog/verilog_top_testbench.cpp | 146 ++++++++++++++++++ 2 files changed, 187 insertions(+), 5 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index b887bd861..fdd27f649 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -181,20 +181,54 @@ int write_config_chain_fabric_bitstream_to_text_file(std::fstream& fp, *******************************************************************/ static int write_memory_bank_fabric_bitstream_to_text_file(std::fstream& fp, - const FabricBitstream& fabric_bitstream) { + const bool& fast_configuration, + const bool& bit_value_to_skip, + const FabricBitstream& fabric_bitstream) { int status = 0; MemoryBankFabricBitstream fabric_bits_by_addr = build_memory_bank_fabric_bitstream_by_address(fabric_bitstream); + /* The address sizes and data input sizes are the same across any element, + * just get it from the 1st element to save runtime + */ + size_t bl_addr_size = fabric_bits_by_addr.begin()->first.first.size(); + size_t wl_addr_size = fabric_bits_by_addr.begin()->first.second.size(); + size_t din_size = fabric_bits_by_addr.begin()->second.size(); + + /* Identify and output bitstream size information */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = fabric_bits_by_addr.size() - find_memory_bank_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); + VTR_LOG("Fast configuration will skip %g% (%lu/%lu) of configuration bitstream.\n", + 100. * (float) num_bits_to_skip / (float) fabric_bits_by_addr.size(), + num_bits_to_skip, fabric_bits_by_addr.size()); + } + + /* Output information about how to intepret the bitstream */ + fp << "// Bitstream length: " << fabric_bits_by_addr.size() - num_bits_to_skip << std::endl; + fp << "// Bitstream width (LSB -> MSB): "; + fp << ""; + fp << ""; + fp << ""; + fp << std::endl; + for (const auto& addr_din_pair : fabric_bits_by_addr) { + /* When fast configuration is enabled, + * the rule to skip any configuration bit should consider the whole data input values. + * Only all the bits in the din port match the value to be skipped, + * the programming cycle can be skipped! + */ + if (true == fast_configuration) { + if (addr_din_pair.second == std::vector(addr_din_pair.second.size(), bit_value_to_skip)) { + continue; + } + } + /* Write BL address code */ fp << addr_din_pair.first.first; - fp << " "; - /* Write WL address code */ fp << addr_din_pair.first.second; - fp << " "; - /* Write data input */ for (const bool& din_value : addr_din_pair.second) { fp << din_value; @@ -334,6 +368,8 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage break; case CONFIG_MEM_MEMORY_BANK: status = write_memory_bank_fabric_bitstream_to_text_file(fp, + apply_fast_configuration, + bit_value_to_skip, fabric_bitstream); break; case CONFIG_MEM_FRAME_BASED: diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 3b82e92c2..3a237160c 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2122,6 +2122,147 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); } +/******************************************************************** + * Print stimulus for a FPGA fabric with a memory bank configuration protocol + * where configuration bits are programming in serial (one by one) + *******************************************************************/ +static +void print_verilog_full_testbench_memory_bank_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const bool& fast_configuration, + const bool& bit_value_to_skip, + const ModuleManager& module_manager, + const ModuleId& top_module, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Reorganize the fabric bitstream by the same address across regions */ + MemoryBankFabricBitstream fabric_bits_by_addr = build_memory_bank_fabric_bitstream_by_address(fabric_bitstream); + + /* For fast configuration, identify the final bitstream size to be used */ + size_t num_bits_to_skip = 0; + if (true == fast_configuration) { + num_bits_to_skip = fabric_bits_by_addr.size() - find_memory_bank_fast_configuration_fabric_bitstream_size(fabric_bitstream, bit_value_to_skip); + } + VTR_ASSERT(num_bits_to_skip < fabric_bits_by_addr.size()); + + /* Feed address and data input pair one by one + * Note: the first cycle is reserved for programming reset + * We should give dummy values + */ + ModulePortId bl_addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_BL_ADDRESS_PORT_NAME)); + BasicPort bl_addr_port = module_manager.module_port(top_module, bl_addr_port_id); + std::vector initial_bl_addr_values(bl_addr_port.get_width(), 0); + + ModulePortId wl_addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_WL_ADDRESS_PORT_NAME)); + BasicPort wl_addr_port = module_manager.module_port(top_module, wl_addr_port_id); + std::vector initial_wl_addr_values(wl_addr_port.get_width(), 0); + + ModulePortId din_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort din_port = module_manager.module_port(top_module, din_port_id); + std::vector initial_din_values(din_port.get_width(), 0); + + /* Define a constant for the bitstream length */ + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), fabric_bits_by_addr.size() - num_bits_to_skip); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), bl_addr_port.get_width() + wl_addr_port.get_width() + din_port.get_width()); + + /* Declare local variables for bitstream loading in Verilog */ + print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----"); + fp << "reg [0:`" << TOP_TB_BITSTREAM_WIDTH_VARIABLE << " - 1] "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; + fp << std::endl; + + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); + fp << "initial begin" << std::endl; + fp << "\t"; + fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");"; + fp << std::endl; + + print_verilog_comment(fp, "----- Bit-Line Address port default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(bl_addr_port, initial_bl_addr_values); + fp << ";"; + fp << std::endl; + + print_verilog_comment(fp, "----- Word-Line Address port default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(wl_addr_port, initial_wl_addr_values); + fp << ";"; + fp << std::endl; + + print_verilog_comment(fp, "----- Data-input port default input -----"); + fp << "\t"; + fp << generate_verilog_port_constant_values(din_port, initial_din_values); + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " <= 0"; + fp << ";"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX), 1); + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t"; + fp << "if ("; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " >= "; + fp << "`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE; + fp << ") begin"; + fp << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t"; + std::vector config_done_final_values(config_done_port.get_width(), 1); + fp << generate_verilog_port_constant_values(config_done_port, config_done_final_values, true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "end else begin"; + fp << std::endl; + + fp << "\t\t"; + fp << "{"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, bl_addr_port); + fp << ", "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, wl_addr_port); + fp << ", "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, din_port); + fp << "}"; + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[" << TOP_TB_BITSTREAM_INDEX_REG_NAME << "]"; + fp << ";" << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " <= "; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1"; + fp << ";" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + /******************************************************************** * Print stimulus for a FPGA fabric with a frame-based configuration protocol * where configuration bits are programming in serial (one by one) @@ -2280,6 +2421,11 @@ void print_verilog_full_testbench_bitstream(std::fstream& fp, fabric_bitstream); break; case CONFIG_MEM_MEMORY_BANK: + print_verilog_full_testbench_memory_bank_bitstream(fp, bitstream_file, + fast_configuration, + bit_value_to_skip, + module_manager, top_module, + fabric_bitstream); break; case CONFIG_MEM_FRAME_BASED: print_verilog_full_testbench_frame_decoder_bitstream(fp, bitstream_file, From 9556f994b4d291593d0bafdf3a4aab11887df73e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 17:49:40 -0600 Subject: [PATCH 137/164] [Test] Use 'write_full_testbench' in all the memory bank -related test cases --- .../full_testbench/fast_memory_bank/config/task.conf | 4 +++- .../full_testbench/fast_memory_bank_use_set/config/task.conf | 4 +++- .../basic_tests/full_testbench/memory_bank/config/task.conf | 4 +++- .../full_testbench/memory_bank_use_reset/config/task.conf | 4 +++- .../full_testbench/memory_bank_use_resetb/config/task.conf | 4 +++- .../full_testbench/memory_bank_use_set/config/task.conf | 4 +++- .../full_testbench/memory_bank_use_set_reset/config/task.conf | 4 +++- .../full_testbench/memory_bank_use_setb/config/task.conf | 4 +++- .../full_testbench/multi_region_memory_bank/config/task.conf | 3 ++- .../full_testbench/smart_fast_memory_bank/config/task.conf | 4 +++- .../smart_fast_multi_region_memory_bank/config/task.conf | 4 +++- 11 files changed, 32 insertions(+), 11 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf index 5510c7a8c..c8789b46a 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank_use_set/config/task.conf index 1ceeb3384..03c0b97ed 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf index 960cc0358..bd08da556 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_reset/config/task.conf index 88f00dd83..d20ef4ee8 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_resetb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_resetb/config/task.conf index 2db9c982f..502cd68ae 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_resetb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_resetb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_resetb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set/config/task.conf index 1b27c683c..33ea7546f 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_set_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set_reset/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set_reset/config/task.conf index 1e15dba22..52e103c6f 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set_reset/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_set_reset/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_setb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_setb/config/task.conf index 6f166e5fc..1ea736ca9 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_setb/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank_use_setb/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_setb_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf index 41c819a8c..20914980b 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf @@ -16,10 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_bank_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout=2x2 +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_memory_bank/config/task.conf index f7e99fd89..b7368ad66 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_memory_bank/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_bank_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_memory_bank/config/task.conf index 66d4da4fb..29fcb228d 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/smart_fast_multi_region_memory_bank/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_bank_use_both_set_reset_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration=--fast_configuration [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 54a53bc988588bfe683b6c5db411cd465a7c1403 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 17:58:00 -0600 Subject: [PATCH 138/164] [Doc] Update documentation on the minor changes on bitstream file for memory bank protocol --- .../manual/file_formats/fabric_bitstream.rst | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/source/manual/file_formats/fabric_bitstream.rst b/docs/source/manual/file_formats/fabric_bitstream.rst index 46a7b852a..2def5d269 100644 --- a/docs/source/manual/file_formats/fabric_bitstream.rst +++ b/docs/source/manual/file_formats/fabric_bitstream.rst @@ -43,21 +43,25 @@ The information depends on the type of configuration procotol. .. option:: memory_bank - Multiple lines will be included, each of which is organized as
. - Note that due to the use of Bit-Line and Word-Line decoders, every two lines are paired. - The first line represents the Bit-Line address and configuration bit. - The second line represents the Word-Line address and configuration bit. + Multiple lines will be included, each of which is organized as . + The size of address line and data input bits are shown as a comment in the bitstream file, which eases the development of bitstream downloader. + For example + + .. code-block:: verilog + + // Bitstream width (LSB -> MSB): + + The first part represents the Bit-Line address. + The second part represents the Word-Line address. + The third part represents the configuration bit. For example .. code-block:: xml - - - - + + ... - - + .. note:: When there are multiple configuration regions, each ```` may consist of multiple bits. For example, ``0110`` represents the bits for 4 configuration regions, where the 4 digits correspond to the bits from region ``0, 1, 2, 3`` respectively. From 5ecd975ec7d4b0903ed05c28dabb959c4abe125b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 19:20:10 -0600 Subject: [PATCH 139/164] [Test] Bug fix --- .../full_testbench/multi_region_memory_bank/config/task.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf index 20914980b..004f3701a 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/multi_region_memory_bank/config/task.conf @@ -19,7 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_bank_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -openfpga_vpr_device_layout=2x2 +openfpga_vpr_device_layout=--device 2x2 openfpga_fast_configuration= [ARCHITECTURES] From 9808b61b36286c583d54836b4616482190967d23 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 20:06:39 -0600 Subject: [PATCH 140/164] [Tool] Bug fix on the unfit vector size of bit index register in Verilog testbench in some cases --- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 3a237160c..6534d5cd6 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1993,11 +1993,11 @@ void print_verilog_full_testbench_configuration_chain_bitstream(std::fstream& fp fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; fp << std::endl; - fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "):0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; BasicPort bit_skip_reg(TOP_TB_BITSTREAM_SKIP_FLAG_REG_NAME, 1); print_verilog_comment(fp, "----- Registers used for fast configuration logic -----"); - fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << ";" << std::endl; + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "):0] " << TOP_TB_BITSTREAM_ITERATOR_REG_NAME << ";" << std::endl; fp << generate_verilog_port(VERILOG_PORT_REG, bit_skip_reg) << ";" << std::endl; print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); @@ -2176,7 +2176,7 @@ void print_verilog_full_testbench_memory_bank_bitstream(std::fstream& fp, fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; fp << std::endl; - fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "):0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); fp << "initial begin" << std::endl; @@ -2312,7 +2312,7 @@ void print_verilog_full_testbench_frame_decoder_bitstream(std::fstream& fp, fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];"; fp << std::endl; - fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "):0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); fp << "initial begin" << std::endl; From 366dcff75d15ffc90b360383741ff691c6da2b03 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 21:49:31 -0600 Subject: [PATCH 141/164] [Tool] Now 'write_full_testbench' supports flatten(vanilla) configuration protocol --- .../write_text_fabric_bitstream.cpp | 86 +++------------ .../fpga_verilog/verilog_top_testbench.cpp | 101 ++++++++++++++++++ 2 files changed, 114 insertions(+), 73 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index fdd27f649..5338d7b09 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -40,65 +40,6 @@ void write_fabric_bitstream_text_file_head(std::fstream& fp) { fp << "// Date: " << std::ctime(&end_time); } -/******************************************************************** - * Write a configuration bit into a plain text file - * The format depends on the type of configuration protocol - * - Vanilla (standalone): just put down pure 0|1 bitstream - * - Configuration chain: just put down pure 0|1 bitstream - * - Memory bank : - * - Frame-based configuration protocol :
- * - * Return: - * - 0 if succeed - * - 1 if critical errors occured - *******************************************************************/ -static -int write_fabric_config_bit_to_text_file(std::fstream& fp, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream, - const FabricBitId& fabric_bit, - const e_config_protocol_type& config_type) { - if (false == valid_file_stream(fp)) { - return 1; - } - - switch (config_type) { - case CONFIG_MEM_STANDALONE: - case CONFIG_MEM_SCAN_CHAIN: - fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit)); - break; - case CONFIG_MEM_MEMORY_BANK: { - for (const char& addr_bit : fabric_bitstream.bit_bl_address(fabric_bit)) { - fp << addr_bit; - } - write_space_to_file(fp, 1); - for (const char& addr_bit : fabric_bitstream.bit_wl_address(fabric_bit)) { - fp << addr_bit; - } - write_space_to_file(fp, 1); - fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit)); - fp << "\n"; - break; - } - case CONFIG_MEM_FRAME_BASED: { - for (const char& addr_bit : fabric_bitstream.bit_address(fabric_bit)) { - fp << addr_bit; - } - write_space_to_file(fp, 1); - fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit)); - fp << "\n"; - break; - } - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid configuration protocol type!\n"); - return 1; - } - - return 0; -} - - /******************************************************************** * Write the flatten fabric bitstream to a plain text file * @@ -109,20 +50,20 @@ int write_fabric_config_bit_to_text_file(std::fstream& fp, static int write_flatten_fabric_bitstream_to_text_file(std::fstream& fp, const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream, - const ConfigProtocol& config_protocol) { - int status = 0; - for (const FabricBitId& fabric_bit : fabric_bitstream.bits()) { - status = write_fabric_config_bit_to_text_file(fp, bitstream_manager, - fabric_bitstream, - fabric_bit, - config_protocol.type()); - if (1 == status) { - return status; - } + const FabricBitstream& fabric_bitstream) { + if (false == valid_file_stream(fp)) { + return 1; } - return status; + /* Output bitstream size information */ + fp << "// Bitstream length: " << fabric_bitstream.num_bits() << std::endl; + + /* Output bitstream data */ + for (const FabricBitId& fabric_bit : fabric_bitstream.bits()) { + fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit)); + } + + return 0; } /******************************************************************** @@ -356,8 +297,7 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage case CONFIG_MEM_STANDALONE: status = write_flatten_fabric_bitstream_to_text_file(fp, bitstream_manager, - fabric_bitstream, - config_protocol); + fabric_bitstream); break; case CONFIG_MEM_SCAN_CHAIN: status = write_config_chain_fabric_bitstream_to_text_file(fp, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 6534d5cd6..c138a3bd9 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1937,6 +1937,101 @@ void print_verilog_top_testbench_bitstream(std::fstream& fp, } } +/******************************************************************** + * Print stimulus for a FPGA fabric with a flatten memory (standalone) configuration protocol + * We will load the bitstream in the second clock cycle, right after the first reset cycle + *******************************************************************/ +static +void print_verilog_full_testbench_vanilla_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const ModuleManager& module_manager, + const ModuleId& top_module, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Find Bit-Line and Word-Line port */ + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + + /* Find Bit-Line and Word-Line port */ + ModulePortId bl_port_id = module_manager.find_module_port(top_module, std::string(MEMORY_BL_PORT_NAME)); + BasicPort bl_port = module_manager.module_port(top_module, bl_port_id); + + ModulePortId wl_port_id = module_manager.find_module_port(top_module, std::string(MEMORY_WL_PORT_NAME)); + BasicPort wl_port = module_manager.module_port(top_module, wl_port_id); + + /* Define a constant for the bitstream length */ + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), fabric_bitstream.num_bits()); + + /* Declare local variables for bitstream loading in Verilog */ + print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----"); + fp << "reg [0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1] "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:0];"; + fp << std::endl; + + /* Initial value should be the first configuration bits + * In the rest of programming cycles, + * configuration bits are fed at the falling edge of programming clock. + * We do not care the value of scan_chain head during the first programming cycle + * It is reset anyway + */ + std::vector initial_bl_values(bl_port.get_width(), 0); + std::vector initial_wl_values(wl_port.get_width(), 0); + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + print_verilog_comment(fp, "----- Configuration chain default input -----"); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(bl_port, initial_bl_values); + fp << ";" << std::endl; + fp << "\t\t"; + fp << generate_verilog_port_constant_values(wl_port, initial_wl_values); + fp << ";" << std::endl; + + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); + fp << "\t"; + fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");"; + fp << std::endl; + + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ") begin" << std::endl; + + /* Enable all the WLs */ + std::vector enabled_wl_values(wl_port.get_width(), 1); + fp << "\t\t\t"; + fp << generate_verilog_port_constant_values(wl_port, enabled_wl_values); + fp << ";" << std::endl; + + fp << "\t\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, bl_port); + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0]"; + fp << ";" << std::endl; + + fp << "\t\tend" << std::endl; + + /* Disable all the WLs */ + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + + fp << "\t\t\t"; + fp << generate_verilog_port_constant_values(wl_port, initial_wl_values); + fp << ";" << std::endl; + + /* Raise the flag of configuration done when bitstream loading is complete */ + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << " <= "; + std::vector config_done_enable_values(config_done_port.get_width(), 1); + fp << generate_verilog_constant_values(config_done_enable_values); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + /******************************************************************** * Print stimulus for a FPGA fabric with a configuration chain protocol * where configuration bits are programming in serial (one by one) @@ -2411,6 +2506,12 @@ void print_verilog_full_testbench_bitstream(std::fstream& fp, /* Branch on the type of configuration protocol */ switch (config_protocol_type) { case CONFIG_MEM_STANDALONE: + print_verilog_full_testbench_vanilla_bitstream(fp, + bitstream_file, + module_manager, + top_module, + fabric_bitstream); + break; case CONFIG_MEM_SCAN_CHAIN: print_verilog_full_testbench_configuration_chain_bitstream(fp, bitstream_file, From 462326aaa532ad908467c2b345f865991aac3885 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 21:50:00 -0600 Subject: [PATCH 142/164] [Test] Update full testbench test case for flatten configuration protocol using 'write_full_testbench' --- .../full_testbench/flatten_memory/config/task.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf index adaf26a25..f980071c0 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_standalone_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 4aef9d5c9669631b396741370e390659c51dd80c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 7 Jun 2021 21:54:01 -0600 Subject: [PATCH 143/164] [Tool] Remove redundant codes --- openfpga/src/fpga_verilog/verilog_top_testbench.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index c138a3bd9..7d2bc98ef 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -3063,11 +3063,6 @@ int print_verilog_full_testbench(const ModuleManager& module_manager, netlist_annotation, explicit_port_mapping); - /* Print tasks used for loading bitstreams */ - print_verilog_top_testbench_load_bitstream_task(fp, - config_protocol.type(), - module_manager, top_module); - /* load bitstream to FPGA fabric in a configuration phase */ print_verilog_full_testbench_bitstream(fp, bitstream_file, From 5075c684185a10989d3f185f3d4debabb3affd20 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Jun 2021 20:58:04 -0600 Subject: [PATCH 144/164] [Tool] Remove duplicated codes on fast configuration --- .../fpga_verilog/verilog_top_testbench.cpp | 124 ++---------------- 1 file changed, 8 insertions(+), 116 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 7d2bc98ef..40c4fdb55 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1461,102 +1461,6 @@ void print_verilog_top_testbench_vanilla_bitstream(std::fstream& fp, print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); } -/******************************************************************** - * Decide if we should use reset or set signal to acheive fast configuration - * - If only one type signal is specified, we use that type - * For example, only reset signal is defined, we will use reset - * - If both are defined, pick the one that will bring bigger reduction - * i.e., larger number of configuration bits can be skipped - *******************************************************************/ -static -bool find_bit_value_to_skip_for_fast_configuration(const e_config_protocol_type& config_protocol_type, - const bool& fast_configuration, - const std::vector& global_prog_reset_ports, - const std::vector& global_prog_set_ports, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream) { - - /* Early exit conditions */ - if (!global_prog_reset_ports.empty() && global_prog_set_ports.empty()) { - return false; - } else if (!global_prog_set_ports.empty() && global_prog_reset_ports.empty()) { - return true; - } else if (global_prog_set_ports.empty() && global_prog_reset_ports.empty()) { - /* If both types of ports are not defined, the fast configuration should be turned off */ - VTR_ASSERT(false == fast_configuration); - return false; - } - - VTR_ASSERT(!global_prog_set_ports.empty() && !global_prog_reset_ports.empty()); - bool bit_value_to_skip = false; - - VTR_LOG("Both reset and set ports are defined for programming controls, selecting the best-fit one...\n"); - - size_t num_ones_to_skip = 0; - size_t num_zeros_to_skip = 0; - - /* Branch on the type of configuration protocol */ - switch (config_protocol_type) { - case CONFIG_MEM_STANDALONE: - break; - case CONFIG_MEM_SCAN_CHAIN: { - /* We can only skip the ones/zeros at the beginning of the bitstream */ - /* Count how many logic '1' bits we can skip */ - for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - if (false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { - break; - } - VTR_ASSERT(true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); - num_ones_to_skip++; - } - /* Count how many logic '0' bits we can skip */ - for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - if (true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { - break; - } - VTR_ASSERT(false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); - num_zeros_to_skip++; - } - break; - } - case CONFIG_MEM_MEMORY_BANK: - case CONFIG_MEM_FRAME_BASED: { - /* Count how many logic '1' and logic '0' bits we can skip */ - for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - if (false == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) { - num_zeros_to_skip++; - } else { - VTR_ASSERT(true == bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))); - num_ones_to_skip++; - } - } - break; - } - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid SRAM organization type!\n"); - exit(1); - } - - VTR_LOG("Using reset will skip %g% (%lu/%lu) of configuration bitstream.\n", - 100. * (float) num_zeros_to_skip / (float) fabric_bitstream.num_bits(), - num_zeros_to_skip, fabric_bitstream.num_bits()); - - VTR_LOG("Using set will skip %g% (%lu/%lu) of configuration bitstream.\n", - 100. * (float) num_ones_to_skip / (float) fabric_bitstream.num_bits(), - num_ones_to_skip, fabric_bitstream.num_bits()); - - /* By default, we prefer to skip zeros (when the numbers are the same */ - if (num_ones_to_skip > num_zeros_to_skip) { - VTR_LOG("Will use set signal in fast configuration\n"); - bit_value_to_skip = true; - } else { - VTR_LOG("Will use reset signal in fast configuration\n"); - } - - return bit_value_to_skip; -} - /******************************************************************** * Print stimulus for a FPGA fabric with a configuration chain protocol * where configuration bits are programming in serial (one by one) @@ -2699,17 +2603,11 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); /* Identify if we can apply fast configuration */ - bool apply_fast_configuration = fast_configuration; - if ( (global_prog_set_ports.empty() && global_prog_reset_ports.empty()) - && (true == fast_configuration)) { - VTR_LOG_WARN("None of global reset and set ports are defined for programming purpose. Fast configuration is turned off\n"); - apply_fast_configuration = false; - } + bool apply_fast_configuration = fast_configuration && is_fast_configuration_applicable(global_ports); bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), - apply_fast_configuration, - global_prog_reset_ports, - global_prog_set_ports, - bitstream_manager, fabric_bitstream); + global_ports, + bitstream_manager, + fabric_bitstream); /* Start of testbench */ print_verilog_top_testbench_ports(fp, module_manager, top_module, @@ -2958,17 +2856,11 @@ int print_verilog_full_testbench(const ModuleManager& module_manager, std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); /* Identify if we can apply fast configuration */ - bool apply_fast_configuration = fast_configuration; - if ( (global_prog_set_ports.empty() && global_prog_reset_ports.empty()) - && (true == fast_configuration)) { - VTR_LOG_WARN("None of global reset and set ports are defined for programming purpose. Fast configuration is turned off\n"); - apply_fast_configuration = false; - } + bool apply_fast_configuration = fast_configuration && is_fast_configuration_applicable(global_ports); bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), - apply_fast_configuration, - global_prog_reset_ports, - global_prog_set_ports, - bitstream_manager, fabric_bitstream); + global_ports, + bitstream_manager, + fabric_bitstream); /* Start of testbench */ print_verilog_top_testbench_ports(fp, module_manager, top_module, From 8db19c7af9ff07f6a7dcb0ddd3e0a003ba917aba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Jun 2021 21:28:16 -0600 Subject: [PATCH 145/164] [Tool] Add a new command 'write_preconfigured_fabric_wrapper' --- openfpga/src/base/openfpga_verilog.cpp | 42 +++++++- openfpga/src/base/openfpga_verilog.h | 5 +- .../src/base/openfpga_verilog_command.cpp | 101 +++++++++++++----- openfpga/src/fpga_verilog/verilog_api.cpp | 45 ++++++++ openfpga/src/fpga_verilog/verilog_api.h | 13 +++ 5 files changed, 180 insertions(+), 26 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 6cfb9879a..5ac0a49e0 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -122,7 +122,7 @@ int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, /******************************************************************** * A wrapper function to call the full testbench generator of FPGA-Verilog *******************************************************************/ -int write_full_testbench(OpenfpgaContext& openfpga_ctx, +int write_full_testbench(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { CommandOptionId opt_output_dir = cmd.option("file"); @@ -170,5 +170,45 @@ int write_full_testbench(OpenfpgaContext& openfpga_ctx, options); } +/******************************************************************** + * A wrapper function to call the preconfigured wrapper generator of FPGA-Verilog + *******************************************************************/ +int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); + CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); + CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog + * Keep it independent from any other outside data structures + */ + VerilogTestbenchOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); + options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_print_formal_verification_top_netlist(true); + + /* If pin constraints are enabled by command options, read the file */ + PinConstraints pin_constraints; + if (true == cmd_context.option_enable(cmd, opt_pcf)) { + pin_constraints = read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str()); + } + + return fpga_verilog_preconfigured_fabric_wrapper(openfpga_ctx.module_graph(), + openfpga_ctx.bitstream_manager(), + g_vpr_ctx.atom(), + g_vpr_ctx.placement(), + pin_constraints, + openfpga_ctx.io_location_map(), + openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.vpr_netlist_annotation(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol, + options); +} } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h index 20ce5d858..f755bf0bb 100644 --- a/openfpga/src/base/openfpga_verilog.h +++ b/openfpga/src/base/openfpga_verilog.h @@ -21,9 +21,12 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx, int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); -int write_full_testbench(OpenfpgaContext& openfpga_ctx, +int write_full_testbench(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 431eac47f..6a3c7c415 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -123,9 +123,9 @@ ShellCommandId add_openfpga_write_verilog_testbench_command(openfpga::Shell& shell, @@ -133,46 +133,88 @@ ShellCommandId add_openfpga_write_full_testbench_command(openfpga::Shell& dependent_cmds) { Command shell_cmd("write_full_testbench"); - /* Add an option '--file' in short '-f'*/ - CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for HDL netlists"); + /* add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "specify the output directory for hdl netlists"); shell_cmd.set_option_short_name(output_opt, "f"); shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); - /* Add an option '--bitstream'*/ - CommandOptionId bitstream_opt = shell_cmd.add_option("bitstream", true, "Specify the bitstream to be loaded in the testbench"); + /* add an option '--bitstream'*/ + CommandOptionId bitstream_opt = shell_cmd.add_option("bitstream", true, "specify the bitstream to be loaded in the testbench"); shell_cmd.set_option_require_value(bitstream_opt, openfpga::OPT_STRING); - /* Add an option '--fabric_netlist_file_path'*/ - CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "Specify the file path to the fabric HDL netlist"); + /* add an option '--fabric_netlist_file_path'*/ + CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "specify the file path to the fabric hdl netlist"); shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); - /* Add an option '--pin_constraints_file in short '-pcf' */ - CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints"); + /* add an option '--pin_constraints_file in short '-pcf' */ + CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "specify the file path to the pin constraints"); shell_cmd.set_option_short_name(pcf_opt, "pcf"); shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); - /* Add an option '--reference_benchmark_file_path'*/ - CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); + /* add an option '--reference_benchmark_file_path'*/ + CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "specify the file path to the reference verilog netlist"); shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); - /* Add an option '--fast_configuration' */ - shell_cmd.add_option("fast_configuration", false, "Reduce the period of configuration by skip certain data points"); + /* add an option '--fast_configuration' */ + shell_cmd.add_option("fast_configuration", false, "reduce the period of configuration by skip certain data points"); - /* Add an option '--explicit_port_mapping' */ - shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); + /* add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, "use explicit port mapping in verilog netlists"); - /* Add an option '--include_signal_init' */ - shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog testbenches"); + /* add an option '--include_signal_init' */ + shell_cmd.add_option("include_signal_init", false, "initialize all the signals in verilog testbenches"); - /* Add an option '--verbose' */ - shell_cmd.add_option("verbose", false, "Enable verbose output"); + /* add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "enable verbose output"); - /* Add command to the Shell */ - ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate full testbenches for an FPGA fabric"); + /* add command to the shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate full testbenches for an fpga fabric"); shell.set_command_class(shell_cmd_id, cmd_class_id); shell.set_command_execute_function(shell_cmd_id, write_full_testbench); - /* Add command dependency to the Shell */ + /* add command dependency to the shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - add a command to shell environment: write preconfigured fabric wrapper + * - add associated options + * - add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_write_preconfigured_fabric_wrapper_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_preconfigured_fabric_wrapper"); + + /* add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "specify the output directory for hdl netlists"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* add an option '--fabric_netlist_file_path'*/ + CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "specify the file path to the fabric hdl netlist"); + shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); + + /* add an option '--pin_constraints_file in short '-pcf' */ + CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "specify the file path to the pin constraints"); + shell_cmd.set_option_short_name(pcf_opt, "pcf"); + shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); + + /* add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, "use explicit port mapping in verilog netlists"); + + /* add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "enable verbose output"); + + /* add command to the shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate a wrapper for a pre-configured fpga fabric"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_preconfigured_fabric_wrapper); + + /* add command dependency to the shell */ shell.set_command_dependency(shell_cmd_id, dependent_cmds); return shell_cmd_id; @@ -214,6 +256,17 @@ void add_openfpga_verilog_commands(openfpga::Shell& shell) { add_openfpga_write_full_testbench_command(shell, openfpga_verilog_cmd_class, full_testbench_dependent_cmds); + + /******************************** + * Command 'write_preconfigured_fabric_wrapper' + */ + /* The command 'write_preconfigured_fabric_wrapper' should NOT be executed before 'build_fabric' */ + std::vector preconfig_wrapper_dependent_cmds; + preconfig_wrapper_dependent_cmds.push_back(build_fabric_cmd_id); + add_openfpga_write_preconfigured_fabric_wrapper_command(shell, + openfpga_verilog_cmd_class, + preconfig_wrapper_dependent_cmds); + } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index fa0980eea..d7628570e 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -313,4 +313,49 @@ int fpga_verilog_full_testbench(const ModuleManager &module_manager, return status; } +/******************************************************************** + * A top-level function of FPGA-Verilog which focuses on full testbench generation + * This function will generate + * - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark + ********************************************************************/ +int fpga_verilog_preconfigured_fabric_wrapper(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const PinConstraints& pin_constraints, + const IoLocationMap &io_location_map, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, + const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options) { + + vtr::ScopedStartFinishTimer timer("Write a wrapper module for a preconfigured FPGA fabric\n"); + + std::string src_dir_path = format_dir_path(options.output_directory()); + + std::string netlist_name = atom_ctx.nlist.netlist_name(); + + int status = CMD_EXEC_SUCCESS; + + /* Create directories */ + create_directory(src_dir_path); + + /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ + std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); + status = print_verilog_preconfig_top_module(module_manager, bitstream_manager, + config_protocol, + circuit_lib, fabric_global_port_info, + atom_ctx, place_ctx, + pin_constraints, + io_location_map, + netlist_annotation, + netlist_name, + formal_verification_top_netlist_file_path, + options.explicit_port_mapping()); + + return status; +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index f3f0b8321..f8665a0e8 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -72,6 +72,19 @@ int fpga_verilog_full_testbench(const ModuleManager& module_manager, const ConfigProtocol& config_protocol, const VerilogTestbenchOption& options); +int fpga_verilog_preconfigured_fabric_wrapper(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const PinConstraints& pin_constraints, + const IoLocationMap &io_location_map, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, + const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options); + + } /* end namespace openfpga */ #endif From 85679c0fe2292eeb1b2b14dfb7f74158295be8d8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Jun 2021 21:32:26 -0600 Subject: [PATCH 146/164] [Tool] Bug fix in the top testbench switch due to fast configuration --- .../fpga_verilog/verilog_top_testbench.cpp | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 40c4fdb55..0eb5f39f0 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2604,10 +2604,13 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, /* Identify if we can apply fast configuration */ bool apply_fast_configuration = fast_configuration && is_fast_configuration_applicable(global_ports); - bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), - global_ports, - bitstream_manager, - fabric_bitstream); + bool bit_value_to_skip = false; + if (true == apply_fast_configuration) { + bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), + global_ports, + bitstream_manager, + fabric_bitstream); + } /* Start of testbench */ print_verilog_top_testbench_ports(fp, module_manager, top_module, @@ -2857,10 +2860,13 @@ int print_verilog_full_testbench(const ModuleManager& module_manager, /* Identify if we can apply fast configuration */ bool apply_fast_configuration = fast_configuration && is_fast_configuration_applicable(global_ports); - bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), - global_ports, - bitstream_manager, - fabric_bitstream); + bool bit_value_to_skip = false; + if (true == apply_fast_configuration) { + bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), + global_ports, + bitstream_manager, + fabric_bitstream); + } /* Start of testbench */ print_verilog_top_testbench_ports(fp, module_manager, top_module, From d2275b971df31ad074cb4a0c7adc5bb239235eb8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Jun 2021 21:53:51 -0600 Subject: [PATCH 147/164] [Tool] Add a new command 'write_preconfigured_testbench' --- openfpga/src/base/openfpga_verilog.cpp | 40 ++++++++++++++ openfpga/src/base/openfpga_verilog.h | 3 + .../src/base/openfpga_verilog_command.cpp | 55 +++++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.cpp | 50 +++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.h | 7 +++ 5 files changed, 155 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 5ac0a49e0..f62c9da71 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -180,6 +180,7 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_verbose = cmd.option("verbose"); /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog @@ -190,6 +191,7 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator)); options.set_print_formal_verification_top_netlist(true); /* If pin constraints are enabled by command options, read the file */ @@ -211,4 +213,42 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, options); } +/******************************************************************** + * A wrapper function to call the preconfigured testbench generator of FPGA-Verilog + *******************************************************************/ +int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); + CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); + CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog + * Keep it independent from any other outside data structures + */ + VerilogTestbenchOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); + options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_print_preconfig_top_testbench(true); + + /* If pin constraints are enabled by command options, read the file */ + PinConstraints pin_constraints; + if (true == cmd_context.option_enable(cmd, opt_pcf)) { + pin_constraints = read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str()); + } + + return fpga_verilog_preconfigured_testbench(openfpga_ctx.module_graph(), + g_vpr_ctx.atom(), + pin_constraints, + openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.vpr_netlist_annotation(), + openfpga_ctx.simulation_setting(), + options); +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h index f755bf0bb..d7c0b5404 100644 --- a/openfpga/src/base/openfpga_verilog.h +++ b/openfpga/src/base/openfpga_verilog.h @@ -27,6 +27,9 @@ int write_full_testbench(const OpenfpgaContext& openfpga_ctx, int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 6a3c7c415..f9d4acc65 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -206,6 +206,9 @@ ShellCommandId add_openfpga_write_preconfigured_fabric_wrapper_command(openfpga: /* add an option '--explicit_port_mapping' */ shell_cmd.add_option("explicit_port_mapping", false, "use explicit port mapping in verilog netlists"); + /* Add an option '--support_icarus_simulator' */ + shell_cmd.add_option("support_icarus_simulator", false, "Fine-tune Verilog testbenches to support icarus simulator"); + /* add an option '--verbose' */ shell_cmd.add_option("verbose", false, "enable verbose output"); @@ -220,6 +223,48 @@ ShellCommandId add_openfpga_write_preconfigured_fabric_wrapper_command(openfpga: return shell_cmd_id; } +/******************************************************************** + * - Add a command to Shell environment: write preconfigured testbench + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_write_preconfigured_testbench_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_preconfigured_testbench"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for HDL netlists"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* Add an option '--pin_constraints_file in short '-pcf' */ + CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints"); + shell_cmd.set_option_short_name(pcf_opt, "pcf"); + shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); + + /* Add an option '--reference_benchmark_file_path'*/ + CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); + shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); + + /* Add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate testbenches for a preconfigured FPGA fabric"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_preconfigured_testbench); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + void add_openfpga_verilog_commands(openfpga::Shell& shell) { /* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ const ShellCommandId& build_fabric_cmd_id = shell.command(std::string("build_fabric")); @@ -267,6 +312,16 @@ void add_openfpga_verilog_commands(openfpga::Shell& shell) { openfpga_verilog_cmd_class, preconfig_wrapper_dependent_cmds); + /******************************** + * Command 'write_preconfigured_testbench' + */ + /* The command 'write_preconfigured_testbench' should NOT be executed before 'build_fabric' */ + std::vector preconfig_testbench_dependent_cmds; + preconfig_testbench_dependent_cmds.push_back(build_fabric_cmd_id); + add_openfpga_write_preconfigured_testbench_command(shell, + openfpga_verilog_cmd_class, + preconfig_testbench_dependent_cmds); + } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index d7628570e..3c1624c1a 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -357,5 +357,55 @@ int fpga_verilog_preconfigured_fabric_wrapper(const ModuleManager &module_manage return status; } +/******************************************************************** + * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation + * This function will generate + * - Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module. + * This testbench is created for quick verification and formal verification purpose. + ********************************************************************/ +int fpga_verilog_preconfigured_testbench(const ModuleManager &module_manager, + const AtomContext &atom_ctx, + const PinConstraints& pin_constraints, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const SimulationSetting &simulation_setting, + const VerilogTestbenchOption &options) { + + vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for a preconfigured FPGA fabric\n"); + + std::string src_dir_path = format_dir_path(options.output_directory()); + + std::string netlist_name = atom_ctx.nlist.netlist_name(); + + int status = CMD_EXEC_SUCCESS; + + /* Create directories */ + create_directory(src_dir_path); + + /* Output preprocessing flags for HDL simulations */ + print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + options); + + /* Generate top-level testbench using random vectors */ + std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_random_top_testbench(netlist_name, + random_top_testbench_file_path, + atom_ctx, + netlist_annotation, + module_manager, + fabric_global_port_info, + pin_constraints, + simulation_setting, + options.explicit_port_mapping()); + + /* Generate a Verilog file including all the netlists that have been generated */ + print_verilog_testbench_include_netlists(src_dir_path, + netlist_name, + options.fabric_netlist_file_path(), + options.reference_benchmark_file_path()); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index f8665a0e8..da6685f3b 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -84,6 +84,13 @@ int fpga_verilog_preconfigured_fabric_wrapper(const ModuleManager &module_manage const ConfigProtocol &config_protocol, const VerilogTestbenchOption &options); +int fpga_verilog_preconfigured_testbench(const ModuleManager &module_manager, + const AtomContext &atom_ctx, + const PinConstraints& pin_constraints, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const SimulationSetting &simulation_setting, + const VerilogTestbenchOption &options); } /* end namespace openfpga */ From 97396eda2be9439fddaad543a2350a329e806e9c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Jun 2021 22:10:02 -0600 Subject: [PATCH 148/164] [Tool] Add a new command 'write_simulation_task_info' --- openfpga/src/base/openfpga_verilog.cpp | 27 ++++++++++++ openfpga/src/base/openfpga_verilog.h | 3 ++ .../src/base/openfpga_verilog_command.cpp | 43 +++++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.cpp | 42 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.h | 9 ++++ 5 files changed, 124 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index f62c9da71..f3b6a7d9a 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -250,5 +250,32 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, options); } +/******************************************************************** + * A wrapper function to call the simulation task information generator of FPGA-Verilog + *******************************************************************/ +int write_simulation_task_info(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog + * Keep it independent from any other outside data structures + */ + VerilogTestbenchOption options; + options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_output_dir)); + + return fpga_verilog_simulation_task_info(openfpga_ctx.module_graph(), + openfpga_ctx.bitstream_manager(), + g_vpr_ctx.atom(), + g_vpr_ctx.placement(), + openfpga_ctx.io_location_map(), + openfpga_ctx.simulation_setting(), + openfpga_ctx.arch().config_protocol, + options); +} } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h index d7c0b5404..7789335a8 100644 --- a/openfpga/src/base/openfpga_verilog.h +++ b/openfpga/src/base/openfpga_verilog.h @@ -30,6 +30,9 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_simulation_task_info(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index f9d4acc65..72115e987 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -265,6 +265,40 @@ ShellCommandId add_openfpga_write_preconfigured_testbench_command(openfpga::Shel return shell_cmd_id; } +/******************************************************************** + * - Add a command to Shell environment: write simulation task info + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_write_simulation_task_info_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_simulation_task_info"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the file path to output simulation-related information"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* Add an option '--reference_benchmark_file_path'*/ + CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); + shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate an interchangable simulation task configuration file"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_simulation_task_info); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + void add_openfpga_verilog_commands(openfpga::Shell& shell) { /* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ const ShellCommandId& build_fabric_cmd_id = shell.command(std::string("build_fabric")); @@ -322,6 +356,15 @@ void add_openfpga_verilog_commands(openfpga::Shell& shell) { openfpga_verilog_cmd_class, preconfig_testbench_dependent_cmds); + /******************************** + * Command 'write_simulation_task_info' + */ + /* The command 'write_simulation_task_info' should NOT be executed before 'build_fabric' */ + std::vector sim_task_info_dependent_cmds; + sim_task_info_dependent_cmds.push_back(build_fabric_cmd_id); + add_openfpga_write_simulation_task_info_command(shell, + openfpga_verilog_cmd_class, + sim_task_info_dependent_cmds); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 3c1624c1a..d7ed62f64 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -407,5 +407,47 @@ int fpga_verilog_preconfigured_testbench(const ModuleManager &module_manager, return status; } +/******************************************************************** + * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation + * This function will generate + * - An interchangable file containing simulation task configuration + ********************************************************************/ +int fpga_verilog_simulation_task_info(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const IoLocationMap &io_location_map, + const SimulationSetting &simulation_setting, + const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options) { + + vtr::ScopedStartFinishTimer timer("Write interchangeable simulation task configuration\n"); + + std::string src_dir_path = format_dir_path(options.output_directory()); + + std::string netlist_name = atom_ctx.nlist.netlist_name(); + + int status = CMD_EXEC_SUCCESS; + + /* Create directories */ + create_directory(src_dir_path); + + /* Generate exchangeable files which contains simulation settings */ + std::string simulation_ini_file_name = options.simulation_ini_path(); + VTR_ASSERT(true != options.simulation_ini_path().empty()); + print_verilog_simulation_info(simulation_ini_file_name, + netlist_name, + src_dir_path, + atom_ctx, place_ctx, io_location_map, + module_manager, + config_protocol.type(), + bitstream_manager.num_bits(), + simulation_setting.num_clock_cycles(), + simulation_setting.programming_clock_frequency(), + simulation_setting.default_operating_clock_frequency()); + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index da6685f3b..0bfb397f8 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -92,6 +92,15 @@ int fpga_verilog_preconfigured_testbench(const ModuleManager &module_manager, const SimulationSetting &simulation_setting, const VerilogTestbenchOption &options); +int fpga_verilog_simulation_task_info(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const IoLocationMap &io_location_map, + const SimulationSetting &simulation_setting, + const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options); + } /* end namespace openfpga */ #endif From be26c06673e399edd8d5a6494342d9cea2d9acee Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 10:41:22 -0600 Subject: [PATCH 149/164] [Script] Update an example script to use 'write_preconfigured_fabric_wrapper' and 'write_preconfigured_testbench' in place of 'write_verilog_testbench' --- .../behavioral_verilog_example_script.openfpga | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga index f8b7245b9..6e17e025f 100644 --- a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga @@ -55,17 +55,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --support_icarus_simulator --explicit_port_mapping - -# Write the SDC files for PnR backend -# - Turn on every options here -write_pnr_sdc --file ./SDC - -# Write SDC to disable timing for configure ports -write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc - -# Write the SDC to run timing analysis for a mapped FPGA fabric -write_analysis_sdc --file ./SDC_analysis +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Finish and exit OpenFPGA exit From 89fb67263179c1477fb7f6c18df9f1aafa4cef2a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 10:49:00 -0600 Subject: [PATCH 150/164] [Tool] Fine-tune the options of 'write_simulation_task_info' to be straightforward to use --- openfpga/src/base/openfpga_verilog.cpp | 6 ++++-- openfpga/src/base/openfpga_verilog_command.cpp | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index f3b6a7d9a..50cd9ba87 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -256,7 +256,8 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, int write_simulation_task_info(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { - CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_file = cmd.option("file"); + CommandOptionId opt_hdl_dir = cmd.option("hdl_dir"); CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -264,9 +265,10 @@ int write_simulation_task_info(const OpenfpgaContext& openfpga_ctx, * Keep it independent from any other outside data structures */ VerilogTestbenchOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_hdl_dir)); options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); - options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_output_dir)); + options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_file)); return fpga_verilog_simulation_task_info(openfpga_ctx.module_graph(), openfpga_ctx.bitstream_manager(), diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 72115e987..471e8784b 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -281,6 +281,10 @@ ShellCommandId add_openfpga_write_simulation_task_info_command(openfpga::Shell Date: Wed, 9 Jun 2021 11:14:45 -0600 Subject: [PATCH 151/164] [Tool] Add '--fabric_netlist' option to 'write_preconfigured_testbench' command --- openfpga/src/base/openfpga_verilog.cpp | 2 ++ openfpga/src/base/openfpga_verilog_command.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 50cd9ba87..a14031c03 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -221,6 +221,7 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); + CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -230,6 +231,7 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, */ VerilogTestbenchOption options; options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 471e8784b..f022ac779 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -239,6 +239,10 @@ ShellCommandId add_openfpga_write_preconfigured_testbench_command(openfpga::Shel shell_cmd.set_option_short_name(output_opt, "f"); shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + /* add an option '--fabric_netlist_file_path'*/ + CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "specify the file path to the fabric hdl netlist"); + shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); + /* Add an option '--pin_constraints_file in short '-pcf' */ CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints"); shell_cmd.set_option_short_name(pcf_opt, "pcf"); From 9adf94bfd37efbb1f5944e19b3702ef031ee0b21 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 11:18:52 -0600 Subject: [PATCH 152/164] [Script] Update all the openshell scripts to deprecate 'write_verilog_testbench' --- .../bitstream_setting_example_script.openfpga | 3 ++- .../configuration_chain_example_script.openfpga | 3 ++- .../custom_fabric_netlist_example_script.openfpga | 2 +- .../duplicated_grid_pin_example_script.openfpga | 3 ++- openfpga_flow/openfpga_shell_scripts/example_script.openfpga | 4 +++- .../example_without_ace_script.openfpga | 4 +++- .../fast_configuration_example_script.openfpga | 2 +- .../fix_device_example_script.openfpga | 4 +++- ...obal_tile_clock_bitstream_setting_example_script.openfpga | 4 +++- .../fix_device_global_tile_clock_example_script.openfpga | 4 +++- .../fix_device_route_chan_width_example_script.openfpga | 4 +++- .../fix_heterogeneous_device_example_script.openfpga | 5 +++-- .../flatten_routing_example_script.openfpga | 4 +++- .../full_testbench_example_script.openfpga | 2 +- .../generate_secure_fabric_example_script.openfpga | 4 +++- .../generate_secure_fabric_from_key_example_script.openfpga | 4 +++- .../generate_testbench_example_script.openfpga | 4 ++++ .../global_tile_clock_example_script.openfpga | 4 +++- .../global_tile_multiclock_example_script.openfpga | 4 +++- .../implicit_verilog_example_script.openfpga | 4 +++- .../openfpga_shell_scripts/iverilog_example_script.openfpga | 4 +++- .../load_external_arch_bitstream_example_script.openfpga | 2 +- .../openfpga_shell_scripts/mcnc_example_script.openfpga | 3 ++- .../skywater_tapeout_example_script.openfpga | 4 +++- .../verilog_default_net_type_example_script.openfpga | 4 +++- 25 files changed, 64 insertions(+), 25 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga index a5cbbaf16..304aa0830 100644 --- a/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga @@ -58,7 +58,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator #--explicit_port_mapping +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga index 67c1419bd..d6a6eaa24 100644 --- a/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga @@ -55,7 +55,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga index 3bc847bdf..00cd426b9 100644 --- a/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga @@ -55,7 +55,7 @@ write_fabric_verilog --file ./FABRIC_NETLIST --explicit_port_mapping --include_t # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --fabric_netlist_file_path ${OPENFPGA_FABRIC_NETLIST_FILE} --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --fabric_netlist_file_path ${OPENFPGA_FABRIC_NETLIST_FILE} --explicit_port_mapping --include_signal_init # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga index 227e1fdda..3d9ea31d5 100644 --- a/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga @@ -55,7 +55,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga index 18b7c97f2..573e0d683 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga index 3c1be9114..5ba6156f7 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga index 0699d4ea6..f4f786e73 100644 --- a/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping --fast_configuration +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fast_configuration # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga index ef37d9242..65c278217 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga index dabb0c6c6..fcc402c14 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga @@ -58,7 +58,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator #--explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga index b5fd710b6..7bc46ced1 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga @@ -57,7 +57,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga index 054f74c0e..137138902 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga index 1d6cca82f..a4a1e68c5 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga @@ -55,8 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator #--explicit_port_mapping - +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here write_pnr_sdc --file ./SDC diff --git a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga index dccde3d8c..6e974623d 100644 --- a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga index 18b7c97f2..f2ddca635 100644 --- a/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga index b7a856dfd..2c3653bae 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga @@ -58,7 +58,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga index 8d8d07d32..30e4eb5a9 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga @@ -58,7 +58,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga index 197c350bd..4db82b16c 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga @@ -53,6 +53,10 @@ write_fabric_bitstream --file fabric_bitstream --format xml # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} + # Write the SDC to run timing analysis for a mapped FPGA fabric write_analysis_sdc --file ./SDC_analysis diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga index b418685ab..2809dafb1 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga @@ -57,7 +57,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator #--explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga index 93af8bdb1..0b1179e3e 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga @@ -61,7 +61,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} #--explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga index 6c793bc88..a335baf75 100644 --- a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --include_timing --print_user_defined_template # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga index e161d8e2f..769163de8 100644 --- a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga @@ -52,7 +52,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga index 976087ded..2830f5fc4 100644 --- a/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga @@ -58,7 +58,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga index 6c753df2f..1f41dfc84 100644 --- a/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga @@ -56,7 +56,8 @@ write_fabric_verilog --file ./SRC \ # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ./${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./simulation_deck_info.ini +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga index e9195e9e7..f21b4d1ce 100644 --- a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga index 6cb86439c..92d6bfbcc 100644 --- a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga @@ -55,7 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend # - Turn on every options here From f9404dc97d5ec7ca11be6a8f2c6f2c7af2e0f713 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 11:55:25 -0600 Subject: [PATCH 153/164] [Script] Patch openfpga shell script due to missing a mandatory option in 'write_full_testbench' --- .../custom_fabric_netlist_example_script.openfpga | 4 ++-- .../openfpga_shell_scripts/example_script.openfpga | 4 ++-- .../example_without_ace_script.openfpga | 4 ++-- .../fast_configuration_example_script.openfpga | 4 ++-- .../fix_device_example_script.openfpga | 4 ++-- ...bal_tile_clock_bitstream_setting_example_script.openfpga | 4 ++-- .../fix_device_global_tile_clock_example_script.openfpga | 4 ++-- .../fix_device_route_chan_width_example_script.openfpga | 4 ++-- .../fix_heterogeneous_device_example_script.openfpga | 4 ++-- .../flatten_routing_example_script.openfpga | 4 ++-- .../full_testbench_example_script.openfpga | 4 ++-- .../generate_secure_fabric_example_script.openfpga | 4 ++-- .../generate_secure_fabric_from_key_example_script.openfpga | 4 ++-- .../generate_testbench_example_script.openfpga | 6 ++---- .../global_tile_clock_example_script.openfpga | 4 ++-- .../global_tile_multiclock_example_script.openfpga | 4 ++-- .../implicit_verilog_example_script.openfpga | 4 ++-- .../openfpga_shell_scripts/iverilog_example_script.openfpga | 5 ++++- .../load_external_arch_bitstream_example_script.openfpga | 4 ++-- .../skywater_tapeout_example_script.openfpga | 4 ++-- .../verilog_default_net_type_example_script.openfpga | 4 ++-- 21 files changed, 44 insertions(+), 43 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga index 00cd426b9..1e1b46d23 100644 --- a/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/custom_fabric_netlist_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./FABRIC_NETLIST --explicit_port_mapping --include_t # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --fabric_netlist_file_path ${OPENFPGA_FABRIC_NETLIST_FILE} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --fabric_netlist_file_path ${OPENFPGA_FABRIC_NETLIST_FILE} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga index 573e0d683..450e1898c 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga index 5ba6156f7..f127f69ac 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} diff --git a/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga index f4f786e73..77af24936 100644 --- a/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fast_configuration +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fast_configuration --bitstream fabric_bitstream.bit # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga index 65c278217..acaf5bdee 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga index fcc402c14..b93e9672e 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga @@ -46,7 +46,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -58,7 +58,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga index 7bc46ced1..58ad5efec 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga @@ -45,7 +45,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga index 137138902..af1af2f1f 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitsream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga index a4a1e68c5..3b173d694 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} # Write the SDC files for PnR backend diff --git a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga index 6e974623d..ce5e43570 100644 --- a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga index f2ddca635..84c107af9 100644 --- a/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/full_testbench_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga index 2c3653bae..be6014b73 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga @@ -46,7 +46,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -58,7 +58,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga index 30e4eb5a9..e975263d5 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga @@ -46,7 +46,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -58,7 +58,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga index 4db82b16c..2c90659b7 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog testbench for FPGA fabric # - We suggest the use of same output directory as fabric Verilog netlists @@ -51,9 +51,7 @@ write_fabric_bitstream --file fabric_bitstream --format xml # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --include_signal_init --support_icarus_simulator --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} - -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga index 2809dafb1..647c8b627 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga @@ -45,7 +45,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga index 0b1179e3e..fad8dd893 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga @@ -49,7 +49,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -61,7 +61,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} diff --git a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga index a335baf75..d08a051a2 100644 --- a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --include_timing --print_user_defined_template # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} diff --git a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga index 769163de8..1a1bf0eb1 100644 --- a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga @@ -42,6 +42,9 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream # Build fabric-dependent bitstream build_fabric_bitstream --verbose +# Write fabric-dependent bitstream +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text + # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose @@ -52,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} diff --git a/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga index 2830f5fc4..1a94138a4 100644 --- a/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/load_external_arch_bitstream_example_script.openfpga @@ -46,7 +46,7 @@ build_architecture_bitstream --verbose \ build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -58,7 +58,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga index f21b4d1ce..93700ded6 100644 --- a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping diff --git a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga index 92d6bfbcc..cff7cd35c 100644 --- a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga @@ -43,7 +43,7 @@ build_architecture_bitstream --verbose --write_file fabric_independent_bitstream build_fabric_bitstream --verbose # Write fabric-dependent bitstream -write_fabric_bitstream --file fabric_bitstream.xml --format xml +write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist @@ -55,7 +55,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping From 2299ce3157a8e01a3903538a4b47f85016004e43 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 13:49:25 -0600 Subject: [PATCH 154/164] [Tool] Preconfigured testbench writer now supports icarus simulator --- openfpga/src/base/openfpga_verilog.cpp | 2 ++ openfpga/src/base/openfpga_verilog_command.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index a14031c03..cc737554b 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -223,6 +223,7 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); + CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -233,6 +234,7 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); + options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator)); options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_print_preconfig_top_testbench(true); diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index f022ac779..79388c4b9 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -252,6 +252,9 @@ ShellCommandId add_openfpga_write_preconfigured_testbench_command(openfpga::Shel CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); + /* Add an option '--support_icarus_simulator' */ + shell_cmd.add_option("support_icarus_simulator", false, "Fine-tune Verilog testbenches to support icarus simulator"); + /* Add an option '--explicit_port_mapping' */ shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); From 4e3f589810f75486996b77d8fad9a35676ff2c99 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 13:53:28 -0600 Subject: [PATCH 155/164] [Script] Patch openfpga shell script to use the new option '--support_icarus_simulator' for 'write_preconfigured_testbench' --- .../behavioral_verilog_example_script.openfpga | 2 +- .../bitstream_setting_example_script.openfpga | 2 +- .../configuration_chain_example_script.openfpga | 2 +- .../duplicated_grid_pin_example_script.openfpga | 2 +- openfpga_flow/openfpga_shell_scripts/example_script.openfpga | 2 +- .../openfpga_shell_scripts/example_without_ace_script.openfpga | 2 +- .../openfpga_shell_scripts/fix_device_example_script.openfpga | 2 +- ...global_tile_clock_bitstream_setting_example_script.openfpga | 2 +- .../fix_device_global_tile_clock_example_script.openfpga | 2 +- .../fix_device_route_chan_width_example_script.openfpga | 2 +- .../fix_heterogeneous_device_example_script.openfpga | 3 ++- .../flatten_routing_example_script.openfpga | 2 +- .../generate_secure_fabric_example_script.openfpga | 2 +- .../generate_secure_fabric_from_key_example_script.openfpga | 2 +- .../generate_testbench_example_script.openfpga | 2 +- .../global_tile_clock_example_script.openfpga | 2 +- .../global_tile_multiclock_example_script.openfpga | 2 +- .../implicit_verilog_example_script.openfpga | 2 +- .../openfpga_shell_scripts/iverilog_example_script.openfpga | 2 +- .../openfpga_shell_scripts/mcnc_example_script.openfpga | 2 +- .../skywater_tapeout_example_script.openfpga | 2 +- .../verilog_default_net_type_example_script.openfpga | 2 +- 22 files changed, 23 insertions(+), 22 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga index 6e17e025f..d27d4eff6 100644 --- a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga @@ -56,7 +56,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Finish and exit OpenFPGA exit diff --git a/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga index 304aa0830..2d4820510 100644 --- a/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/bitstream_setting_example_script.openfpga @@ -59,7 +59,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga index d6a6eaa24..34e56c3b9 100644 --- a/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga @@ -56,7 +56,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga index 3d9ea31d5..cf9cbd8b5 100644 --- a/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/duplicated_grid_pin_example_script.openfpga @@ -56,7 +56,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga index 450e1898c..71e6fd224 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga index f127f69ac..b5a9fe352 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga index acaf5bdee..94a9a41c5 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga index b93e9672e..ea4f9912e 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_bitstream_setting_example_script.openfpga @@ -60,7 +60,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga index 58ad5efec..c40e79c79 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_global_tile_clock_example_script.openfpga @@ -59,7 +59,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga index af1af2f1f..d31ffe36c 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitsream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga index 3b173d694..79430e976 100644 --- a/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fix_heterogeneous_device_example_script.openfpga @@ -57,7 +57,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator + # Write the SDC files for PnR backend # - Turn on every options here write_pnr_sdc --file ./SDC diff --git a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga index ce5e43570..4511ba34d 100644 --- a/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/flatten_routing_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga index be6014b73..571ceee8f 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_example_script.openfpga @@ -60,7 +60,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga index e975263d5..857b20981 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_example_script.openfpga @@ -60,7 +60,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga index 2c90659b7..19487e48b 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_testbench_example_script.openfpga @@ -53,7 +53,7 @@ write_fabric_bitstream --file fabric_bitstream.bit --format plain_text # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --fabric_netlist_file_path ${OPENFPGA_FABRIC_VERILOG_NETLIST} --support_icarus_simulator # Write the SDC to run timing analysis for a mapped FPGA fabric write_analysis_sdc --file ./SDC_analysis diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga index 647c8b627..bc56dc8ee 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_clock_example_script.openfpga @@ -59,7 +59,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga index fad8dd893..5fe8cd4b5 100644 --- a/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga @@ -63,7 +63,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga index d08a051a2..c0752db76 100644 --- a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --include_timing --print_user_defined_template # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga index 1a1bf0eb1..d44e57a24 100644 --- a/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/iverilog_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga index 1f41dfc84..56b5c9caf 100644 --- a/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mcnc_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC \ # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga index 93700ded6..7cfe311f3 100644 --- a/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/skywater_tapeout_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga index cff7cd35c..77f01d371 100644 --- a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga @@ -57,7 +57,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here From c62666e7c3da09678e61ce6e4453eb7f123eb0ce Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 14:24:34 -0600 Subject: [PATCH 156/164] [Test] Use proper template for some failing tests --- .../tasks/basic_tests/fixed_device_support/config/task.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/fixed_device_support/config/task.conf b/openfpga_flow/tasks/basic_tests/fixed_device_support/config/task.conf index 3fea70cef..d80ebe9c4 100644 --- a/openfpga_flow/tasks/basic_tests/fixed_device_support/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/fixed_device_support/config/task.conf @@ -16,11 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -openfpga_vpr_device_layout=2x2 -openfpga_vpr_route_chan_width=40 +openfpga_vpr_device_layout=--device 2x2 --route_chan_width 40 +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 52c0ed571bad2c5d545ed6e15a907d52ee32fee9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 14:27:02 -0600 Subject: [PATCH 157/164] [Test] Patch test case to use proper template --- .../synthesizable_verilog/config/task.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/verilog_netlist_formats/synthesizable_verilog/config/task.conf b/openfpga_flow/tasks/fpga_verilog/verilog_netlist_formats/synthesizable_verilog/config/task.conf index 22fe003b9..0e60465b0 100644 --- a/openfpga_flow/tasks/fpga_verilog/verilog_netlist_formats/synthesizable_verilog/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/verilog_netlist_formats/synthesizable_verilog/config/task.conf @@ -16,12 +16,12 @@ timeout_each_job = 20*60 fpga_flow=vpr_blif [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga #openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_stdcell_mux_40nm_openfpga_synthesizable.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -openfpga_vpr_device_layout=auto -openfpga_vpr_route_chan_width=20 +openfpga_vpr_device_layout=--device auto --route_chan_width 20 +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml From d545069aacb2fba6e478d1e9b26f5fcaa43fcc93 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 14:50:37 -0600 Subject: [PATCH 158/164] [Script] Bug fix --- .../example_without_ace_script.openfpga | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga index b5a9fe352..61c5ea8af 100644 --- a/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga @@ -55,9 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit -write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --support_icarus_simulator +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --bitstream fabric_bitstream.bit +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE} --support_icarus_simulator # Write the SDC files for PnR backend # - Turn on every options here From eed30605d7667a4f19d228f7ac83eb3f4b61bbf0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 15:20:55 -0600 Subject: [PATCH 159/164] [Test] patch test case --- .../power_gated_design/power_gated_inverter/config/task.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf b/openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf index 1e919ff29..9bc003ff1 100644 --- a/openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf @@ -16,9 +16,11 @@ timeout_each_job = 20*60 fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fast_configuration_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_powergate_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From b719419931e8171dd08d216724dfc805ec067e40 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 16:59:02 -0600 Subject: [PATCH 160/164] [Doc] Update documentation on the FPGA-Verilog commands in openfpga shell; Deprecated the 'write_verilog_testbench' command --- .../fpga_verilog_commands.rst | 140 +++++++++++------- 1 file changed, 90 insertions(+), 50 deletions(-) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index 8b559c698..5c57d104e 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -6,7 +6,7 @@ FPGA-Verilog write_fabric_verilog ~~~~~~~~~~~~~~~~~~~~ - Write the Verilog netlist for FPGA fabric based on module graph + Write the Verilog netlist for FPGA fabric based on module graph. See details in :ref:`fabric_netlists`. .. option:: --file or -f @@ -40,58 +40,10 @@ write_fabric_verilog Show verbose log -write_verilog_testbench -~~~~~~~~~~~~~~~~~~~~~~~ - - Write the Verilog testbench for FPGA fabric - - .. option:: --file or -f - - The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` - - .. option:: --fabric_netlist_file_path - - Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v`` - - .. option:: --reference_benchmark_file_path - - Must specify the reference benchmark Verilog file if you want to output any testbenches. For example, ``--reference_benchmark_file_path /temp/benchmark/counter_post_synthesis.v`` - - .. option:: --pin_constraints_file or -pcf - - Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` - Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. - - .. option:: --fast_configuration - - Enable fast configuration phase for the top-level testbench in order to reduce runtime of simulations. It is applicable to configuration chain, memory bank and frame-based configuration protocols. For configuration chain, when enabled, the zeros at the head of the bitstream will be skipped. For memory bank and frame-based, when enabled, all the zero configuration bits will be skipped. So ensure that your memory cells can be correctly reset to zero with a reset signal. - - .. note:: If both reset and set ports are defined in the circuit modeling for programming, OpenFPGA will pick the one that will bring largest benefit in speeding up configuration. - - .. option:: --print_top_testbench - - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA - - .. option:: --print_formal_verification_top_netlist - - Generate a top-level module which can be used in formal verification - - .. option:: --print_preconfig_top_testbench - - Enable pre-configured top-level testbench which is a fast verification skipping programming phase - - .. option:: --print_simulation_ini - - Output an exchangeable simulation ini file, which is needed only when you need to interface different HDL simulators using openfpga flow-run scripts. For example, ``--print_simulation_ini /temp/testbench/sim.ini`` - - .. option:: --explicit_port_mapping - - Use explicit port mapping when writing the Verilog netlists - write_full_testbench ~~~~~~~~~~~~~~~~~~~~~~~ - Write the full testbench for FPGA fabric in Verilog format + Write the full testbench for FPGA fabric in Verilog format. See details in :ref:`fpga_verilog_testbench`. .. option:: --file or -f @@ -128,4 +80,92 @@ write_full_testbench Output signal initialization to Verilog testbench to smooth convergence in HDL simulation + .. option:: --verbose + Show verbose log + +write_preconfigured_fabric_wrapper +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Write the Verilog wrapper for a preconfigured FPGA fabric. See details in :ref:`fpga_verilog_testbench`. + + .. option:: --file or -f + + The output directory for the netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + + .. option:: --fabric_netlist_file_path + + Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v`` + + .. option:: --pin_constraints_file or -pcf + + Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` + Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. + + .. option:: --explicit_port_mapping + + Use explicit port mapping when writing the Verilog netlists + + .. option:: --support_icarus_simulator + + Output Verilog netlists with syntax that iVerilog simulator can accept + + .. option:: --verbose + + Show verbose log + +write_preconfigured_testbench +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Write the Verilog testbench for a preconfigured FPGA fabric. See details in :ref:`fpga_verilog_testbench`. + + .. option:: --file or -f + + The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + + .. option:: --fabric_netlist_file_path + + Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v`` + + .. option:: --reference_benchmark_file_path + + Must specify the reference benchmark Verilog file if you want to output any testbenches. For example, ``--reference_benchmark_file_path /temp/benchmark/counter_post_synthesis.v`` + + .. option:: --pin_constraints_file or -pcf + + Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` + Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. + + .. option:: --explicit_port_mapping + + Use explicit port mapping when writing the Verilog netlists + + .. option:: --support_icarus_simulator + + Output Verilog netlists with syntax that iVerilog simulator can accept + + .. option:: --verbose + + Show verbose log + +write_simulation_task_info +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Write an interchangeable file in ``.ini`` format to interface HDL simulators, such as iVerilog and Modelsim. + + .. option:: --file or -f + + Specify the file path to output simulation-related information. For example, ``--file simulation.ini`` + + .. option:: --hdl_dir + + Specify the directory path where HDL netlists are created. For example, ``--hdl_dir ./SRC`` + + .. option:: --reference_benchmark_file_path + + Must specify the reference benchmark Verilog file if you want to output any testbenches. For example, ``--reference_benchmark_file_path /temp/benchmark/counter_post_synthesis.v`` + + .. option:: --verbose + + Show verbose log + From 7ade48343cec8fa51c0f2b40f912c9043c950485 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 9 Jun 2021 17:06:01 -0600 Subject: [PATCH 161/164] [Tool] Deprecate command 'write_verilog_testbench' --- openfpga/src/base/openfpga_verilog.cpp | 58 -- openfpga/src/base/openfpga_verilog.h | 3 - .../src/base/openfpga_verilog_command.cpp | 78 -- openfpga/src/fpga_verilog/verilog_api.cpp | 113 --- openfpga/src/fpga_verilog/verilog_api.h | 14 - .../fpga_verilog/verilog_top_testbench.cpp | 951 ------------------ .../src/fpga_verilog/verilog_top_testbench.h | 16 - 7 files changed, 1233 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index cc737554b..e279f1200 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -61,64 +61,6 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx, return CMD_EXEC_SUCCESS; } -/******************************************************************** - * A wrapper function to call the Verilog testbench generator of FPGA-Verilog - *******************************************************************/ -int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, - const Command& cmd, const CommandContext& cmd_context) { - - CommandOptionId opt_output_dir = cmd.option("file"); - CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); - CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); - CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); - CommandOptionId opt_print_top_testbench = cmd.option("print_top_testbench"); - CommandOptionId opt_fast_configuration = cmd.option("fast_configuration"); - CommandOptionId opt_print_formal_verification_top_netlist = cmd.option("print_formal_verification_top_netlist"); - CommandOptionId opt_print_preconfig_top_testbench = cmd.option("print_preconfig_top_testbench"); - CommandOptionId opt_print_simulation_ini = cmd.option("print_simulation_ini"); - CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); - CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); - CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); - CommandOptionId opt_verbose = cmd.option("verbose"); - - /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog - * Keep it independent from any other outside data structures - */ - VerilogTestbenchOption options; - options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); - options.set_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist)); - options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark)); - options.set_print_formal_verification_top_netlist(cmd_context.option_enable(cmd, opt_print_formal_verification_top_netlist)); - options.set_print_preconfig_top_testbench(cmd_context.option_enable(cmd, opt_print_preconfig_top_testbench)); - options.set_fast_configuration(cmd_context.option_enable(cmd, opt_fast_configuration)); - options.set_print_top_testbench(cmd_context.option_enable(cmd, opt_print_top_testbench)); - options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_print_simulation_ini)); - options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); - options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init)); - options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator)); - options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); - - /* If pin constraints are enabled by command options, read the file */ - PinConstraints pin_constraints; - if (true == cmd_context.option_enable(cmd, opt_pcf)) { - pin_constraints = read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str()); - } - - return fpga_verilog_testbench(openfpga_ctx.module_graph(), - openfpga_ctx.bitstream_manager(), - openfpga_ctx.fabric_bitstream(), - g_vpr_ctx.atom(), - g_vpr_ctx.placement(), - pin_constraints, - openfpga_ctx.io_location_map(), - openfpga_ctx.fabric_global_port_info(), - openfpga_ctx.vpr_netlist_annotation(), - openfpga_ctx.arch().circuit_lib, - openfpga_ctx.simulation_setting(), - openfpga_ctx.arch().config_protocol, - options); -} - /******************************************************************** * A wrapper function to call the full testbench generator of FPGA-Verilog *******************************************************************/ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h index 7789335a8..3bbd7fab3 100644 --- a/openfpga/src/base/openfpga_verilog.h +++ b/openfpga/src/base/openfpga_verilog.h @@ -18,9 +18,6 @@ namespace openfpga { int write_fabric_verilog(OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); -int write_verilog_testbench(OpenfpgaContext& openfpga_ctx, - const Command& cmd, const CommandContext& cmd_context); - int write_full_testbench(const OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 79388c4b9..174b8a4d7 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -54,74 +54,6 @@ ShellCommandId add_openfpga_write_fabric_verilog_command(openfpga::Shell& shell, - const ShellCommandClassId& cmd_class_id, - const std::vector& dependent_cmds) { - Command shell_cmd("write_verilog_testbench"); - - /* Add an option '--file' in short '-f'*/ - CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for Verilog netlists"); - shell_cmd.set_option_short_name(output_opt, "f"); - shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); - - /* Add an option '--fabric_netlist_file_path'*/ - CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "Specify the file path to the fabric Verilog netlist"); - shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); - - /* Add an option '--pin_constraints_file in short '-pcf' */ - CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints"); - shell_cmd.set_option_short_name(pcf_opt, "pcf"); - shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); - - /* Add an option '--reference_benchmark_file_path'*/ - CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist"); - shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING); - - /* Add an option '--print_top_testbench' */ - shell_cmd.add_option("print_top_testbench", false, "Generate a full testbench for top-level fabric module with autocheck capability"); - - /* Add an option '--fast_configuration' */ - shell_cmd.add_option("fast_configuration", false, "Reduce the period of configuration by skip zero data points"); - - /* Add an option '--print_formal_verification_top_netlist' */ - shell_cmd.add_option("print_formal_verification_top_netlist", false, "Generate a top-level module which can be used in formal verification"); - - /* Add an option '--print_preconfig_top_testbench' */ - shell_cmd.add_option("print_preconfig_top_testbench", false, "Generate a pre-configured testbench for top-level fabric module with autocheck capability"); - - /* Add an option '--print_simulation_ini' */ - CommandOptionId sim_ini_opt = shell_cmd.add_option("print_simulation_ini", false, "Generate a .ini file as an exchangeable file to enable HDL simulations"); - shell_cmd.set_option_require_value(sim_ini_opt, openfpga::OPT_STRING); - - /* Add an option '--explicit_port_mapping' */ - shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); - - /* Add an option '--include_signal_init' */ - shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog testbenches"); - - /* Add an option '--support_icarus_simulator' */ - shell_cmd.add_option("support_icarus_simulator", false, "Fine-tune Verilog testbenches to support icarus simulator"); - - /* Add an option '--verbose' */ - shell_cmd.add_option("verbose", false, "Enable verbose output"); - - /* Add command to the Shell */ - ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate Verilog testbenches for full FPGA fabric"); - shell.set_command_class(shell_cmd_id, cmd_class_id); - shell.set_command_execute_function(shell_cmd_id, write_verilog_testbench); - - /* Add command dependency to the Shell */ - shell.set_command_dependency(shell_cmd_id, dependent_cmds); - - return shell_cmd_id; -} - /******************************************************************** * - add a command to shell environment: write full testbench * - add associated options @@ -327,16 +259,6 @@ void add_openfpga_verilog_commands(openfpga::Shell& shell) { openfpga_verilog_cmd_class, fabric_verilog_dependent_cmds); - /******************************** - * Command 'write_verilog_testbench' - */ - /* The command 'write_verilog_testbench' should NOT be executed before 'build_fabric' */ - std::vector verilog_testbench_dependent_cmds; - verilog_testbench_dependent_cmds.push_back(build_fabric_cmd_id); - add_openfpga_write_verilog_testbench_command(shell, - openfpga_verilog_cmd_class, - verilog_testbench_dependent_cmds); - /******************************** * Command 'write_full_testbench' */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index d7ed62f64..f114f6d36 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -139,119 +139,6 @@ void fpga_fabric_verilog(ModuleManager &module_manager, module_manager.num_modules()); } -/******************************************************************** - * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation - * This function will generate - * - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark - * - Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors - * - Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module. - * This testbench is created for quick verification and formal verification purpose. - * - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated - ********************************************************************/ -int fpga_verilog_testbench(const ModuleManager &module_manager, - const BitstreamManager &bitstream_manager, - const FabricBitstream &fabric_bitstream, - const AtomContext &atom_ctx, - const PlacementContext &place_ctx, - const PinConstraints& pin_constraints, - const IoLocationMap &io_location_map, - const FabricGlobalPortInfo &fabric_global_port_info, - const VprNetlistAnnotation &netlist_annotation, - const CircuitLibrary &circuit_lib, - const SimulationSetting &simulation_setting, - const ConfigProtocol &config_protocol, - const VerilogTestbenchOption &options) { - - vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); - - std::string src_dir_path = format_dir_path(options.output_directory()); - - std::string netlist_name = atom_ctx.nlist.netlist_name(); - - int status = CMD_EXEC_SUCCESS; - - /* Create directories */ - create_directory(src_dir_path); - - /* Output preprocessing flags for HDL simulations */ - print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), - options); - - /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ - if (true == options.print_formal_verification_top_netlist()) { - std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); - status = print_verilog_preconfig_top_module(module_manager, bitstream_manager, - config_protocol, - circuit_lib, fabric_global_port_info, - atom_ctx, place_ctx, - pin_constraints, - io_location_map, - netlist_annotation, - netlist_name, - formal_verification_top_netlist_file_path, - options.explicit_port_mapping()); - if (status == CMD_EXEC_FATAL_ERROR) { - return status; - } - } - - if (true == options.print_preconfig_top_testbench()) { - /* Generate top-level testbench using random vectors */ - std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_random_top_testbench(netlist_name, - random_top_testbench_file_path, - atom_ctx, - netlist_annotation, - module_manager, - fabric_global_port_info, - pin_constraints, - simulation_setting, - options.explicit_port_mapping()); - } - - /* Generate full testbench for verification, including configuration phase and operating phase */ - if (true == options.print_top_testbench()) { - std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_top_testbench(module_manager, - bitstream_manager, fabric_bitstream, - circuit_lib, - config_protocol, - fabric_global_port_info, - atom_ctx, place_ctx, - pin_constraints, - io_location_map, - netlist_annotation, - netlist_name, - top_testbench_file_path, - simulation_setting, - options); - } - - /* Generate exchangeable files which contains simulation settings */ - if (true == options.print_simulation_ini()) { - std::string simulation_ini_file_name = options.simulation_ini_path(); - VTR_ASSERT(true != options.simulation_ini_path().empty()); - print_verilog_simulation_info(simulation_ini_file_name, - netlist_name, - src_dir_path, - atom_ctx, place_ctx, io_location_map, - module_manager, - config_protocol.type(), - bitstream_manager.num_bits(), - simulation_setting.num_clock_cycles(), - simulation_setting.programming_clock_frequency(), - simulation_setting.default_operating_clock_frequency()); - } - - /* Generate a Verilog file including all the netlists that have been generated */ - print_verilog_testbench_include_netlists(src_dir_path, - netlist_name, - options.fabric_netlist_file_path(), - options.reference_benchmark_file_path()); - - return status; -} - /******************************************************************** * A top-level function of FPGA-Verilog which focuses on full testbench generation * This function will generate diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 0bfb397f8..934dc08f7 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -43,20 +43,6 @@ void fpga_fabric_verilog(ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, const FabricVerilogOption& options); -int fpga_verilog_testbench(const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream, - const AtomContext& atom_ctx, - const PlacementContext& place_ctx, - const PinConstraints& pin_constraints, - const IoLocationMap& io_location_map, - const FabricGlobalPortInfo &fabric_global_port_info, - const VprNetlistAnnotation& netlist_annotation, - const CircuitLibrary& circuit_lib, - const SimulationSetting& simulation_parameters, - const ConfigProtocol& config_protocol, - const VerilogTestbenchOption& options); - int fpga_verilog_full_testbench(const ModuleManager& module_manager, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 0eb5f39f0..779b6ceaa 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -945,229 +945,6 @@ void print_verilog_top_testbench_benchmark_instance(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * Print tasks (processes) in Verilog format, - * which is very useful in generating stimuli for each clock cycle - * This function is tuned for configuration-chain manipulation: - * During each programming cycle, we feed the input of scan chain with a memory bit - *******************************************************************/ -static -void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module) { - - /* Validate the file stream */ - valid_file_stream(fp); - - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name()); - BasicPort cc_head_port = module_manager.module_port(top_module, cc_head_port_id); - BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), cc_head_port.get_width()); - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Feed the scan-chain input at each falling edge of programming clock - * It aims at avoid racing the programming clock (scan-chain data changes at the rising edge). - */ - print_verilog_comment(fp, std::string("----- Task: input values during a programming clock cycle -----")); - fp << "task " << std::string(TOP_TESTBENCH_PROG_TASK_NAME) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, cc_head_value) << ";" << std::endl; - fp << "\tbegin" << std::endl; - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_value); - fp << ";" << std::endl; - - fp << "\tend" << std::endl; - fp << "endtask" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; -} - -/******************************************************************** - * Print tasks (processes) in Verilog format, - * which is very useful in generating stimuli for each clock cycle - * This function is tuned for memory bank manipulation: - * During each programming cycle, we feed - * - an address to the BL address port of top module - * - an address to the WL address port of top module - * - a data input to the din port of top module - *******************************************************************/ -static -void print_verilog_top_testbench_load_bitstream_task_memory_bank(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module) { - - /* Validate the file stream */ - valid_file_stream(fp); - - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - - ModulePortId bl_addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_BL_ADDRESS_PORT_NAME)); - BasicPort bl_addr_port = module_manager.module_port(top_module, bl_addr_port_id); - BasicPort bl_addr_value = bl_addr_port; - bl_addr_value.set_name(std::string(MEMORY_BL_PORT_NAME) + std::string("_val")); - - ModulePortId wl_addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_WL_ADDRESS_PORT_NAME)); - BasicPort wl_addr_port = module_manager.module_port(top_module, wl_addr_port_id); - BasicPort wl_addr_value = wl_addr_port; - wl_addr_value.set_name(std::string(MEMORY_WL_PORT_NAME) + std::string("_val")); - - ModulePortId din_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_DATA_IN_PORT_NAME)); - BasicPort din_port = module_manager.module_port(top_module, din_port_id); - BasicPort din_value = din_port; - din_value.set_name(std::string(DECODER_DATA_IN_PORT_NAME) + std::string("_val")); - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Feed the address and data input at each falling edge of programming clock - * As the enable signal is wired to the programming clock, we should synchronize - * address and data with the enable signal - */ - print_verilog_comment(fp, std::string("----- Task: assign BL and WL address, and data values at rising edge of enable signal -----")); - fp << "task " << std::string(TOP_TESTBENCH_PROG_TASK_NAME) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, bl_addr_value) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, wl_addr_value) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, din_value) << ";" << std::endl; - fp << "\tbegin" << std::endl; - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, bl_addr_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, bl_addr_value); - fp << ";" << std::endl; - fp << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, wl_addr_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, wl_addr_value); - fp << ";" << std::endl; - fp << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, din_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, din_value); - fp << ";" << std::endl; - fp << std::endl; - - fp << "\tend" << std::endl; - fp << "endtask" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; -} - - -/******************************************************************** - * Print tasks (processes) in Verilog format, - * which is very useful in generating stimuli for each clock cycle - * This function is tuned for frame-based memory manipulation: - * During each programming cycle, we feed - * - an address to the address port of top module - * - a data input to the din port of top module - *******************************************************************/ -static -void print_verilog_top_testbench_load_bitstream_task_frame_decoder(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module) { - - /* Validate the file stream */ - valid_file_stream(fp); - - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - - ModulePortId addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_ADDRESS_PORT_NAME)); - BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); - BasicPort addr_value = addr_port; - addr_value.set_name(std::string(DECODER_ADDRESS_PORT_NAME) + std::string("_val")); - - ModulePortId din_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_DATA_IN_PORT_NAME)); - BasicPort din_port = module_manager.module_port(top_module, din_port_id); - BasicPort din_value = din_port; - din_value.set_name(std::string(DECODER_DATA_IN_PORT_NAME) + std::string("_val")); - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Feed the address and data input at each falling edge of programming clock - * As the enable signal is wired to the programming clock, we should synchronize - * address and data with the enable signal - */ - print_verilog_comment(fp, std::string("----- Task: assign address and data values at rising edge of enable signal -----")); - fp << "task " << std::string(TOP_TESTBENCH_PROG_TASK_NAME) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, addr_value) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_INPUT, din_value) << ";" << std::endl; - fp << "\tbegin" << std::endl; - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, addr_value); - fp << ";" << std::endl; - fp << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, din_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, din_value); - fp << ";" << std::endl; - fp << std::endl; - - fp << "\tend" << std::endl; - fp << "endtask" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; -} - -/******************************************************************** - * Print tasks, which is very useful in generating stimuli for each clock cycle - *******************************************************************/ -static -void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp, - const e_config_protocol_type& sram_orgz_type, - const ModuleManager& module_manager, - const ModuleId& top_module) { - switch (sram_orgz_type) { - case CONFIG_MEM_STANDALONE: - /* No need to have a specific task. Loading is done in 1 clock cycle */ - break; - case CONFIG_MEM_SCAN_CHAIN: - print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp, - module_manager, - top_module); - break; - case CONFIG_MEM_MEMORY_BANK: - print_verilog_top_testbench_load_bitstream_task_memory_bank(fp, - module_manager, - top_module); - break; - case CONFIG_MEM_FRAME_BASED: - print_verilog_top_testbench_load_bitstream_task_frame_decoder(fp, - module_manager, - top_module); - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid type of SRAM organization!\n"); - exit(1); - } -} - /******************************************************************** * Print generatic input stimuli for the top testbench * include: @@ -1370,477 +1147,6 @@ void print_verilog_top_testbench_configuration_protocol_stimulus(std::fstream& f } } -/******************************************************************** - * Print stimulus for a FPGA fabric with a flatten memory (standalone) configuration protocol - * We will load the bitstream in the second clock cycle, right after the first reset cycle - *******************************************************************/ -static -void print_verilog_top_testbench_vanilla_bitstream(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream) { - /* Validate the file stream */ - valid_file_stream(fp); - - /* Find Bit-Line and Word-Line port */ - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - - /* Find Bit-Line and Word-Line port */ - ModulePortId bl_port_id = module_manager.find_module_port(top_module, std::string(MEMORY_BL_PORT_NAME)); - BasicPort bl_port = module_manager.module_port(top_module, bl_port_id); - - ModulePortId wl_port_id = module_manager.find_module_port(top_module, std::string(MEMORY_WL_PORT_NAME)); - BasicPort wl_port = module_manager.module_port(top_module, wl_port_id); - - /* Initial value should be the first configuration bits - * In the rest of programming cycles, - * configuration bits are fed at the falling edge of programming clock. - * We do not care the value of scan_chain head during the first programming cycle - * It is reset anyway - */ - std::vector initial_bl_values(bl_port.get_width(), 0); - std::vector initial_wl_values(wl_port.get_width(), 0); - - print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); - fp << "initial" << std::endl; - fp << "\tbegin" << std::endl; - print_verilog_comment(fp, "----- Configuration chain default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(bl_port, initial_bl_values); - fp << ";" << std::endl; - fp << "\t\t"; - fp << generate_verilog_port_constant_values(wl_port, initial_wl_values); - fp << ";" << std::endl; - - fp << std::endl; - - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ") begin" << std::endl; - - /* Enable all the WLs */ - std::vector enabled_wl_values(wl_port.get_width(), 1); - fp << "\t\t\t"; - fp << generate_verilog_port_constant_values(wl_port, enabled_wl_values); - fp << ";" << std::endl; - - size_t ibit = 0; - for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - BasicPort cur_bl_port(bl_port); - cur_bl_port.set_width(ibit, ibit); - - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, cur_bl_port); - fp << " = "; - fp << "1'b" << (size_t)bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id)); - fp << ";" << std::endl; - - ibit++; - } - - fp << "\t\tend" << std::endl; - - /* Disable all the WLs */ - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - fp << "\t\t\t"; - fp << generate_verilog_port_constant_values(wl_port, initial_wl_values); - fp << ";" << std::endl; - - /* Raise the flag of configuration done when bitstream loading is complete */ - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); - fp << " <= "; - std::vector config_done_enable_values(config_done_port.get_width(), 1); - fp << generate_verilog_constant_values(config_done_enable_values); - fp << ";" << std::endl; - - fp << "\tend" << std::endl; - print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); -} - -/******************************************************************** - * Print stimulus for a FPGA fabric with a configuration chain protocol - * where configuration bits are programming in serial (one by one) - * Task list: - * 1. For clock signal, we should create voltage waveforms for two types of clock signals: - * a. operation clock - * b. programming clock - * 2. For Set/Reset, we reset the chip after programming phase ends - * and before operation phase starts - * 3. For input/output clb nets (mapped to I/O grids), - * we should create voltage waveforms only after programming phase - *******************************************************************/ -static -void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp, - const bool& fast_configuration, - const bool& bit_value_to_skip, - const ModuleManager& module_manager, - const ModuleId& top_module, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream) { - /* Validate the file stream */ - valid_file_stream(fp); - - /* Initial value should be the first configuration bits - * In the rest of programming cycles, - * configuration bits are fed at the falling edge of programming clock. - * We do not care the value of scan_chain head during the first programming cycle - * It is reset anyway - */ - ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name()); - BasicPort config_chain_head_port = module_manager.module_port(top_module, cc_head_port_id); - std::vector initial_values(config_chain_head_port.get_width(), 0); - - print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); - fp << "initial" << std::endl; - fp << "\tbegin" << std::endl; - print_verilog_comment(fp, "----- Configuration chain default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values); - fp << ";"; - - fp << std::endl; - - /* Find the longest bitstream */ - size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream); - - /* For fast configuration, the bitstream size counts from the first bit '1' */ - size_t num_bits_to_skip = 0; - if (true == fast_configuration) { - num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip); - } - VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size); - - /* Reorganize the regional bitstreams to be the same size */ - ConfigChainFabricBitstream regional_bitstreams = build_config_chain_fabric_bitstream_by_region(bitstream_manager, fabric_bitstream); - - /* Attention: when the fast configuration is enabled, we will start from the first bit '1' - * This requires a reset signal (as we forced in the first clock cycle) - * - * Note that bitstream may come from different regions - * The bitstream value to be loaded should be organized as follows - * - * cycleA - * | - * Region 0: 0|00000001111101010 - * Region 1: | 00000011010101 - * Region 2: | 0010101111000110 - * - * Zero bits will be added to the head of those bitstreams are shorter - * than the longest bitstream - */ - for (size_t ibit = num_bits_to_skip; ibit < regional_bitstream_max_size; ++ibit) { - std::vector curr_cc_head_val; - curr_cc_head_val.reserve(fabric_bitstream.regions().size()); - for (const auto& region_bitstream : regional_bitstreams) { - curr_cc_head_val.push_back((size_t)region_bitstream[ibit]); - } - - fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); - fp << "(" << generate_verilog_constant_values(curr_cc_head_val) << ");" << std::endl; - } - - /* Raise the flag of configuration done when bitstream loading is complete */ - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); - fp << " <= "; - std::vector config_done_enable_values(config_done_port.get_width(), 1); - fp << generate_verilog_constant_values(config_done_enable_values); - fp << ";" << std::endl; - - fp << "\tend" << std::endl; - print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); -} - -/******************************************************************** - * Print stimulus for a FPGA fabric with a memory bank configuration protocol - * where configuration bits are programming in serial (one by one) - * - * We will use the programming task function created before - *******************************************************************/ -static -void print_verilog_top_testbench_memory_bank_bitstream(std::fstream& fp, - const bool& fast_configuration, - const bool& bit_value_to_skip, - const ModuleManager& module_manager, - const ModuleId& top_module, - const FabricBitstream& fabric_bitstream) { - /* Validate the file stream */ - valid_file_stream(fp); - - /* Feed addresss and data input pair one by one - * Note: the first cycle is reserved for programming reset - * We should give dummy values - */ - ModulePortId bl_addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_BL_ADDRESS_PORT_NAME)); - BasicPort bl_addr_port = module_manager.module_port(top_module, bl_addr_port_id); - std::vector initial_bl_addr_values(bl_addr_port.get_width(), 0); - - ModulePortId wl_addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_WL_ADDRESS_PORT_NAME)); - BasicPort wl_addr_port = module_manager.module_port(top_module, wl_addr_port_id); - std::vector initial_wl_addr_values(wl_addr_port.get_width(), 0); - - ModulePortId din_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_DATA_IN_PORT_NAME)); - BasicPort din_port = module_manager.module_port(top_module, din_port_id); - std::vector initial_din_values(din_port.get_width(), 0); - - print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); - fp << "initial" << std::endl; - fp << "\tbegin" << std::endl; - print_verilog_comment(fp, "----- Address port default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(bl_addr_port, initial_bl_addr_values); - fp << ";"; - fp << std::endl; - - fp << "\t\t"; - fp << generate_verilog_port_constant_values(wl_addr_port, initial_wl_addr_values); - fp << ";"; - fp << std::endl; - - print_verilog_comment(fp, "----- Data-input port default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(din_port, initial_din_values); - fp << ";"; - - fp << std::endl; - - /* Reorganize the fabric bitstream by the same address across regions */ - MemoryBankFabricBitstream fabric_bits_by_addr = build_memory_bank_fabric_bitstream_by_address(fabric_bitstream); - - for (const auto& addr_din_pair : fabric_bits_by_addr) { - /* When fast configuration is enabled, - * the rule to skip any configuration bit should consider the whole data input values. - * Only all the bits in the din port match the value to be skipped, - * the programming cycle can be skipped! - */ - if (true == fast_configuration) { - bool skip_curr_bits = true; - for (const bool& bit : addr_din_pair.second) { - if (bit_value_to_skip != bit) { - skip_curr_bits = false; - break; - } - } - - if (true == skip_curr_bits) { - continue; - } - } - - fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); - fp << "(" << bl_addr_port.get_width() << "'b"; - VTR_ASSERT(bl_addr_port.get_width() == addr_din_pair.first.first.length()); - fp << addr_din_pair.first.first; - - fp << ", "; - fp << wl_addr_port.get_width() << "'b"; - VTR_ASSERT(wl_addr_port.get_width() == addr_din_pair.first.second.length()); - fp << addr_din_pair.first.second; - - fp << ", "; - fp << din_port.get_width() << "'b"; - VTR_ASSERT(din_port.get_width() == addr_din_pair.second.size()); - for (const bool& din_value : addr_din_pair.second) { - if (true == din_value) { - fp << "1"; - } else { - VTR_ASSERT(false == din_value); - fp << "0"; - } - } - fp << ");" << std::endl; - } - - /* Raise the flag of configuration done when bitstream loading is complete */ - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); - fp << " <= "; - std::vector config_done_enable_values(config_done_port.get_width(), 1); - fp << generate_verilog_constant_values(config_done_enable_values); - fp << ";" << std::endl; - - fp << "\tend" << std::endl; - print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); -} - -/******************************************************************** - * Print stimulus for a FPGA fabric with a frame-based configuration protocol - * where configuration bits are programming in serial (one by one) - * - * We will use the programming task function created before - *******************************************************************/ -static -void print_verilog_top_testbench_frame_decoder_bitstream(std::fstream& fp, - const bool& fast_configuration, - const bool& bit_value_to_skip, - const ModuleManager& module_manager, - const ModuleId& top_module, - const FabricBitstream& fabric_bitstream) { - /* Validate the file stream */ - valid_file_stream(fp); - - /* Feed addresss and data input pair one by one - * Note: the first cycle is reserved for programming reset - * We should give dummy values - */ - ModulePortId addr_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_ADDRESS_PORT_NAME)); - BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); - std::vector initial_addr_values(addr_port.get_width(), 0); - - ModulePortId din_port_id = module_manager.find_module_port(top_module, - std::string(DECODER_DATA_IN_PORT_NAME)); - BasicPort din_port = module_manager.module_port(top_module, din_port_id); - std::vector initial_din_values(din_port.get_width(), 0); - - print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); - fp << "initial" << std::endl; - fp << "\tbegin" << std::endl; - print_verilog_comment(fp, "----- Address port default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(addr_port, initial_addr_values); - fp << ";"; - fp << std::endl; - - print_verilog_comment(fp, "----- Data-input port default input -----"); - fp << "\t\t"; - fp << generate_verilog_port_constant_values(din_port, initial_din_values); - fp << ";"; - - fp << std::endl; - - /* Reorganize the fabric bitstream by the same address across regions */ - FrameFabricBitstream fabric_bits_by_addr = build_frame_based_fabric_bitstream_by_address(fabric_bitstream); - - for (const auto& addr_din_pair : fabric_bits_by_addr) { - /* When fast configuration is enabled, - * the rule to skip any configuration bit should consider the whole data input values. - * Only all the bits in the din port match the value to be skipped, - * the programming cycle can be skipped! - */ - if (true == fast_configuration) { - bool skip_curr_bits = true; - for (const bool& bit : addr_din_pair.second) { - if (bit_value_to_skip != bit) { - skip_curr_bits = false; - break; - } - } - - if (true == skip_curr_bits) { - continue; - } - } - - fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); - fp << "(" << addr_port.get_width() << "'b"; - VTR_ASSERT(addr_port.get_width() == addr_din_pair.first.size()); - fp << addr_din_pair.first; - fp << ", "; - fp << din_port.get_width() << "'b"; - VTR_ASSERT(din_port.get_width() == addr_din_pair.second.size()); - for (const bool& din_value : addr_din_pair.second) { - if (true == din_value) { - fp << "1"; - } else { - VTR_ASSERT(false == din_value); - fp << "0"; - } - } - fp << ");" << std::endl; - } - - /* Disable the address and din - fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); - fp << "(" << addr_port.get_width() << "'b"; - std::vector all_zero_addr(addr_port.get_width(), 0); - for (const size_t& addr_bit : all_zero_addr) { - fp << addr_bit; - } - fp << ", "; - fp << generate_verilog_constant_values(initial_din_values); - fp << ");" << std::endl; - */ - - /* Raise the flag of configuration done when bitstream loading is complete */ - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); - fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; - - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); - fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); - fp << " <= "; - std::vector config_done_enable_values(config_done_port.get_width(), 1); - fp << generate_verilog_constant_values(config_done_enable_values); - fp << ";" << std::endl; - - fp << "\tend" << std::endl; - print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); -} - -/******************************************************************** - * Generate the stimuli for the top-level testbench - * The simulation consists of two phases: configuration phase and operation phase - * Configuration bits are loaded serially. - * This is actually what we do for a physical FPGA - *******************************************************************/ -static -void print_verilog_top_testbench_bitstream(std::fstream& fp, - const e_config_protocol_type& config_protocol_type, - const bool& fast_configuration, - const bool& bit_value_to_skip, - const ModuleManager& module_manager, - const ModuleId& top_module, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream) { - - /* Branch on the type of configuration protocol */ - switch (config_protocol_type) { - case CONFIG_MEM_STANDALONE: - print_verilog_top_testbench_vanilla_bitstream(fp, - module_manager, top_module, - bitstream_manager, fabric_bitstream); - break; - case CONFIG_MEM_SCAN_CHAIN: - print_verilog_top_testbench_configuration_chain_bitstream(fp, fast_configuration, - bit_value_to_skip, - module_manager, top_module, - bitstream_manager, fabric_bitstream); - break; - case CONFIG_MEM_MEMORY_BANK: - print_verilog_top_testbench_memory_bank_bitstream(fp, fast_configuration, - bit_value_to_skip, - module_manager, top_module, - fabric_bitstream); - break; - case CONFIG_MEM_FRAME_BASED: - print_verilog_top_testbench_frame_decoder_bitstream(fp, fast_configuration, - bit_value_to_skip, - module_manager, top_module, - fabric_bitstream); - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid SRAM organization type!\n"); - exit(1); - } -} - /******************************************************************** * Print stimulus for a FPGA fabric with a flatten memory (standalone) configuration protocol * We will load the bitstream in the second clock cycle, right after the first reset cycle @@ -2534,263 +1840,6 @@ void print_verilog_top_testbench_check(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * TODO: This top function will be deprecated!!! The only top function is - * print_verilog_full_testbench() - * The top-level function to generate a testbench, in order to verify: - * 1. Configuration phase of the FPGA fabric, where the bitstream is - * loaded to the configuration protocol of the FPGA fabric - * 2. Operating phase of the FPGA fabric, where input stimuli are - * fed to the I/Os of the FPGA fabric - * +----------+ - * | FPGA | +------------+ - * +----->| Fabric |------>| | - * | | | | | - * | +----------+ | | - * | | Output | - * random_input_vectors -----+ | Vector |---->Functional correct? - * | | Comparator | - * | +-----------+ | | - * | | Input | | | - * +----->| Benchmark |----->| | - * +-----------+ +------------+ - * - *******************************************************************/ -void print_verilog_top_testbench(const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream, - const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, - const FabricGlobalPortInfo& global_ports, - const AtomContext& atom_ctx, - const PlacementContext& place_ctx, - const PinConstraints& pin_constraints, - const IoLocationMap& io_location_map, - const VprNetlistAnnotation& netlist_annotation, - const std::string& circuit_name, - const std::string& verilog_fname, - const SimulationSetting& simulation_parameters, - const VerilogTestbenchOption& options) { - - bool fast_configuration = options.fast_configuration(); - bool explicit_port_mapping = options.explicit_port_mapping(); - - std::string timer_message = std::string("Write autocheck testbench for FPGA top-level Verilog netlist for '") + circuit_name + std::string("'"); - - /* Start time count */ - vtr::ScopedStartFinishTimer timer(timer_message); - - /* Create the file stream */ - std::fstream fp; - fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); - - /* Validate the file stream */ - check_file_stream(verilog_fname.c_str(), fp); - - /* Generate a brief description on the Verilog file*/ - std::string title = std::string("FPGA Verilog Testbench for Top-level netlist of Design: ") + circuit_name; - print_verilog_file_header(fp, title); - - /* Find the top_module */ - ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name()); - VTR_ASSERT(true == module_manager.valid_module_id(top_module)); - - /* Preparation: find all the clock ports */ - std::vector clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); - - /* Preparation: find all the reset/set ports for programming usage */ - std::vector global_prog_reset_ports = find_fabric_global_programming_reset_ports(global_ports); - std::vector global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports); - - /* Identify if we can apply fast configuration */ - bool apply_fast_configuration = fast_configuration && is_fast_configuration_applicable(global_ports); - bool bit_value_to_skip = false; - if (true == apply_fast_configuration) { - bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(), - global_ports, - bitstream_manager, - fabric_bitstream); - } - - /* Start of testbench */ - print_verilog_top_testbench_ports(fp, module_manager, top_module, - atom_ctx, netlist_annotation, - clock_port_names, - pin_constraints, - simulation_parameters, config_protocol, - circuit_name); - - /* Find the clock period */ - float prog_clock_period = (1./simulation_parameters.programming_clock_frequency()); - float default_op_clock_period = (1./simulation_parameters.default_operating_clock_frequency()); - float max_op_clock_period = 0.; - for (const SimulationClockId& clock_id : simulation_parameters.clocks()) { - max_op_clock_period = std::max(max_op_clock_period, (float)(1./simulation_parameters.clock_frequency(clock_id))); - } - - /* Estimate the number of configuration clock cycles */ - size_t num_config_clock_cycles = calculate_num_config_clock_cycles(config_protocol.type(), - apply_fast_configuration, - bit_value_to_skip, - bitstream_manager, - fabric_bitstream); - - /* Generate stimuli for general control signals */ - print_verilog_top_testbench_generic_stimulus(fp, - simulation_parameters, - num_config_clock_cycles, - prog_clock_period, - default_op_clock_period, - VERILOG_SIM_TIMESCALE); - - /* Generate stimuli for programming interface */ - print_verilog_top_testbench_configuration_protocol_stimulus(fp, - config_protocol.type(), - module_manager, top_module, - prog_clock_period, - VERILOG_SIM_TIMESCALE); - - /* Identify the stimulus for global reset/set for programming purpose: - * - If only reset port is seen we turn on Reset - * - If only set port is seen we turn on Reset - * - If both reset and set port is defined, - * we pick the one which is consistent with the bit value to be skipped - */ - bool active_global_prog_reset = false; - bool active_global_prog_set = false; - - if (!global_prog_reset_ports.empty()) { - active_global_prog_reset = true; - } - - if (!global_prog_set_ports.empty()) { - active_global_prog_set = true; - } - - /* Ensure that at most only one of the two switches is activated */ - if ( (true == active_global_prog_reset) - && (true == active_global_prog_set) ) { - /* If we will skip logic '0', we will activate programming reset */ - active_global_prog_reset = !bit_value_to_skip; - /* If we will skip logic '1', we will activate programming set */ - active_global_prog_set = bit_value_to_skip; - } - - /* Generate stimuli for global ports or connect them to existed signals */ - print_verilog_top_testbench_global_ports_stimuli(fp, - module_manager, top_module, - pin_constraints, - global_ports, - simulation_parameters, - active_global_prog_reset, - active_global_prog_set); - - /* Instanciate FPGA top-level module */ - print_verilog_testbench_fpga_instance(fp, module_manager, top_module, - std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), - explicit_port_mapping); - - /* Connect I/Os to benchmark I/Os or constant driver */ - print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module, - atom_ctx, place_ctx, io_location_map, - netlist_annotation, - std::string(), - std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), - (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); - - /* Instanciate input benchmark */ - print_verilog_top_testbench_benchmark_instance(fp, - circuit_name, - atom_ctx, - netlist_annotation, - explicit_port_mapping); - - /* Print tasks used for loading bitstreams */ - print_verilog_top_testbench_load_bitstream_task(fp, - config_protocol.type(), - module_manager, top_module); - - /* load bitstream to FPGA fabric in a configuration phase */ - print_verilog_top_testbench_bitstream(fp, config_protocol.type(), - apply_fast_configuration, - bit_value_to_skip, - module_manager, top_module, - bitstream_manager, fabric_bitstream); - - /* Add signal initialization: - * Bypass writing codes to files due to the autogenerated codes are very large. - */ - if (true == options.include_signal_init()) { - print_verilog_testbench_signal_initialization(fp, - std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), - circuit_lib, - module_manager, - top_module); - } - - - /* Add stimuli for reset, set, clock and iopad signals */ - print_verilog_top_testbench_reset_stimuli(fp, - atom_ctx, - netlist_annotation, - module_manager, - global_ports, - pin_constraints, - clock_port_names); - print_verilog_testbench_random_stimuli(fp, atom_ctx, - netlist_annotation, - module_manager, - global_ports, - pin_constraints, - clock_port_names, - std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), - std::vector(1, BasicPort(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1))); - - /* Add output autocheck */ - print_verilog_testbench_check(fp, - std::string(AUTOCHECKED_SIMULATION_FLAG), - std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), - std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), - std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), - std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), - std::string(TOP_TESTBENCH_ERROR_COUNTER), - atom_ctx, - netlist_annotation, - clock_port_names, - std::string(TOP_TB_OP_CLOCK_PORT_NAME)); - - /* Add autocheck for configuration phase */ - print_verilog_top_testbench_check(fp, - std::string(AUTOCHECKED_SIMULATION_FLAG), - std::string(TOP_TB_CONFIG_DONE_PORT_NAME), - std::string(TOP_TESTBENCH_ERROR_COUNTER)); - - /* Find simulation time */ - float simulation_time = find_simulation_time_period(VERILOG_SIM_TIMESCALE, - num_config_clock_cycles, - 1./simulation_parameters.programming_clock_frequency(), - simulation_parameters.num_clock_cycles(), - 1./simulation_parameters.default_operating_clock_frequency()); - - - /* Add Icarus requirement: - * Always ceil the simulation time so that we test a sufficient length of period!!! - */ - print_verilog_timeout_and_vcd(fp, - std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)), - std::string(circuit_name + std::string("_formal.vcd")), - std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), - std::string(TOP_TESTBENCH_ERROR_COUNTER), - std::ceil(simulation_time)); - - - /* Testbench ends*/ - print_verilog_module_end(fp, std::string(circuit_name) + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)); - - /* Close the file stream */ - fp.close(); -} - /******************************************************************** * The top-level function to generate a full testbench, in order to verify: * 1. Configuration phase of the FPGA fabric, where the bitstream is diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.h b/openfpga/src/fpga_verilog/verilog_top_testbench.h index 1704d0dd5..aff108d72 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.h +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.h @@ -26,22 +26,6 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_top_testbench(const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const FabricBitstream& fabric_bitstream, - const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, - const FabricGlobalPortInfo& global_ports, - const AtomContext& atom_ctx, - const PlacementContext& place_ctx, - const PinConstraints& pin_constraints, - const IoLocationMap& io_location_map, - const VprNetlistAnnotation& netlist_annotation, - const std::string& circuit_name, - const std::string& verilog_fname, - const SimulationSetting& simulation_parameters, - const VerilogTestbenchOption& options); - int print_verilog_full_testbench(const ModuleManager& module_manager, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, From d9d57aad427a0a1d53efd9b758e0a295dec0b186 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Jun 2021 11:37:49 -0600 Subject: [PATCH 162/164] [Tool] Added default net type options to verilog testbench generator command --- openfpga/src/base/openfpga_verilog.cpp | 12 ++++++++++++ .../src/base/openfpga_verilog_command.cpp | 12 ++++++++++++ openfpga/src/fpga_verilog/verilog_api.cpp | 4 ++-- .../verilog_formal_random_top_testbench.cpp | 13 +++++++------ .../verilog_formal_random_top_testbench.h | 3 ++- .../verilog_preconfig_top_module.cpp | 6 +++--- .../verilog_preconfig_top_module.h | 3 ++- .../verilog_testbench_options.cpp | 19 +++++++++++++++++++ .../fpga_verilog/verilog_testbench_options.h | 4 ++++ .../fpga_verilog/verilog_top_testbench.cpp | 8 +++++--- 10 files changed, 68 insertions(+), 16 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index e279f1200..081466c06 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -74,6 +74,7 @@ int write_full_testbench(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); CommandOptionId opt_fast_configuration = cmd.option("fast_configuration"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_default_net_type = cmd.option("default_net_type"); CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -89,6 +90,9 @@ int write_full_testbench(const OpenfpgaContext& openfpga_ctx, options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_print_top_testbench(true); options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init)); + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { + options.set_default_net_type(cmd_context.option_value(cmd, opt_default_net_type)); + } /* If pin constraints are enabled by command options, read the file */ PinConstraints pin_constraints; @@ -122,6 +126,7 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_default_net_type = cmd.option("default_net_type"); CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -135,6 +140,9 @@ int write_preconfigured_fabric_wrapper(const OpenfpgaContext& openfpga_ctx, options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator)); options.set_print_formal_verification_top_netlist(true); + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { + options.set_default_net_type(cmd_context.option_value(cmd, opt_default_net_type)); + } /* If pin constraints are enabled by command options, read the file */ PinConstraints pin_constraints; @@ -167,6 +175,7 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path"); CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_default_net_type = cmd.option("default_net_type"); CommandOptionId opt_verbose = cmd.option("verbose"); /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog @@ -180,6 +189,9 @@ int write_preconfigured_testbench(const OpenfpgaContext& openfpga_ctx, options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_print_preconfig_top_testbench(true); + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { + options.set_default_net_type(cmd_context.option_value(cmd, opt_default_net_type)); + } /* If pin constraints are enabled by command options, read the file */ PinConstraints pin_constraints; diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index 174b8a4d7..a890d6971 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -93,6 +93,10 @@ ShellCommandId add_openfpga_write_full_testbench_command(openfpga::Shell& clock_port_names, const AtomContext& atom_ctx, - const VprNetlistAnnotation& netlist_annotation) { + const VprNetlistAnnotation& netlist_annotation, + const e_verilog_default_net_type& default_net_type) { /* Validate the file stream */ valid_file_stream(fp); print_verilog_default_net_type_declaration(fp, - VERILOG_DEFAULT_NET_TYPE_NONE); + default_net_type); /* Print the declaration for the module */ fp << "module " << circuit_name << FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX << ";" << std::endl; @@ -278,7 +279,7 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, const FabricGlobalPortInfo& global_ports, const PinConstraints& pin_constraints, const SimulationSetting& simulation_parameters, - const bool& explicit_port_mapping) { + const VerilogTestbenchOption &options) { std::string timer_message = std::string("Write configuration-skip testbench for FPGA top-level Verilog netlist implemented by '") + circuit_name.c_str() + std::string("'"); /* Start time count */ @@ -299,17 +300,17 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, std::vector clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); /* Start of testbench */ - print_verilog_top_random_testbench_ports(fp, circuit_name, clock_port_names, atom_ctx, netlist_annotation); + print_verilog_top_random_testbench_ports(fp, circuit_name, clock_port_names, atom_ctx, netlist_annotation, options.default_net_type()); /* Call defined top-level module */ print_verilog_random_testbench_fpga_instance(fp, circuit_name, atom_ctx, netlist_annotation, - explicit_port_mapping); + options.explicit_port_mapping()); /* Call defined benchmark */ print_verilog_top_random_testbench_benchmark_instance(fp, circuit_name, atom_ctx, netlist_annotation, - explicit_port_mapping); + options.explicit_port_mapping()); /* Find clock port to be used */ std::vector clock_ports = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME)); diff --git a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.h b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.h index 6dd4f0310..d58bae969 100644 --- a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.h +++ b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.h @@ -10,6 +10,7 @@ #include "module_manager.h" #include "fabric_global_port_info.h" #include "simulation_setting.h" +#include "verilog_testbench_options.h" /******************************************************************** * Function declaration @@ -26,7 +27,7 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, const FabricGlobalPortInfo& global_ports, const PinConstraints& pin_constraints, const SimulationSetting& simulation_parameters, - const bool& explicit_port_mapping); + const VerilogTestbenchOption &options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp index ece6df67a..5b7264044 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp @@ -442,7 +442,7 @@ int print_verilog_preconfig_top_module(const ModuleManager &module_manager, const VprNetlistAnnotation &netlist_annotation, const std::string &circuit_name, const std::string &verilog_fname, - const bool &explicit_port_mapping) { + const VerilogTestbenchOption& options) { std::string timer_message = std::string("Write pre-configured FPGA top-level Verilog netlist for design '") + circuit_name + std::string("'"); int status = CMD_EXEC_SUCCESS; @@ -462,7 +462,7 @@ int print_verilog_preconfig_top_module(const ModuleManager &module_manager, print_verilog_file_header(fp, title); print_verilog_default_net_type_declaration(fp, - VERILOG_DEFAULT_NET_TYPE_NONE); + options.default_net_type()); /* Print module declaration and ports */ print_verilog_preconfig_top_module_ports(fp, circuit_name, atom_ctx, netlist_annotation); @@ -477,7 +477,7 @@ int print_verilog_preconfig_top_module(const ModuleManager &module_manager, /* Instanciate FPGA top-level module */ print_verilog_testbench_fpga_instance(fp, module_manager, top_module, std::string(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME), - explicit_port_mapping); + options.explicit_port_mapping()); /* Find clock ports in benchmark */ std::vector benchmark_clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h index ff82dea94..01bb12f08 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h @@ -15,6 +15,7 @@ #include "fabric_global_port_info.h" #include "config_protocol.h" #include "vpr_netlist_annotation.h" +#include "verilog_testbench_options.h" /******************************************************************** * Function declaration @@ -35,7 +36,7 @@ int print_verilog_preconfig_top_module(const ModuleManager& module_manager, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, - const bool& explicit_port_mapping); + const VerilogTestbenchOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_testbench_options.cpp b/openfpga/src/fpga_verilog/verilog_testbench_options.cpp index b39adef43..cce9338cf 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_options.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_options.cpp @@ -23,6 +23,7 @@ VerilogTestbenchOption::VerilogTestbenchOption() { explicit_port_mapping_ = false; support_icarus_simulator_ = false; include_signal_init_ = false; + default_net_type_ = VERILOG_DEFAULT_NET_TYPE_NONE; verbose_output_ = false; } @@ -77,6 +78,10 @@ bool VerilogTestbenchOption::support_icarus_simulator() const { return support_icarus_simulator_; } +e_verilog_default_net_type VerilogTestbenchOption::default_net_type() const { + return default_net_type_; +} + bool VerilogTestbenchOption::verbose_output() const { return verbose_output_; } @@ -141,6 +146,20 @@ void VerilogTestbenchOption::set_support_icarus_simulator(const bool& enabled) { support_icarus_simulator_ = enabled; } +void VerilogTestbenchOption::set_default_net_type(const std::string& default_net_type) { + /* Decode from net type string */; + if (default_net_type == std::string(VERILOG_DEFAULT_NET_TYPE_STRING[VERILOG_DEFAULT_NET_TYPE_NONE])) { + default_net_type_ = VERILOG_DEFAULT_NET_TYPE_NONE; + } else if (default_net_type == std::string(VERILOG_DEFAULT_NET_TYPE_STRING[VERILOG_DEFAULT_NET_TYPE_WIRE])) { + default_net_type_ = VERILOG_DEFAULT_NET_TYPE_WIRE; + } else { + VTR_LOG_WARN("Invalid default net type: '%s'! Expect ['%s'|'%s']\n", + default_net_type.c_str(), + VERILOG_DEFAULT_NET_TYPE_STRING[VERILOG_DEFAULT_NET_TYPE_NONE], + VERILOG_DEFAULT_NET_TYPE_STRING[VERILOG_DEFAULT_NET_TYPE_WIRE]); + } +} + void VerilogTestbenchOption::set_verbose_output(const bool& enabled) { verbose_output_ = enabled; } diff --git a/openfpga/src/fpga_verilog/verilog_testbench_options.h b/openfpga/src/fpga_verilog/verilog_testbench_options.h index 492740f8e..6b46d8316 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_options.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_options.h @@ -5,6 +5,7 @@ * Include header files required by the data structure definition *******************************************************************/ #include +#include "verilog_port_types.h" /* Begin namespace openfpga */ namespace openfpga { @@ -34,6 +35,7 @@ class VerilogTestbenchOption { bool explicit_port_mapping() const; bool include_signal_init() const; bool support_icarus_simulator() const; + e_verilog_default_net_type default_net_type() const; bool verbose_output() const; public: /* Public validator */ bool validate() const; @@ -58,6 +60,7 @@ class VerilogTestbenchOption { void set_explicit_port_mapping(const bool& enabled); void set_include_signal_init(const bool& enabled); void set_support_icarus_simulator(const bool& enabled); + void set_default_net_type(const std::string& default_net_type); void set_verbose_output(const bool& enabled); private: /* Internal Data */ std::string output_directory_; @@ -72,6 +75,7 @@ class VerilogTestbenchOption { bool explicit_port_mapping_; bool support_icarus_simulator_; bool include_signal_init_; + e_verilog_default_net_type default_net_type_; bool verbose_output_; }; diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 779b6ceaa..aecf72d6f 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -701,12 +701,13 @@ void print_verilog_top_testbench_ports(std::fstream& fp, const PinConstraints& pin_constraints, const SimulationSetting& simulation_parameters, const ConfigProtocol& config_protocol, - const std::string& circuit_name){ + const std::string& circuit_name, + const e_verilog_default_net_type& default_net_type) { /* Validate the file stream */ valid_file_stream(fp); print_verilog_default_net_type_declaration(fp, - VERILOG_DEFAULT_NET_TYPE_NONE); + default_net_type); /* Print module definition */ fp << "module " << circuit_name << std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX); @@ -1923,7 +1924,8 @@ int print_verilog_full_testbench(const ModuleManager& module_manager, clock_port_names, pin_constraints, simulation_parameters, config_protocol, - circuit_name); + circuit_name, + options.default_net_type()); /* Find the clock period */ float prog_clock_period = (1./simulation_parameters.programming_clock_frequency()); From d40cf98c4833ca170b53f597f82c754121ea61c9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Jun 2021 11:47:28 -0600 Subject: [PATCH 163/164] [Test] Update test cases by using default net type in testbench generator --- .../behavioral_verilog_example_script.openfpga | 4 ++-- .../implicit_verilog_example_script.openfpga | 6 +++--- .../verilog_default_net_type_example_script.openfpga | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga index d27d4eff6..cf614678e 100644 --- a/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/behavioral_verilog_example_script.openfpga @@ -55,8 +55,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --default_net_type ${OPENFPGA_VERILOG_DEFAULT_NET_TYPE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator --default_net_type ${OPENFPGA_VERILOG_DEFAULT_NET_TYPE} # Finish and exit OpenFPGA exit diff --git a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga index c0752db76..9a32a86eb 100644 --- a/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/implicit_verilog_example_script.openfpga @@ -55,9 +55,9 @@ write_fabric_verilog --file ./SRC --include_timing --print_user_defined_template # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit -write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --bitstream fabric_bitstream.bit --default_net_type ${OPENFPGA_DEFAULT_NET_TYPE} +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --default_net_type ${OPENFPGA_DEFAULT_NET_TYPE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --support_icarus_simulator --default_net_type ${OPENFPGA_DEFAULT_NET_TYPE} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga index 77f01d371..fab25be5a 100644 --- a/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/verilog_default_net_type_example_script.openfpga @@ -55,9 +55,9 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA # - Enable pre-configured top-level testbench which is a fast verification skipping programming phase # - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts -write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit -write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator +write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit --default_net_type ${OPENFPGA_VERILOG_DEFAULT_NET_TYPE} +write_preconfigured_fabric_wrapper --file ./SRC --support_icarus_simulator --explicit_port_mapping --default_net_type ${OPENFPGA_VERILOG_DEFAULT_NET_TYPE} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --support_icarus_simulator --default_net_type ${OPENFPGA_VERILOG_DEFAULT_NET_TYPE} # Write the SDC files for PnR backend # - Turn on every options here From 9585e1d3b5f94e78a3802c8ed64675180fcd647f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Jun 2021 14:00:34 -0600 Subject: [PATCH 164/164] [Doc] Update documentation about 'default_net_type' option in testbench generators --- .../openfpga_commands/fpga_verilog_commands.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index 5c57d104e..2a9af3b96 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -76,6 +76,10 @@ write_full_testbench Use explicit port mapping when writing the Verilog netlists + .. option:: --default_net_type + + Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``. + .. option:: --include_signal_init Output signal initialization to Verilog testbench to smooth convergence in HDL simulation @@ -106,6 +110,10 @@ write_preconfigured_fabric_wrapper Use explicit port mapping when writing the Verilog netlists + .. option:: --default_net_type + + Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``. + .. option:: --support_icarus_simulator Output Verilog netlists with syntax that iVerilog simulator can accept @@ -140,6 +148,11 @@ write_preconfigured_testbench Use explicit port mapping when writing the Verilog netlists + .. option:: --default_net_type + + Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``. + + .. option:: --support_icarus_simulator Output Verilog netlists with syntax that iVerilog simulator can accept