| #include "fasm.h" |
| |
| #include <algorithm> |
| #include <fstream> |
| #include <iomanip> |
| #include <iostream> |
| #include <iterator> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <unordered_map> |
| |
| #include "globals.h" |
| |
| #include "rr_metadata.h" |
| |
| #include "vtr_assert.h" |
| #include "vtr_logic.h" |
| #include "vtr_version.h" |
| #include "vpr_error.h" |
| |
| #include "atom_netlist_utils.h" |
| #include "netlist_writer.h" |
| #include "vpr_utils.h" |
| #include "route_tree_timing.h" |
| |
| #include "fasm_utils.h" |
| |
| namespace fasm { |
| |
| FasmWriterVisitor::FasmWriterVisitor(std::ostream& f) : os_(f) {} |
| |
| void FasmWriterVisitor::visit_top_impl(const char* top_level_name) { |
| (void)top_level_name; |
| auto& device_ctx = g_vpr_ctx.device(); |
| pb_graph_pin_lookup_from_index_by_type_.resize(device_ctx.logical_block_types.size()); |
| for(unsigned int itype = 0; itype < device_ctx.logical_block_types.size(); itype++) { |
| pb_graph_pin_lookup_from_index_by_type_.at(itype) = alloc_and_load_pb_graph_pin_lookup_from_index(&device_ctx.logical_block_types[itype]); |
| } |
| } |
| |
| void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { |
| auto& place_ctx = g_vpr_ctx.placement(); |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| current_blk_id_ = blk_id; |
| |
| VTR_ASSERT(clb->pb_graph_node != nullptr); |
| VTR_ASSERT(clb->pb_graph_node->pb_type); |
| |
| root_clb_ = clb->pb_graph_node; |
| |
| int x = place_ctx.block_locs[blk_id].loc.x; |
| int y = place_ctx.block_locs[blk_id].loc.y; |
| int z = place_ctx.block_locs[blk_id].loc.z; |
| auto &grid_loc = device_ctx.grid[x][y]; |
| blk_type_ = grid_loc.type; |
| |
| blk_prefix_ = ""; |
| clb_prefix_ = ""; |
| clb_prefix_map_.clear(); |
| |
| // Get placeholder list (if provided) |
| tags_.clear(); |
| if(grid_loc.meta != nullptr && grid_loc.meta->has("fasm_placeholders")) { |
| auto* value = grid_loc.meta->get("fasm_placeholders"); |
| VTR_ASSERT(value != nullptr); |
| |
| // Parse placeholder definition |
| std::vector<std::string> tag_defs = vtr::split(value->front().as_string(), "\n"); |
| for (auto& tag_def: tag_defs) { |
| auto parts = split_fasm_entry(tag_def, "=:", "\t "); |
| if (parts.size() == 0) { |
| continue; |
| } |
| |
| VTR_ASSERT(parts.size() == 2); |
| |
| VTR_ASSERT(tags_.count(parts.at(0)) == 0); |
| tags_[parts.at(0)] = parts.at(1); |
| } |
| } |
| |
| std::string grid_prefix; |
| if(grid_loc.meta != nullptr && grid_loc.meta->has("fasm_prefix")) { |
| auto* value = grid_loc.meta->get("fasm_prefix"); |
| VTR_ASSERT(value != nullptr); |
| std::string prefix_unsplit = value->front().as_string(); |
| std::vector<std::string> fasm_prefixes = vtr::split(prefix_unsplit, " \t\n"); |
| if(fasm_prefixes.size() != static_cast<size_t>(blk_type_->capacity)) { |
| vpr_throw(VPR_ERROR_OTHER, |
| __FILE__, __LINE__, |
| "number of fasm_prefix (%s) options (%d) for block (%s) must match capacity(%d)", |
| prefix_unsplit.c_str(), fasm_prefixes.size(), blk_type_->name, blk_type_->capacity); |
| } |
| grid_prefix = fasm_prefixes[z]; |
| blk_prefix_ = grid_prefix + "."; |
| } |
| else { |
| blk_prefix_ = ""; |
| } |
| } |
| |
| void FasmWriterVisitor::check_interconnect(const t_pb_routes &pb_routes, int inode) { |
| auto iter = pb_routes.find(inode); |
| if(iter == pb_routes.end() || !iter->second.atom_net_id) { |
| // Net is open. |
| return; |
| } |
| |
| /* No previous driver implies that this is either a top-level input pin |
| * or a primitive output pin */ |
| int prev_node = iter->second.driver_pb_pin_id; |
| if(prev_node == OPEN) { |
| return; |
| } |
| |
| t_pb_graph_pin *prev_pin = pb_graph_pin_lookup_from_index_by_type_.at(blk_type_->index)[prev_node]; |
| |
| int prev_edge; |
| for(prev_edge = 0; prev_edge < prev_pin->num_output_edges; prev_edge++) { |
| VTR_ASSERT(prev_pin->output_edges[prev_edge]->num_output_pins == 1); |
| if(prev_pin->output_edges[prev_edge]->output_pins[0]->pin_count_in_cluster == inode) { |
| break; |
| } |
| } |
| VTR_ASSERT(prev_edge < prev_pin->num_output_edges); |
| |
| auto *interconnect = prev_pin->output_edges[prev_edge]->interconnect; |
| if(interconnect->meta.has("fasm_mux")) { |
| auto* value = interconnect->meta.get("fasm_mux"); |
| VTR_ASSERT(value != nullptr); |
| std::string fasm_mux = value->front().as_string(); |
| output_fasm_mux(fasm_mux, interconnect, prev_pin); |
| } |
| } |
| |
| static std::string handle_fasm_prefix(const t_metadata_dict *meta, |
| const t_pb_graph_node *pb_graph_node, const t_pb_type *pb_type) { |
| bool has_prefix = meta != nullptr && meta->has("fasm_prefix"); |
| if(!has_prefix) { |
| return ""; |
| } |
| |
| auto* value = meta->one("fasm_prefix"); |
| VTR_ASSERT(value != nullptr); |
| auto fasm_prefix_unsplit = value->as_string(); |
| auto fasm_prefix = vtr::split(fasm_prefix_unsplit, " \t\n"); |
| VTR_ASSERT(pb_type->num_pb >= 0); |
| if(fasm_prefix.size() != static_cast<size_t>(pb_type->num_pb)) { |
| vpr_throw(VPR_ERROR_OTHER, |
| __FILE__, __LINE__, |
| "number of fasm_prefix (%s) options (%d) for block (%s) must match capacity(%d)", |
| fasm_prefix_unsplit.c_str(), fasm_prefix.size(), pb_type->name, pb_type->num_pb); |
| } |
| |
| if(pb_graph_node->placement_index >= pb_type->num_pb) { |
| vpr_throw(VPR_ERROR_OTHER, |
| __FILE__, __LINE__, |
| "pb_graph_node->placement_index = %d >= pb_type->num_pb = %d", |
| fasm_prefix_unsplit.c_str(), pb_type->num_pb); |
| } |
| |
| return fasm_prefix.at(pb_graph_node->placement_index) + "."; |
| } |
| |
| std::string FasmWriterVisitor::build_clb_prefix(const t_pb *pb, const t_pb_graph_node* pb_graph_node, bool* is_parent_pb_null) const { |
| std::string clb_prefix = ""; |
| |
| const t_pb *pb_for_graph_node = nullptr; |
| |
| // If not t_pb, mode_index is always 0. |
| int mode_index = 0; |
| if(root_clb_ != pb_graph_node && pb_graph_node->parent_pb_graph_node != root_clb_) { |
| VTR_ASSERT(pb_graph_node->parent_pb_graph_node != nullptr); |
| if(pb != nullptr) { |
| while(pb_for_graph_node == nullptr) { |
| pb_for_graph_node = pb->find_pb(pb_graph_node); |
| |
| if(pb_for_graph_node == nullptr) { |
| if(pb->parent_pb == nullptr) { |
| *is_parent_pb_null = true; |
| break; |
| } |
| pb = pb->parent_pb; |
| } |
| } |
| |
| if(pb_for_graph_node != nullptr) { |
| mode_index = pb_for_graph_node->mode; |
| } |
| } |
| |
| clb_prefix = build_clb_prefix(pb, pb_graph_node->parent_pb_graph_node, is_parent_pb_null); |
| } |
| |
| const auto *pb_type = pb_graph_node->pb_type; |
| |
| clb_prefix += handle_fasm_prefix(&pb_type->meta, pb_graph_node, pb_type); |
| |
| if(pb_type->modes != nullptr) { |
| VTR_ASSERT(mode_index < pb_type->num_modes); |
| |
| clb_prefix += handle_fasm_prefix(&pb_type->modes[mode_index].meta, |
| pb_graph_node, pb_type); |
| } else { |
| VTR_ASSERT(mode_index == 0); |
| } |
| |
| return clb_prefix; |
| } |
| |
| static const t_pb_graph_pin* is_node_used(const t_pb_routes &top_pb_route, const t_pb_graph_node* pb_graph_node) { |
| // Is the node used at all? |
| const t_pb_graph_pin* pin = nullptr; |
| for(int port_index = 0; port_index < pb_graph_node->num_output_ports; ++port_index) { |
| for(int pin_index = 0; pin_index < pb_graph_node->num_output_pins[port_index]; ++pin_index) { |
| pin = &pb_graph_node->output_pins[port_index][pin_index]; |
| if (top_pb_route.count(pin->pin_count_in_cluster) > 0 && top_pb_route[pin->pin_count_in_cluster].atom_net_id != AtomNetId::INVALID()) { |
| return pin; |
| } |
| } |
| } |
| for(int port_index = 0; port_index < pb_graph_node->num_input_ports; ++port_index) { |
| for(int pin_index = 0; pin_index < pb_graph_node->num_input_pins[port_index]; ++pin_index) { |
| pin = &pb_graph_node->input_pins[port_index][pin_index]; |
| if (top_pb_route.count(pin->pin_count_in_cluster) > 0 && top_pb_route[pin->pin_count_in_cluster].atom_net_id != AtomNetId::INVALID()) { |
| return pin; |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| void FasmWriterVisitor::check_features(const t_metadata_dict *meta) const { |
| if(meta == nullptr) { |
| return; |
| } |
| |
| if(!meta->has("fasm_features")) { |
| return; |
| } |
| |
| auto* value = meta->one("fasm_features"); |
| VTR_ASSERT(value != nullptr); |
| |
| output_fasm_features(value->as_string()); |
| } |
| |
| void FasmWriterVisitor::visit_all_impl(const t_pb_routes &pb_routes, const t_pb* pb) { |
| VTR_ASSERT(pb != nullptr); |
| VTR_ASSERT(pb->pb_graph_node != nullptr); |
| |
| const t_pb_graph_node *pb_graph_node = pb->pb_graph_node; |
| |
| // Check if this PB is `open` and has to be skipped |
| bool is_parent_pb_null = false; |
| std::string clb_prefix = build_clb_prefix(pb, pb_graph_node, &is_parent_pb_null); |
| clb_prefix_map_.insert(std::make_pair(pb_graph_node, clb_prefix)); |
| clb_prefix_ = clb_prefix; |
| if (is_parent_pb_null == true) { |
| return; |
| } |
| |
| t_pb_type *pb_type = pb_graph_node->pb_type; |
| auto *mode = &pb_type->modes[pb->mode]; |
| |
| check_features(&pb_type->meta); |
| if(mode != nullptr) { |
| check_features(&mode->meta); |
| } |
| |
| if(mode != nullptr && std::string(mode->name) == "wire") { |
| auto io_pin = is_node_used(pb_routes, pb_graph_node); |
| if(io_pin != nullptr) { |
| const auto& route = pb_routes.at(io_pin->pin_count_in_cluster); |
| const int num_inputs = *route.pb_graph_pin->parent_node->num_input_pins; |
| const auto *lut_definition = find_lut(route.pb_graph_pin->parent_node); |
| VTR_ASSERT(lut_definition->num_inputs == num_inputs); |
| |
| output_fasm_features(lut_definition->CreateWire(route.pb_graph_pin->pin_number)); |
| } |
| } |
| |
| int port_index = 0; |
| for (int i = 0; i < pb_type->num_ports; i++) { |
| if (pb_type->ports[i].is_clock || pb_type->ports[i].type != IN_PORT) { |
| continue; |
| } |
| for (int j = 0; j < pb_type->ports[i].num_pins; j++) { |
| int inode = pb->pb_graph_node->input_pins[port_index][j].pin_count_in_cluster; |
| check_interconnect(pb_routes, inode); |
| } |
| port_index += 1; |
| } |
| |
| port_index = 0; |
| for (int i = 0; i < pb_type->num_ports; i++) { |
| if (!pb_type->ports[i].is_clock || pb_type->ports[i].type != IN_PORT) { |
| continue; |
| } |
| for (int j = 0; j < pb_type->ports[i].num_pins; j++) { |
| int inode = pb->pb_graph_node->clock_pins[port_index][j].pin_count_in_cluster; |
| check_interconnect(pb_routes, inode); |
| } |
| port_index += 1; |
| } |
| |
| port_index = 0; |
| for (int i = 0; i < pb_type->num_ports; i++) { |
| if (pb_type->ports[i].type != OUT_PORT) { |
| continue; |
| } |
| for (int j = 0; j < pb_type->ports[i].num_pins; j++) { |
| int inode = pb->pb_graph_node->output_pins[port_index][j].pin_count_in_cluster; |
| check_interconnect(pb_routes, inode); |
| } |
| port_index += 1; |
| } |
| } |
| |
| void FasmWriterVisitor::visit_route_through_impl(const t_pb* atom) { |
| check_for_lut(atom); |
| check_for_param(atom); |
| } |
| |
| static AtomNetId _find_atom_input_logical_net(const t_pb* atom, const t_pb_routes &pb_route, int atom_input_idx) { |
| const t_pb_graph_node* pb_node = atom->pb_graph_node; |
| const int cluster_pin_idx = pb_node->input_pins[0][atom_input_idx].pin_count_in_cluster; |
| if(pb_route.count(cluster_pin_idx) > 0) { |
| return pb_route[cluster_pin_idx].atom_net_id; |
| } else { |
| return AtomNetId::INVALID(); |
| } |
| } |
| |
| static LogicVec lut_outputs(const t_pb* atom_pb, size_t num_inputs, const t_pb_routes &pb_route) { |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| AtomBlockId block_id = atom_ctx.lookup.pb_atom(atom_pb); |
| const auto& truth_table = atom_ctx.nlist.block_truth_table(block_id); |
| auto ports = atom_ctx.nlist.block_input_ports(atom_ctx.lookup.pb_atom(atom_pb)); |
| |
| const t_pb_graph_node* gnode = atom_pb->pb_graph_node; |
| |
| if(ports.size() != 1) { |
| if(ports.size() != 0) { |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, "LUT port unexpected size is %d", ports.size()); |
| } |
| |
| Lut lut(num_inputs); |
| if(truth_table.size() == 1) { |
| VTR_ASSERT(truth_table[0].size() == 1); |
| lut.SetConstant(truth_table[0][0]); |
| } else if(truth_table.size() == 0) { |
| lut.SetConstant(vtr::LogicValue::FALSE); |
| } else { |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, "LUT truth table unexpected size is %d", truth_table.size()); |
| } |
| |
| return lut.table(); |
| } |
| |
| VTR_ASSERT(gnode->num_input_ports == 1); |
| //VTR_ASSERT(gnode->num_input_pins[0] >= num_inputs); |
| std::vector<vtr::LogicValue> inputs(num_inputs, vtr::LogicValue::DONT_CARE); |
| std::vector<int> permutation(num_inputs, -1); |
| |
| AtomPortId port_id = *ports.begin(); |
| |
| for(size_t ipin = 0; ipin < num_inputs; ++ipin) { |
| //The net currently connected to input j |
| const AtomNetId impl_input_net_id = _find_atom_input_logical_net(atom_pb, pb_route, ipin); |
| |
| //Find the original pin index |
| const t_pb_graph_pin* gpin = &gnode->input_pins[0][ipin]; |
| const BitIndex orig_index = atom_pb->atom_pin_bit_index(gpin); |
| |
| if(impl_input_net_id) { |
| //If there is a valid net connected in the implementation |
| AtomNetId logical_net_id = atom_ctx.nlist.port_net(port_id, orig_index); |
| VTR_ASSERT(impl_input_net_id == logical_net_id); |
| |
| //Mark the permutation. |
| // The net originally located at orig_index in the atom netlist |
| // was moved to ipin in the implementation |
| permutation[orig_index] = ipin; |
| } |
| } |
| |
| // truth_table a vector of truth table rows. The last value in each row is |
| // the output value, and the other values are the input values. Open pins |
| // are don't cares, and should be -1 in the permutation table. |
| Lut lut(num_inputs); |
| for(const auto& row : truth_table) { |
| std::fill(std::begin(inputs), std::end(inputs), vtr::LogicValue::DONT_CARE); |
| for(size_t i = 0; i < row.size() - 1; i++) { |
| int permuted_idx = permutation[i]; |
| if(permuted_idx != -1) { |
| inputs[permuted_idx] = row[i]; |
| } |
| } |
| |
| lut.SetOutput(inputs, row[row.size() - 1]); |
| } |
| |
| return lut.table(); |
| } |
| |
| static const t_metadata_dict *get_fasm_type(const t_pb_graph_node* pb_graph_node, std::string target_type) { |
| if(pb_graph_node == nullptr) { |
| return nullptr; |
| } |
| |
| if(pb_graph_node->pb_type == nullptr) { |
| return nullptr; |
| } |
| |
| const t_metadata_dict *meta = nullptr; |
| if(pb_graph_node->pb_type->meta.has("fasm_type")) { |
| meta = &pb_graph_node->pb_type->meta; |
| } |
| |
| if(pb_graph_node->pb_type->parent_mode != nullptr) { |
| VTR_ASSERT(pb_graph_node->pb_type->parent_mode->parent_pb_type != nullptr); |
| const t_pb_type *pb_type = pb_graph_node->pb_type->parent_mode->parent_pb_type; |
| if(pb_graph_node->pb_type->parent_mode->meta.has("fasm_type")) { |
| meta = &pb_graph_node->pb_type->parent_mode->meta; |
| } else if(pb_type->num_modes <= 1) { |
| if(pb_type->meta.has("fasm_type")) { |
| meta = &pb_type->meta; |
| } |
| } |
| } |
| |
| if(meta != nullptr) { |
| auto* value = meta->one("fasm_type"); |
| VTR_ASSERT(value != nullptr); |
| if(value->as_string() == target_type) { |
| return meta; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| const LutOutputDefinition* FasmWriterVisitor::find_lut(const t_pb_graph_node* pb_graph_node) { |
| while(pb_graph_node != nullptr) { |
| VTR_ASSERT(pb_graph_node->pb_type != nullptr); |
| |
| auto iter = lut_definitions_.find(pb_graph_node->pb_type); |
| if(iter == lut_definitions_.end()) { |
| const t_metadata_dict *meta = get_fasm_type(pb_graph_node, "LUT"); |
| if(meta != nullptr) { |
| VTR_ASSERT(meta->has("fasm_lut")); |
| auto* value = meta->one("fasm_lut"); |
| VTR_ASSERT(value != nullptr); |
| |
| std::vector<std::pair<std::string, LutOutputDefinition>> luts; |
| luts.push_back(std::make_pair( |
| vtr::string_fmt("%s[0]", pb_graph_node->pb_type->name), |
| LutOutputDefinition(value->as_string()))); |
| |
| auto insert_result = lut_definitions_.insert( |
| std::make_pair(pb_graph_node->pb_type, luts)); |
| VTR_ASSERT(insert_result.second); |
| iter = insert_result.first; |
| } |
| |
| meta = get_fasm_type(pb_graph_node, "SPLIT_LUT"); |
| if(meta != nullptr) { |
| VTR_ASSERT(meta->has("fasm_lut")); |
| auto* value = meta->one("fasm_lut"); |
| VTR_ASSERT(value != nullptr); |
| std::string fasm_lut = value->as_string(); |
| auto lut_parts = split_fasm_entry(fasm_lut, "\n", "\t "); |
| if(__builtin_popcount(lut_parts.size()) != 1) { |
| vpr_throw(VPR_ERROR_OTHER, |
| __FILE__, __LINE__, |
| "Number of lut splits must be power of two, found %d parts", |
| lut_parts.size()); |
| } |
| |
| std::vector<std::pair<std::string, LutOutputDefinition>> luts; |
| luts.reserve(lut_parts.size()); |
| for(const auto &part : lut_parts) { |
| auto parts = vtr::split(part, "="); |
| if(parts.size() != 2) { |
| vpr_throw(VPR_ERROR_OTHER, |
| __FILE__, __LINE__, |
| "Split lut definition fasm_lut = %s does not parse.", |
| fasm_lut.c_str()); |
| } |
| |
| luts.push_back(std::make_pair( |
| parts[1], LutOutputDefinition(parts[0]))); |
| } |
| |
| auto insert_result = lut_definitions_.insert( |
| std::make_pair(pb_graph_node->pb_type, |
| luts)); |
| VTR_ASSERT(insert_result.second); |
| iter = insert_result.first; |
| } |
| } |
| |
| if(iter != lut_definitions_.end()) { |
| auto string_at_node = vtr::string_fmt("%s[%d]", pb_graph_node->pb_type->name, pb_graph_node->placement_index); |
| for(const auto &lut : iter->second) { |
| if(lut.first == string_at_node) { |
| return &lut.second; |
| } |
| } |
| } |
| |
| pb_graph_node = pb_graph_node->parent_pb_graph_node; |
| } |
| |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, |
| "Failed to find LUT output definition."); |
| return nullptr; |
| } |
| |
| static const t_pb_routes &find_pb_route(const t_pb* pb) { |
| const t_pb* parent = pb->parent_pb; |
| while(parent != nullptr) { |
| pb = parent; |
| parent = pb->parent_pb; |
| } |
| return pb->pb_route; |
| } |
| |
| void FasmWriterVisitor::check_for_param(const t_pb *atom) { |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| auto atom_blk_id = atom_ctx.lookup.pb_atom(atom); |
| if (atom_blk_id == AtomBlockId::INVALID()) { |
| return; |
| } |
| |
| if(atom->pb_graph_node == nullptr || |
| atom->pb_graph_node->pb_type == nullptr) { |
| return; |
| } |
| |
| const auto *meta = &atom->pb_graph_node->pb_type->meta; |
| if(!meta->has("fasm_params")) { |
| return; |
| } |
| |
| auto iter = parameters_.find(atom->pb_graph_node->pb_type); |
| |
| if(iter == parameters_.end()) { |
| Parameters params; |
| auto* value = meta->one("fasm_params"); |
| VTR_ASSERT(value != nullptr); |
| |
| std::string fasm_params = value->as_string(); |
| for(const auto param : vtr::split(fasm_params, "\n")) { |
| auto param_parts = split_fasm_entry(param, "=", "\t "); |
| if(param_parts.size() == 0) { |
| continue; |
| } |
| VTR_ASSERT(param_parts.size() == 2); |
| |
| params.AddParameter(param_parts[1], param_parts[0]); |
| } |
| |
| auto ret = parameters_.insert(std::make_pair( |
| atom->pb_graph_node->pb_type, |
| params)); |
| |
| VTR_ASSERT(ret.second); |
| iter = ret.first; |
| } |
| |
| auto ¶ms = iter->second; |
| |
| for(auto param : atom_ctx.nlist.block_params(atom_blk_id)) { |
| auto feature = params.EmitFasmFeature(param.first, param.second); |
| |
| if(feature.size() > 0) { |
| output_fasm_features(feature); |
| } |
| } |
| } |
| |
| void FasmWriterVisitor::check_for_lut(const t_pb* atom) { |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| auto atom_blk_id = atom_ctx.lookup.pb_atom(atom); |
| if (atom_blk_id == AtomBlockId::INVALID()) { |
| return; |
| } |
| |
| const t_model* model = atom_ctx.nlist.block_model(atom_blk_id); |
| if (model->name == std::string(MODEL_NAMES)) { |
| VTR_ASSERT(atom->pb_graph_node != nullptr); |
| const auto *lut_definition = find_lut(atom->pb_graph_node); |
| VTR_ASSERT(lut_definition->num_inputs == *atom->pb_graph_node->num_input_pins); |
| |
| const t_pb_routes &pb_route = find_pb_route(atom); |
| LogicVec lut_mask = lut_outputs(atom, lut_definition->num_inputs, pb_route); |
| output_fasm_features(lut_definition->CreateInit(lut_mask)); |
| } |
| } |
| |
| void FasmWriterVisitor::visit_atom_impl(const t_pb* atom) { |
| check_for_lut(atom); |
| check_for_param(atom); |
| } |
| |
| void FasmWriterVisitor::walk_route_tree(const t_rt_node *root) { |
| for (t_linked_rt_edge* edge = root->u.child_list; edge != nullptr; edge = edge->next) { |
| auto *meta = vpr::rr_edge_metadata(root->inode, edge->child->inode, edge->iswitch, "fasm_features"); |
| if(meta != nullptr) { |
| output_fasm_features(meta->as_string(), "", ""); |
| } |
| |
| walk_route_tree(edge->child); |
| } |
| } |
| |
| void FasmWriterVisitor::walk_routing() { |
| auto& route_ctx = g_vpr_ctx.mutable_routing(); |
| |
| for(const auto &trace : route_ctx.trace) { |
| t_trace *head = trace.head; |
| if (!head) continue; |
| t_rt_node* root = traceback_to_route_tree(head); |
| walk_route_tree(root); |
| free_route_tree(root); |
| } |
| } |
| |
| |
| void FasmWriterVisitor::finish_impl() { |
| auto& device_ctx = g_vpr_ctx.device(); |
| for(unsigned int itype = 0; itype < device_ctx.logical_block_types.size(); itype++) { |
| free_pb_graph_pin_lookup_from_index (pb_graph_pin_lookup_from_index_by_type_.at(itype)); |
| } |
| |
| walk_routing(); |
| } |
| |
| void FasmWriterVisitor::find_clb_prefix(const t_pb_graph_node *node, |
| bool *have_prefix, std::string *clb_prefix) const { |
| <<<<<<< HEAD |
| ======= |
| |
| *have_prefix = false; |
| *clb_prefix = ""; |
| |
| >>>>>>> Fixed fasm output for top-level blocks without prefixes. |
| while(node != nullptr) { |
| auto clb_prefix_itr = clb_prefix_map_.find(node); |
| *have_prefix = clb_prefix_itr != clb_prefix_map_.end(); |
| if(*have_prefix) { |
| *clb_prefix = clb_prefix_itr->second; |
| return; |
| } |
| |
| node = node->parent_pb_graph_node; |
| } |
| } |
| |
| void FasmWriterVisitor::output_fasm_mux(std::string fasm_mux, |
| t_interconnect *interconnect, |
| t_pb_graph_pin *mux_input_pin) { |
| auto *pb_name = mux_input_pin->parent_node->pb_type->name; |
| auto pb_index = mux_input_pin->parent_node->placement_index; |
| auto *port_name = mux_input_pin->port->name; |
| auto pin_index = mux_input_pin->pin_number; |
| auto mux_inputs = vtr::split(fasm_mux, "\n"); |
| |
| bool have_prefix = false; |
| std::string clb_prefix; |
| find_clb_prefix(mux_input_pin->parent_node, &have_prefix, &clb_prefix); |
| |
| for(const auto &mux_input : mux_inputs) { |
| auto mux_parts = split_fasm_entry(mux_input, "=:", "\t "); |
| |
| if(mux_parts.size() == 0) { |
| // Swallow whitespace. |
| continue; |
| } |
| |
| if(mux_parts.size() != 2) { |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, |
| "fasm_mux line %s does not have 2 parts, has %d parts.\n", |
| mux_input.c_str(), mux_parts.size()); |
| } |
| |
| auto vtr_parts = vtr::split(mux_parts[0], "."); |
| if(vtr_parts.size() != 2) { |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, |
| "fasm_mux line %s does not have 2 parts, has %d parts.\n", |
| mux_parts[0].c_str(), vtr_parts.size()); |
| } |
| |
| std::string mux_pb_name; |
| int mux_pb_index; |
| parse_name_with_optional_index(vtr_parts[0], &mux_pb_name, &mux_pb_index); |
| std::string mux_port_name; |
| int mux_pin_index; |
| parse_name_with_optional_index(vtr_parts[1], &mux_port_name, &mux_pin_index); |
| |
| bool root_level_connection = interconnect->parent_mode->parent_pb_type == |
| mux_input_pin->parent_node->pb_type; |
| |
| auto fasm_features = vtr::join(vtr::split(mux_parts[1], ","), "\n"); |
| |
| |
| if(root_level_connection) { |
| // This connection is root level. pb_index selects between |
| // pb_type_prefixes_, not on the mux input. |
| if(mux_pb_name == pb_name && mux_port_name == port_name && mux_pin_index == pin_index) { |
| if(mux_parts[1] != "NULL") { |
| <<<<<<< HEAD |
| output_fasm_features(have_prefix, clb_prefix, fasm_features); |
| ======= |
| output_fasm_features(fasm_features, clb_prefix, blk_prefix_); |
| >>>>>>> Fixed fasm output for top-level blocks without prefixes. |
| } |
| return; |
| } |
| } else if(mux_pb_name == pb_name && |
| mux_pb_index == pb_index && |
| mux_port_name == port_name && |
| mux_pin_index == pin_index) { |
| if(mux_parts[1] != "NULL") { |
| output_fasm_features(fasm_features, clb_prefix, blk_prefix_); |
| } |
| return; |
| } |
| } |
| |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, |
| "fasm_mux %s[%d].%s[%d] found no matches in:\n%s\n", |
| pb_name, pb_index, port_name, pin_index, fasm_mux.c_str()); |
| } |
| |
| void FasmWriterVisitor::output_fasm_features(const std::string features) const { |
| output_fasm_features(features, clb_prefix_, blk_prefix_); |
| } |
| |
| void FasmWriterVisitor::output_fasm_features(const std::string features, const std::string clb_prefix, const std::string blk_prefix) const { |
| std::stringstream os(features); |
| |
| while(os) { |
| std::string feature; |
| os >> feature; |
| if(os) { |
| std::string out_feature; |
| out_feature += blk_prefix; |
| out_feature += clb_prefix; |
| out_feature += feature; |
| // Substitute tags |
| os_ << substitute_tags(out_feature, tags_) << std::endl; |
| } |
| } |
| |
| } |
| |
| } // namespace fasm |