diff --git a/libs/libarchopenfpga/src/bitstream_setting.cpp b/libs/libarchopenfpga/src/bitstream_setting.cpp index c04eeba87..7186864b2 100644 --- a/libs/libarchopenfpga/src/bitstream_setting.cpp +++ b/libs/libarchopenfpga/src/bitstream_setting.cpp @@ -102,6 +102,10 @@ std::string BitstreamSetting::default_path( return interconnect_default_paths_[interconnect_setting_id]; } +std::vector BitstreamSetting::none_fabric() const { + return none_fabric_; +} + /************************************************************************ * Public Mutators ***********************************************************************/ @@ -154,6 +158,22 @@ BitstreamSetting::add_bitstream_interconnect_setting( return interc_setting_id; } +void BitstreamSetting::add_none_fabric(const std::string& name, + const std::string& file) { + VTR_ASSERT(name.size()); + VTR_ASSERT(file.size()); + none_fabric_.push_back(NoneFabricBitstreamSetting(name, file)); +} + +void BitstreamSetting::add_none_fabric_pb(const std::string& pb, + const std::string& type, + const std::string& content) { + VTR_ASSERT(none_fabric_.size()); + VTR_ASSERT(type == "param" || type == "attr"); + VTR_ASSERT(content.size()); + none_fabric_.back().add_pb(pb, type, content); +} + /************************************************************************ * Public Validators ***********************************************************************/ diff --git a/libs/libarchopenfpga/src/bitstream_setting.h b/libs/libarchopenfpga/src/bitstream_setting.h index 7963942a0..1bd6ec525 100644 --- a/libs/libarchopenfpga/src/bitstream_setting.h +++ b/libs/libarchopenfpga/src/bitstream_setting.h @@ -6,6 +6,7 @@ * which are used by OpenFPGA *******************************************************************/ #include +#include #include "bitstream_setting_fwd.h" #include "vtr_vector.h" @@ -13,6 +14,29 @@ /* namespace openfpga begins */ namespace openfpga { +struct NoneFabricBitstreamPBSetting { + NoneFabricBitstreamPBSetting(const std::string& p = "", + const std::string& t = "", + const std::string& c = "") + : pb(p), type(t), content(c) {} + const std::string pb = ""; + const std::string type = ""; + const std::string content = ""; +}; + +struct NoneFabricBitstreamSetting { + NoneFabricBitstreamSetting(const std::string& n = "", + const std::string& f = "") + : name(n), file(f) {} + void add_pb(const std::string& p, const std::string& t, + const std::string& c) { + pbs.push_back(NoneFabricBitstreamPBSetting(p, t, c)); + } + const std::string name = ""; + const std::string file = ""; + std::vector pbs; +}; + /******************************************************************** * A data structure to describe bitstream settings * @@ -73,6 +97,7 @@ class BitstreamSetting { const BitstreamInterconnectSettingId& interconnect_setting_id) const; std::string default_path( const BitstreamInterconnectSettingId& interconnect_setting_id) const; + std::vector none_fabric() const; public: /* Public Mutators */ BitstreamPbTypeSettingId add_bitstream_pb_type_setting( @@ -92,6 +117,10 @@ class BitstreamSetting { const std::vector& parent_mode_names, const std::string& default_path); + void add_none_fabric(const std::string& name, const std::string& file); + void add_none_fabric_pb(const std::string& pb, const std::string& type, + const std::string& content); + public: /* Public Validators */ bool valid_bitstream_pb_type_setting_id( const BitstreamPbTypeSettingId& pb_type_setting_id) const; @@ -133,6 +162,7 @@ class BitstreamSetting { interconnect_parent_mode_names_; vtr::vector interconnect_default_paths_; + std::vector none_fabric_; }; } // namespace openfpga diff --git a/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp b/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp index 7447bec2a..876ae0562 100644 --- a/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp +++ b/libs/libarchopenfpga/src/read_xml_bitstream_setting.cpp @@ -76,6 +76,33 @@ static void read_xml_bitstream_interconnect_setting( operating_pb_parser.modes(), default_path_attr); } +/******************************************************************** + * Parse XML description for a none_fabric annotation under a XML node + *******************************************************************/ +static void read_xml_none_fabric_bitstream_setting( + pugi::xml_node& xml_none_fabric, const pugiutil::loc_data& loc_data, + openfpga::BitstreamSetting& bitstream_setting) { + const std::string& name_attr = + get_attribute(xml_none_fabric, "name", loc_data).as_string(); + const std::string& file_attr = + get_attribute(xml_none_fabric, "file", loc_data).as_string(); + /* Add to none fabric */ + bitstream_setting.add_none_fabric(name_attr, file_attr); + for (pugi::xml_node xml_child : xml_none_fabric.children()) { + if (xml_child.name() != std::string("pb")) { + bad_tag(xml_child, loc_data, xml_none_fabric, {"pb"}); + } + const std::string& pb_name_attr = + get_attribute(xml_child, "name", loc_data).as_string(); + const std::string& type_attr = + get_attribute(xml_child, "type", loc_data).as_string(); + const std::string& content_attr = + get_attribute(xml_child, "content", loc_data).as_string(); + /* Add PB to none fabric */ + bitstream_setting.add_none_fabric_pb(pb_name_attr, type_attr, content_attr); + } +} + /******************************************************************** * Parse XML codes about to an object *******************************************************************/ @@ -89,17 +116,22 @@ openfpga::BitstreamSetting read_xml_bitstream_setting( for (pugi::xml_node xml_child : Node.children()) { /* Error out if the XML child has an invalid name! */ if ((xml_child.name() != std::string("pb_type")) && - (xml_child.name() != std::string("interconnect"))) { - bad_tag(xml_child, loc_data, Node, {"pb_type | interconnect"}); + (xml_child.name() != std::string("interconnect")) && + (xml_child.name() != std::string("none_fabric"))) { + bad_tag(xml_child, loc_data, Node, + {"pb_type | interconnect | none_fabric"}); } 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")); + } else if (xml_child.name() == std::string("interconnect")) { read_xml_bitstream_interconnect_setting(xml_child, loc_data, bitstream_setting); + } else { + VTR_ASSERT_SAFE(xml_child.name() == std::string("none_fabric")); + read_xml_none_fabric_bitstream_setting(xml_child, loc_data, + bitstream_setting); } } diff --git a/openfpga/src/base/openfpga_bitstream_template.h b/openfpga/src/base/openfpga_bitstream_template.h index 8821c8392..c3d0ea3d6 100644 --- a/openfpga/src/base/openfpga_bitstream_template.h +++ b/openfpga/src/base/openfpga_bitstream_template.h @@ -11,6 +11,7 @@ #include "command.h" #include "command_context.h" #include "command_exit_codes.h" +#include "extract_device_none_fabric_bitstream.h" #include "globals.h" #include "openfpga_digest.h" #include "openfpga_naming.h" @@ -59,6 +60,9 @@ int fpga_bitstream_template(T& openfpga_ctx, const Command& cmd, !cmd_context.option_enable(cmd, opt_no_time_stamp)); } + extract_device_none_fabric_bitstream( + g_vpr_ctx, openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose)); + /* TODO: should identify the error code from internal function execution */ return CMD_EXEC_SUCCESS; } diff --git a/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.cpp new file mode 100644 index 000000000..385f58b33 --- /dev/null +++ b/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.cpp @@ -0,0 +1,255 @@ +/******************************************************************** + * This file includes functions to build bitstream from a mapped + * FPGA fabric. + * We decode the bitstream from configuration of routing multiplexers + * and Look-Up Tables (LUTs) which locate in CLBs and global routing + *architecture + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "extract_device_none_fabric_bitstream.h" +#include "openfpga_pb_parser.h" +#include "pb_type_utils.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Extract data from the targetted PB + * 1. If it is primitive + * a. If it match the targetted PB, try to get data from + * param of attr depends on what being defined in XML + * b. If it is does not match, do nothing + * 2. If it is not primitive, then we loop for the child + *******************************************************************/ +static bool extract_pb_data(std::fstream& fp, const AtomContext& atom_ctx, + const t_pb* op_pb, const t_pb_type* target_pb_type, + const NoneFabricBitstreamPBSetting& setting) { + t_pb_graph_node* pb_graph_node = op_pb->pb_graph_node; + t_pb_type* pb_type = pb_graph_node->pb_type; + bool found_pb = false; + if (true == is_primitive_pb_type(pb_type)) { + if (target_pb_type == pb_type) { + AtomBlockId atom_blk = atom_ctx.nlist.find_block(op_pb->name); + VTR_ASSERT(atom_blk); + if (setting.type == "param") { + for (const auto& param_search : atom_ctx.nlist.block_params(atom_blk)) { + std::string param = param_search.first; + std::string content = param_search.second; + if (setting.content == param) { + fp << ",\n \"data\" : \"" << content.c_str() << "\""; + break; + } + } + } else { + VTR_ASSERT(setting.type == "attr"); + for (const auto& attr_search : atom_ctx.nlist.block_attrs(atom_blk)) { + std::string attr = attr_search.first; + std::string content = attr_search.second; + if (setting.content == attr) { + fp << ",\n \"data\" : \"" << content.c_str() << "\""; + break; + } + } + } + found_pb = true; + } + } else { + t_mode* mapped_mode = &(pb_graph_node->pb_type->modes[op_pb->mode]); + for (int ipb = 0; ipb < mapped_mode->num_pb_type_children && !found_pb; + ++ipb) { + /* Each child may exist multiple times in the hierarchy*/ + for (int jpb = 0; + jpb < mapped_mode->pb_type_children[ipb].num_pb && !found_pb; + ++jpb) { + if ((nullptr != op_pb->child_pbs[ipb]) && + (nullptr != op_pb->child_pbs[ipb][jpb].name)) { + found_pb = + extract_pb_data(fp, atom_ctx, &(op_pb->child_pbs[ipb][jpb]), + target_pb_type, setting); + } + } + } + } + return found_pb; +} + +/******************************************************************** + * Extract data from the targetted PB (from that particular grid) + *******************************************************************/ +static void extract_grid_none_fabric_bitstream( + std::fstream& fp, const VprContext& vpr_ctx, + const OpenfpgaContext& openfpga_ctx, const ClusterBlockId& cluster_block_id, + const t_pb_type* target_pb_type, const NoneFabricBitstreamPBSetting setting) { + const ClusteringContext& clustering_ctx = vpr_ctx.clustering(); + const AtomContext& atom_ctx = vpr_ctx.atom(); + + if (ClusterBlockId::INVALID() != cluster_block_id) { + const t_pb* op_pb = clustering_ctx.clb_nlist.block_pb(cluster_block_id); + extract_pb_data(fp, atom_ctx, op_pb, target_pb_type, setting); + } else { + // Grid is valid, but this resource is not being used + } +} + +/******************************************************************** + * Extract data from the targetted PB (from the device) + *******************************************************************/ +static void extract_device_none_fabric_pb_bitstream( + std::fstream& fp, const NoneFabricBitstreamPBSetting setting, + const std::string& target_parent_pb_name, const t_pb_type* target_pb_type, + const VprContext& vpr_ctx, const OpenfpgaContext& openfpga_ctx) { + const DeviceContext& device_ctx = vpr_ctx.device(); + const PlacementContext& placement_ctx = vpr_ctx.placement(); + const DeviceGrid& grids = device_ctx.grid; + const size_t& layer = 0; + + // Loop logic block one by one + fp << ",\n \"grid\" : ["; + size_t grid_count = 0; + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + t_physical_tile_loc phy_tile_loc(ix, iy, layer); + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(phy_tile_loc); + // Bypass EMPTY grid + if (true == is_empty_type(grid_type)) { + continue; + } + + // Skip width > 1 or height > 1 tiles (mostly heterogeneous blocks) + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { + continue; + } + + // Skip if this grid is not what we are looking for + if (target_parent_pb_name != std::string(grid_type->name)) { + continue; + } + + // Get the mapped blocks to this grid + for (int isubtile = 0; isubtile < grid_type->capacity; ++isubtile) { + ClusterBlockId cluster_blk_id = + placement_ctx.grid_blocks.block_at_location( + {(int)ix, (int)iy, (int)isubtile, (int)layer}); + if (grid_count) { + fp << ","; + } + fp << "\n"; + fp << " {\n"; + fp << " \"x\" : " << (uint32_t)(ix) << ",\n"; + fp << " \"y\" : " << (uint32_t)(iy); + extract_grid_none_fabric_bitstream( + fp, vpr_ctx, openfpga_ctx, cluster_blk_id, target_pb_type, setting); + fp << "\n }"; + grid_count++; + } + } + } + fp << "\n ]"; +} + +/******************************************************************** + * Search the PB type based on the given name defined in XML + *******************************************************************/ +static t_pb_type* find_pb_type(const DeviceContext& device_ctx, + const std::string& parent_pb, + const std::string& pb) { + t_pb_type* pb_type = nullptr; + openfpga::PbParser pb_parser(pb); + std::vector names = pb_parser.parents(); + names.push_back(pb_parser.leaf()); + for (const t_logical_block_type& lb_type : device_ctx.logical_block_types) { + /* Bypass 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 (parent_pb != 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 */ + pb_type = try_find_pb_type_with_given_path(lb_type.pb_type, names, + pb_parser.modes()); + if (nullptr == pb_type) { + continue; + } + break; + } + return pb_type; +} + +/******************************************************************** + * A top-level function to extract data based on none-fabric bitstream setting + *******************************************************************/ +void extract_device_none_fabric_bitstream(const VprContext& vpr_ctx, + const OpenfpgaContext& openfpga_ctx, + const bool& verbose) { + std::string timer_message = + std::string("\nBuild none-fabric bitstream for implementation '") + + vpr_ctx.atom().nlist.netlist_name() + std::string("'\n"); + vtr::ScopedStartFinishTimer timer(timer_message); + const openfpga::BitstreamSetting& bitstream_setting = + openfpga_ctx.bitstream_setting(); + std::vector none_fabric_setting = + bitstream_setting.none_fabric(); + + // Only proceed if it is defined in bitstream_setting.xml + if (none_fabric_setting.size()) { + // Go through each none_fabric settting + for (auto setting : none_fabric_setting) { + std::fstream fp; + fp.open(setting.file.c_str(), std::fstream::out); + fp << "{\n"; + fp << " \"" << setting.name.c_str() << "\" : [\n"; + int pb_count = 0; + // Extract each needed PB data + for (auto pb_setting : setting.pbs) { + std::string pb_type = setting.name + pb_setting.pb; + t_pb_type* target_pb_type = + find_pb_type(vpr_ctx.device(), setting.name, pb_type); + if (pb_count) { + fp << ",\n"; + } + fp << " {\n"; + fp << " \"pb\" : \"" << pb_type.c_str() << "\",\n"; + if (target_pb_type == nullptr) { + fp << " \"is_primitive_pb_type\" : \"invalid\"\n"; + } else { + if (is_primitive_pb_type(target_pb_type)) { + fp << " \"is_primitive_pb_type\" : \"true\",\n"; + } else { + fp << " \"is_primitive_pb_type\" : \"false\",\n"; + } + } + fp << " \"type\" : \"" << pb_setting.type.c_str() << "\",\n"; + fp << " \"content\" : \"" << pb_setting.content.c_str() << "\""; + if (target_pb_type != nullptr && is_primitive_pb_type(target_pb_type)) { + extract_device_none_fabric_pb_bitstream(fp, pb_setting, setting.name, + target_pb_type, vpr_ctx, + openfpga_ctx); + } + fp << "\n }"; + pb_count++; + } + if (pb_count) { + fp << "\n"; + } + fp << " ]\n"; + fp << "}\n"; + } + } + VTR_LOGV(verbose, "Done\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.h b/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.h new file mode 100644 index 000000000..2a83bc63f --- /dev/null +++ b/openfpga/src/fpga_bitstream/extract_device_none_fabric_bitstream.h @@ -0,0 +1,25 @@ +#ifndef EXTRACT_DEVICE_NONE_FABRIC_BITSTREAM_H +#define EXTRACT_DEVICE_NONE_FABRIC_BITSTREAM_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "openfpga_context.h" +#include "vpr_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void extract_device_none_fabric_bitstream(const VprContext& vpr_ctx, + const OpenfpgaContext& openfpga_ctx, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif