2020-02-21 18:47:27 -06:00
|
|
|
/************************************************************************
|
|
|
|
* Function to perform fundamental operation for the physical pb using
|
|
|
|
* data structures
|
|
|
|
***********************************************************************/
|
2021-02-18 18:18:34 -06:00
|
|
|
#include <algorithm>
|
|
|
|
|
2020-02-21 18:47:27 -06:00
|
|
|
/* Headers from vtrutil library */
|
|
|
|
#include "vtr_assert.h"
|
|
|
|
#include "vtr_log.h"
|
|
|
|
|
2021-02-01 21:49:36 -06:00
|
|
|
/* Headers from openfpgautil library */
|
|
|
|
#include "openfpga_tokenizer.h"
|
|
|
|
|
2020-02-21 18:47:27 -06:00
|
|
|
#include "openfpga_naming.h"
|
2021-02-18 20:37:17 -06:00
|
|
|
#include "lut_utils.h"
|
2020-02-21 18:47:27 -06:00
|
|
|
#include "pb_type_utils.h"
|
|
|
|
#include "physical_pb_utils.h"
|
|
|
|
|
|
|
|
/* begin namespace openfpga */
|
|
|
|
namespace openfpga {
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* Allocate an empty physical pb graph based on pb_graph
|
|
|
|
* This function should start with an empty physical pb object!!!
|
|
|
|
* Suggest to check this before executing this function
|
|
|
|
* VTR_ASSERT(true == phy_pb.empty());
|
|
|
|
***********************************************************************/
|
|
|
|
static
|
|
|
|
void rec_alloc_physical_pb_from_pb_graph(PhysicalPb& phy_pb,
|
2020-02-21 21:39:49 -06:00
|
|
|
const t_pb_graph_node* pb_graph_node,
|
2020-02-21 18:47:27 -06:00
|
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
|
|
t_pb_type* pb_type = pb_graph_node->pb_type;
|
|
|
|
|
|
|
|
t_mode* physical_mode = device_annotation.physical_mode(pb_type);
|
|
|
|
|
|
|
|
PhysicalPbId cur_phy_pb_id = phy_pb.create_pb(pb_graph_node);
|
|
|
|
VTR_ASSERT(true == phy_pb.valid_pb_id(cur_phy_pb_id));
|
|
|
|
|
|
|
|
/* Finish for primitive node */
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
2020-02-26 00:45:49 -06:00
|
|
|
/* Deposite mode bits here */
|
|
|
|
phy_pb.set_mode_bits(cur_phy_pb_id, device_annotation.pb_type_mode_bits(pb_type));
|
2020-02-21 18:47:27 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the physical mode */
|
|
|
|
VTR_ASSERT(nullptr != physical_mode);
|
|
|
|
|
|
|
|
/* Go to the leaf nodes first. This aims to build all the primitive nodes first
|
|
|
|
* and then we build the parents and create links
|
|
|
|
*/
|
|
|
|
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
|
|
|
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
|
|
|
rec_alloc_physical_pb_from_pb_graph(phy_pb,
|
|
|
|
&(pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]),
|
|
|
|
device_annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* Build all the relationships between parent and children
|
|
|
|
* inside a physical pb graph
|
|
|
|
* This function must be executed after rec_alloc_physical_pb_from_pb_graph()!!!
|
|
|
|
***********************************************************************/
|
|
|
|
static
|
|
|
|
void rec_build_physical_pb_children_from_pb_graph(PhysicalPb& phy_pb,
|
2020-02-21 21:39:49 -06:00
|
|
|
const t_pb_graph_node* pb_graph_node,
|
2020-02-21 18:47:27 -06:00
|
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
|
|
t_pb_type* pb_type = pb_graph_node->pb_type;
|
|
|
|
|
|
|
|
/* Finish for primitive node */
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
t_mode* physical_mode = device_annotation.physical_mode(pb_type);
|
|
|
|
VTR_ASSERT(nullptr != physical_mode);
|
|
|
|
|
|
|
|
/* Please use the openfpga naming function so that you can build the link to module manager */
|
|
|
|
PhysicalPbId parent_pb_id = phy_pb.find_pb(pb_graph_node);
|
|
|
|
VTR_ASSERT(true == phy_pb.valid_pb_id(parent_pb_id));
|
|
|
|
|
|
|
|
/* Add all the children */
|
|
|
|
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
|
|
|
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
|
|
|
PhysicalPbId child_pb_id = phy_pb.find_pb(&(pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]));
|
|
|
|
VTR_ASSERT(true == phy_pb.valid_pb_id(child_pb_id));
|
|
|
|
phy_pb.add_child(parent_pb_id, child_pb_id, &(physical_mode->pb_type_children[ipb]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go to the leaf nodes first. This aims to build all the primitive nodes first
|
|
|
|
* and then we build the parents and create links
|
|
|
|
*/
|
|
|
|
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
|
|
|
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
|
|
|
rec_build_physical_pb_children_from_pb_graph(phy_pb,
|
|
|
|
&(pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]),
|
|
|
|
device_annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* Allocate an empty physical pb graph based on pb_graph
|
|
|
|
* This function should start with an empty physical pb object!!!
|
|
|
|
* Suggest to check this before executing this function
|
|
|
|
* VTR_ASSERT(true == phy_pb.empty());
|
|
|
|
***********************************************************************/
|
|
|
|
void alloc_physical_pb_from_pb_graph(PhysicalPb& phy_pb,
|
2020-02-21 21:39:49 -06:00
|
|
|
const t_pb_graph_node* pb_graph_head,
|
2020-02-21 18:47:27 -06:00
|
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
|
|
VTR_ASSERT(true == phy_pb.empty());
|
|
|
|
|
|
|
|
rec_alloc_physical_pb_from_pb_graph(phy_pb, pb_graph_head, device_annotation);
|
|
|
|
rec_build_physical_pb_children_from_pb_graph(phy_pb, pb_graph_head, device_annotation);
|
|
|
|
}
|
|
|
|
|
2020-02-21 21:39:49 -06:00
|
|
|
/************************************************************************
|
|
|
|
* Update a mapping net from a pin of an operating primitive pb to a
|
|
|
|
* physical pb data base
|
|
|
|
***********************************************************************/
|
|
|
|
static
|
|
|
|
void update_primitive_physical_pb_pin_atom_net(PhysicalPb& phy_pb,
|
|
|
|
const PhysicalPbId& primitive_pb,
|
|
|
|
const t_pb_graph_pin* pb_graph_pin,
|
|
|
|
const t_pb_routes& pb_route,
|
|
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
|
|
int node_index = pb_graph_pin->pin_count_in_cluster;
|
|
|
|
if (pb_route.count(node_index)) {
|
|
|
|
/* The pin is mapped to a net, find the original pin in the atom netlist */
|
|
|
|
AtomNetId atom_net = pb_route[node_index].atom_net_id;
|
|
|
|
|
|
|
|
VTR_ASSERT(atom_net);
|
|
|
|
|
|
|
|
/* Find the physical pb_graph_pin */
|
2020-02-21 21:45:22 -06:00
|
|
|
t_pb_graph_pin* physical_pb_graph_pin = device_annotation.physical_pb_graph_pin(pb_graph_pin);
|
2020-02-21 21:39:49 -06:00
|
|
|
VTR_ASSERT(nullptr != physical_pb_graph_pin);
|
|
|
|
|
2020-04-19 17:42:31 -05:00
|
|
|
/* Print info to help debug
|
|
|
|
bool verbose = true;
|
|
|
|
VTR_LOGV(verbose,
|
|
|
|
"\nSynchronize net '%lu' to physical pb_graph_pin '%s.%s[%d]'\n",
|
|
|
|
size_t(atom_net),
|
|
|
|
pb_graph_pin->parent_node->pb_type->name,
|
|
|
|
pb_graph_pin->port->name,
|
|
|
|
pb_graph_pin->pin_number);
|
|
|
|
*/
|
|
|
|
|
2020-02-21 21:39:49 -06:00
|
|
|
/* Check if the pin has been mapped to a net.
|
|
|
|
* If yes, the atom net must be the same
|
|
|
|
*/
|
|
|
|
if (AtomNetId::INVALID() == phy_pb.pb_graph_pin_atom_net(primitive_pb, physical_pb_graph_pin)) {
|
|
|
|
phy_pb.set_pb_graph_pin_atom_net(primitive_pb, physical_pb_graph_pin, atom_net);
|
|
|
|
} else {
|
|
|
|
VTR_ASSERT(atom_net == phy_pb.pb_graph_pin_atom_net(primitive_pb, physical_pb_graph_pin));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* Synchronize mapping nets from an operating primitive pb to a physical pb
|
|
|
|
***********************************************************************/
|
|
|
|
static
|
|
|
|
void synchronize_primitive_physical_pb_atom_nets(PhysicalPb& phy_pb,
|
|
|
|
const PhysicalPbId& primitive_pb,
|
|
|
|
const t_pb_graph_node* pb_graph_node,
|
|
|
|
const t_pb_routes& pb_route,
|
|
|
|
const AtomContext& atom_ctx,
|
|
|
|
const AtomBlockId& atom_blk,
|
|
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
|
|
/* Iterate over all the ports: input, output and clock */
|
2020-04-19 17:42:31 -05:00
|
|
|
|
2020-02-21 21:39:49 -06:00
|
|
|
for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ++ipin) {
|
|
|
|
/* Port exists (some LUTs may have no input and hence no port in the atom netlist) */
|
|
|
|
t_model_ports* model_port = pb_graph_node->input_pins[iport][ipin].port->model_port;
|
|
|
|
if (nullptr == model_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AtomPortId atom_port = atom_ctx.nlist.find_atom_port(atom_blk, model_port);
|
|
|
|
if (!atom_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Find the atom nets mapped to the pin
|
|
|
|
* Note that some inputs may not be used, we set them to be open by default
|
|
|
|
*/
|
|
|
|
update_primitive_physical_pb_pin_atom_net(phy_pb, primitive_pb,
|
|
|
|
&(pb_graph_node->input_pins[iport][ipin]),
|
|
|
|
pb_route, device_annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int iport = 0; iport < pb_graph_node->num_output_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ++ipin) {
|
|
|
|
/* Port exists (some LUTs may have no input and hence no port in the atom netlist) */
|
|
|
|
t_model_ports* model_port = pb_graph_node->output_pins[iport][ipin].port->model_port;
|
|
|
|
if (nullptr == model_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AtomPortId atom_port = atom_ctx.nlist.find_atom_port(atom_blk, model_port);
|
|
|
|
if (!atom_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Find the atom nets mapped to the pin
|
|
|
|
* Note that some inputs may not be used, we set them to be open by default
|
|
|
|
*/
|
|
|
|
update_primitive_physical_pb_pin_atom_net(phy_pb, primitive_pb,
|
|
|
|
&(pb_graph_node->output_pins[iport][ipin]),
|
|
|
|
pb_route, device_annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int iport = 0; iport < pb_graph_node->num_clock_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_clock_pins[iport]; ++ipin) {
|
|
|
|
/* Port exists (some LUTs may have no input and hence no port in the atom netlist) */
|
|
|
|
t_model_ports* model_port = pb_graph_node->clock_pins[iport][ipin].port->model_port;
|
|
|
|
if (nullptr == model_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AtomPortId atom_port = atom_ctx.nlist.find_atom_port(atom_blk, model_port);
|
|
|
|
if (!atom_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Find the atom nets mapped to the pin
|
|
|
|
* Note that some inputs may not be used, we set them to be open by default
|
|
|
|
*/
|
|
|
|
update_primitive_physical_pb_pin_atom_net(phy_pb, primitive_pb,
|
|
|
|
&(pb_graph_node->clock_pins[iport][ipin]),
|
|
|
|
pb_route, device_annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 17:42:31 -05:00
|
|
|
/************************************************************************
|
|
|
|
* Reach this function, the primitive pb should be
|
|
|
|
* - linked to a LUT pb_type
|
|
|
|
* - operating in the wire mode of a LUT
|
|
|
|
*
|
|
|
|
* Note: this function will not check the prequistics here
|
|
|
|
* Users must be responsible for this!!!
|
|
|
|
*
|
|
|
|
* This function will find the physical pb_graph_pin for each output
|
|
|
|
* of the pb_graph node and mark in the physical_pb database
|
|
|
|
* as driven by an wired LUT
|
|
|
|
***********************************************************************/
|
|
|
|
static
|
|
|
|
void mark_physical_pb_wired_lut_outputs(PhysicalPb& phy_pb,
|
|
|
|
const PhysicalPbId& primitive_pb,
|
|
|
|
const t_pb_graph_node* pb_graph_node,
|
|
|
|
const VprDeviceAnnotation& device_annotation,
|
|
|
|
const bool& verbose) {
|
|
|
|
|
|
|
|
for (int iport = 0; iport < pb_graph_node->num_output_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ++ipin) {
|
|
|
|
t_pb_graph_pin* pb_graph_pin = &(pb_graph_node->output_pins[iport][ipin]);
|
|
|
|
|
|
|
|
/* Find the physical pb_graph_pin */
|
|
|
|
t_pb_graph_pin* physical_pb_graph_pin = device_annotation.physical_pb_graph_pin(pb_graph_pin);
|
|
|
|
VTR_ASSERT(nullptr != physical_pb_graph_pin);
|
|
|
|
|
|
|
|
/* Print debug info */
|
|
|
|
VTR_LOGV(verbose,
|
|
|
|
"Mark physical pb_graph pin '%s.%s[%d]' as wire LUT output\n",
|
|
|
|
physical_pb_graph_pin->parent_node->pb_type->name,
|
|
|
|
physical_pb_graph_pin->port->name,
|
|
|
|
physical_pb_graph_pin->pin_number);
|
|
|
|
|
|
|
|
/* Label the pins in physical_pb as driven by wired LUT*/
|
|
|
|
phy_pb.set_wire_lut_output(primitive_pb, physical_pb_graph_pin, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-21 18:47:27 -06:00
|
|
|
/************************************************************************
|
|
|
|
* Synchronize mapping results from an operating pb to a physical pb
|
|
|
|
***********************************************************************/
|
|
|
|
void rec_update_physical_pb_from_operating_pb(PhysicalPb& phy_pb,
|
2020-02-21 21:39:49 -06:00
|
|
|
const t_pb* op_pb,
|
|
|
|
const t_pb_routes& pb_route,
|
|
|
|
const AtomContext& atom_ctx,
|
2020-04-19 17:42:31 -05:00
|
|
|
const VprDeviceAnnotation& device_annotation,
|
2021-02-01 21:49:36 -06:00
|
|
|
const VprBitstreamAnnotation& bitstream_annotation,
|
2020-04-19 17:42:31 -05:00
|
|
|
const bool& verbose) {
|
2020-02-21 21:39:49 -06:00
|
|
|
t_pb_graph_node* pb_graph_node = op_pb->pb_graph_node;
|
|
|
|
t_pb_type* pb_type = pb_graph_node->pb_type;
|
|
|
|
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
|
|
t_pb_graph_node* physical_pb_graph_node = device_annotation.physical_pb_graph_node(pb_graph_node);
|
|
|
|
VTR_ASSERT(nullptr != physical_pb_graph_node);
|
|
|
|
/* Find the physical pb */
|
|
|
|
const PhysicalPbId& physical_pb = phy_pb.find_pb(physical_pb_graph_node);
|
|
|
|
VTR_ASSERT(true == phy_pb.valid_pb_id(physical_pb));
|
|
|
|
|
|
|
|
/* Set the mode bits */
|
2020-02-26 00:29:16 -06:00
|
|
|
phy_pb.set_mode_bits(physical_pb, device_annotation.pb_type_mode_bits(pb_type));
|
2020-02-21 21:39:49 -06:00
|
|
|
|
|
|
|
/* Find mapped atom block and add to this physical pb */
|
|
|
|
AtomBlockId atom_blk = atom_ctx.nlist.find_block(op_pb->name);
|
|
|
|
VTR_ASSERT(atom_blk);
|
|
|
|
|
|
|
|
phy_pb.add_atom_block(physical_pb, atom_blk);
|
2021-03-10 22:28:09 -06:00
|
|
|
|
2021-02-01 21:49:36 -06:00
|
|
|
/* if the operating pb type has bitstream annotation,
|
|
|
|
* bind the bitstream value from atom block to the physical pb
|
|
|
|
*/
|
|
|
|
if (VprBitstreamAnnotation::e_bitstream_source_type::BITSTREAM_SOURCE_EBLIF == bitstream_annotation.pb_type_bitstream_source(pb_type)) {
|
|
|
|
StringToken tokenizer = bitstream_annotation.pb_type_bitstream_content(pb_type);
|
|
|
|
std::vector<std::string> tokens = tokenizer.split(" ");
|
|
|
|
/* FIXME: The token-level check should be done much earlier!!! */
|
|
|
|
VTR_ASSERT(2 == tokens.size());
|
2021-03-10 21:45:48 -06:00
|
|
|
/* The token is typically organized as <.param|.attr> <identifier string> */
|
2021-02-01 21:49:36 -06:00
|
|
|
if (std::string(".param") == tokens[0]) {
|
|
|
|
for (const auto& param_search : atom_ctx.nlist.block_params(atom_blk)) {
|
2021-03-10 21:45:48 -06:00
|
|
|
/* Bypass unmatched parameter identifier */
|
|
|
|
if (param_search.first != tokens[1]) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-03-10 22:28:09 -06:00
|
|
|
phy_pb.set_fixed_bitstream(physical_pb, param_search.second);
|
|
|
|
phy_pb.set_fixed_bitstream_offset(physical_pb, bitstream_annotation.pb_type_bitstream_offset(pb_type));
|
|
|
|
}
|
|
|
|
} else if (std::string(".attr") == tokens[0]) {
|
|
|
|
for (const auto& attr_search : atom_ctx.nlist.block_attrs(atom_blk)) {
|
|
|
|
/* Bypass unmatched parameter identifier */
|
|
|
|
if (attr_search.first == tokens[1]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
phy_pb.set_fixed_bitstream(physical_pb, attr_search.second);
|
|
|
|
phy_pb.set_fixed_bitstream_offset(physical_pb, bitstream_annotation.pb_type_bitstream_offset(pb_type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* if the operating pb type has mode-select bitstream annotation,
|
|
|
|
* bind the bitstream value from atom block to the physical pb
|
|
|
|
*/
|
|
|
|
if (VprBitstreamAnnotation::e_bitstream_source_type::BITSTREAM_SOURCE_EBLIF == bitstream_annotation.pb_type_mode_select_bitstream_source(pb_type)) {
|
|
|
|
StringToken tokenizer = bitstream_annotation.pb_type_mode_select_bitstream_content(pb_type);
|
|
|
|
std::vector<std::string> tokens = tokenizer.split(" ");
|
|
|
|
/* FIXME: The token-level check should be done much earlier!!! */
|
|
|
|
VTR_ASSERT(2 == tokens.size());
|
|
|
|
/* The token is typically organized as <.param|.attr> <identifier string> */
|
|
|
|
if (std::string(".param") == tokens[0]) {
|
|
|
|
for (const auto& param_search : atom_ctx.nlist.block_params(atom_blk)) {
|
|
|
|
/* Bypass unmatched parameter identifier */
|
|
|
|
if (param_search.first != tokens[1]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
phy_pb.set_fixed_mode_select_bitstream(physical_pb, param_search.second);
|
|
|
|
phy_pb.set_fixed_mode_select_bitstream_offset(physical_pb, bitstream_annotation.pb_type_mode_select_bitstream_offset(pb_type));
|
2021-02-01 21:49:36 -06:00
|
|
|
}
|
|
|
|
} else if (std::string(".attr") == tokens[0]) {
|
|
|
|
for (const auto& attr_search : atom_ctx.nlist.block_attrs(atom_blk)) {
|
2021-03-10 21:45:48 -06:00
|
|
|
/* Bypass unmatched parameter identifier */
|
2021-02-01 21:49:36 -06:00
|
|
|
if (attr_search.first == tokens[1]) {
|
2021-03-10 21:45:48 -06:00
|
|
|
continue;
|
|
|
|
}
|
2021-03-10 22:28:09 -06:00
|
|
|
phy_pb.set_fixed_mode_select_bitstream(physical_pb, attr_search.second);
|
|
|
|
phy_pb.set_fixed_mode_select_bitstream_offset(physical_pb, bitstream_annotation.pb_type_mode_select_bitstream_offset(pb_type));
|
2021-02-01 21:49:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-21 21:39:49 -06:00
|
|
|
|
2020-02-25 14:34:13 -06:00
|
|
|
/* Iterate over ports and annotate the atom pins */
|
2020-02-21 21:39:49 -06:00
|
|
|
synchronize_primitive_physical_pb_atom_nets(phy_pb, physical_pb,
|
|
|
|
pb_graph_node,
|
|
|
|
pb_route,
|
|
|
|
atom_ctx, atom_blk,
|
|
|
|
device_annotation);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Walk through the pb recursively but only visit the mapped modes and child pbs */
|
|
|
|
t_mode* mapped_mode = &(pb_graph_node->pb_type->modes[op_pb->mode]);
|
|
|
|
for (int ipb = 0; ipb < mapped_mode->num_pb_type_children; ++ipb) {
|
|
|
|
/* Each child may exist multiple times in the hierarchy*/
|
|
|
|
for (int jpb = 0; jpb < mapped_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
|
|
|
if ((nullptr != op_pb->child_pbs[ipb]) && (nullptr != op_pb->child_pbs[ipb][jpb].name)) {
|
|
|
|
rec_update_physical_pb_from_operating_pb(phy_pb,
|
|
|
|
&(op_pb->child_pbs[ipb][jpb]),
|
|
|
|
pb_route,
|
|
|
|
atom_ctx,
|
2020-04-19 17:42:31 -05:00
|
|
|
device_annotation,
|
2021-02-01 21:49:36 -06:00
|
|
|
bitstream_annotation,
|
2020-04-19 17:42:31 -05:00
|
|
|
verbose);
|
|
|
|
} else {
|
|
|
|
/* Some pb may be used just in routing purpose, find out the output nets */
|
|
|
|
/* The following code is inspired by output_cluster.cpp */
|
|
|
|
bool is_used = false;
|
|
|
|
t_pb_type* child_pb_type = &(mapped_mode->pb_type_children[ipb]);
|
2020-04-22 18:28:16 -05:00
|
|
|
|
|
|
|
/* Bypass non-primitive pb_type, we care only the LUT pb_type */
|
|
|
|
if (false == is_primitive_pb_type(child_pb_type)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-04-19 17:42:31 -05:00
|
|
|
int port_index = 0;
|
|
|
|
t_pb_graph_node* child_pb_graph_node = &(pb_graph_node->child_pb_graph_nodes[op_pb->mode][ipb][jpb]);
|
|
|
|
|
|
|
|
for (int k = 0; k < child_pb_type->num_ports && !is_used; k++) {
|
|
|
|
if (OUT_PORT == child_pb_type->ports[k].type) {
|
|
|
|
for (int m = 0; m < child_pb_type->ports[k].num_pins; m++) {
|
|
|
|
int node_index = child_pb_graph_node->output_pins[port_index][m].pin_count_in_cluster;
|
|
|
|
if (pb_route.count(node_index) && pb_route[node_index].atom_net_id) {
|
|
|
|
is_used = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port_index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Identify output pb_graph_pin that is driven by a wired LUT
|
|
|
|
* Without this function, physical Look-Up Table build-up will cause errors
|
|
|
|
* and bitstream will be incorrect!!!
|
|
|
|
*/
|
|
|
|
if (true == is_used) {
|
|
|
|
VTR_ASSERT(LUT_CLASS == child_pb_type->class_type);
|
|
|
|
|
|
|
|
t_pb_graph_node* physical_pb_graph_node = device_annotation.physical_pb_graph_node(child_pb_graph_node);
|
|
|
|
VTR_ASSERT(nullptr != physical_pb_graph_node);
|
|
|
|
/* Find the physical pb */
|
|
|
|
const PhysicalPbId& physical_pb = phy_pb.find_pb(physical_pb_graph_node);
|
|
|
|
VTR_ASSERT(true == phy_pb.valid_pb_id(physical_pb));
|
|
|
|
|
|
|
|
/* Set the mode bits */
|
|
|
|
phy_pb.set_mode_bits(physical_pb, device_annotation.pb_type_mode_bits(child_pb_type));
|
|
|
|
|
|
|
|
mark_physical_pb_wired_lut_outputs(phy_pb, physical_pb,
|
|
|
|
child_pb_graph_node,
|
|
|
|
device_annotation,
|
|
|
|
verbose);
|
|
|
|
}
|
2020-02-21 21:39:49 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-21 18:47:27 -06:00
|
|
|
}
|
|
|
|
|
2021-02-18 20:37:17 -06:00
|
|
|
/***************************************************************************************
|
|
|
|
* This function will identify all the wire LUTs that is created by repacker only
|
|
|
|
* under a physical pb
|
2021-02-18 18:18:34 -06:00
|
|
|
*
|
|
|
|
* A practical example of wire LUT that is created by VPR packer:
|
|
|
|
*
|
|
|
|
* LUT
|
|
|
|
* +------------+
|
|
|
|
* | |
|
|
|
|
* netA ---->+----+ |
|
|
|
|
* | |-------+-----> netC
|
|
|
|
* netB ---->+----+ |
|
|
|
|
* | |
|
|
|
|
* netC ---->+------------+-----> netC
|
|
|
|
* | |
|
|
|
|
* +------------+
|
|
|
|
*
|
|
|
|
* A fracturable LUT may be mapped to two functions:
|
|
|
|
* - a function which involves netA, netB and netC
|
|
|
|
* the function is defined in an atom block atomA
|
|
|
|
* In this case, netC's driver block in atom context is atomA
|
|
|
|
* - a function which just wire netC through the LUT
|
|
|
|
* the function is NOT defined in any atom block
|
|
|
|
* Such wire LUT is created by VPR's packer
|
|
|
|
*
|
|
|
|
* THIS CASE IS WHAT THIS FUNCTION IS HANDLING
|
|
|
|
* A practical example of wire LUT that is created by repacker:
|
|
|
|
*
|
|
|
|
* LUT
|
|
|
|
* +------------+
|
|
|
|
* | |
|
|
|
|
* netA ---->+----+ |
|
|
|
|
* | |-------+-----> netC
|
|
|
|
* netB ---->+----+ |
|
|
|
|
* | |
|
|
|
|
* netD ---->+------------+-----> netD
|
|
|
|
* | |
|
|
|
|
* +------------+
|
|
|
|
*
|
|
|
|
* A fracturable LUT may be mapped to two functions:
|
|
|
|
* - a function which involves netA, netB and netC
|
|
|
|
* the function is defined in an atom block atomA
|
|
|
|
* In this case, netC's driver block in atom context is atomA
|
|
|
|
* - a function which just wire netD through the LUT
|
|
|
|
* the function is NOT defined in any atom block
|
|
|
|
* netD is driven by another atom block atomB which is not mapped to the LUT
|
|
|
|
* Such wire LUT is created by repacker
|
|
|
|
*
|
2021-02-18 20:37:17 -06:00
|
|
|
* Return the number of wire LUTs that are found
|
|
|
|
***************************************************************************************/
|
|
|
|
int identify_one_physical_pb_wire_lut_created_by_repack(PhysicalPb& physical_pb,
|
|
|
|
const PhysicalPbId& lut_pb_id,
|
|
|
|
const VprDeviceAnnotation& device_annotation,
|
2021-02-18 18:18:34 -06:00
|
|
|
const AtomContext& atom_ctx,
|
2021-02-18 20:37:17 -06:00
|
|
|
const CircuitLibrary& circuit_lib,
|
|
|
|
const bool& verbose) {
|
|
|
|
int wire_lut_counter = 0;
|
|
|
|
const t_pb_graph_node* pb_graph_node = physical_pb.pb_graph_node(lut_pb_id);
|
|
|
|
|
|
|
|
CircuitModelId lut_model = device_annotation.pb_type_circuit_model(physical_pb.pb_graph_node(lut_pb_id)->pb_type);
|
|
|
|
VTR_ASSERT(CIRCUIT_MODEL_LUT == circuit_lib.model_type(lut_model));
|
|
|
|
|
|
|
|
/* Find all the nets mapped to each inputs */
|
|
|
|
std::vector<AtomNetId> input_nets;
|
|
|
|
for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ++ipin) {
|
|
|
|
/* Skip the input pin that do not drive by LUT MUXes */
|
|
|
|
CircuitPortId circuit_port = device_annotation.pb_circuit_port(pb_graph_node->input_pins[iport][ipin].port);
|
|
|
|
if (true == circuit_lib.port_is_harden_lut_port(circuit_port)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
input_nets.push_back(physical_pb.pb_graph_pin_atom_net(lut_pb_id, &(pb_graph_node->input_pins[iport][ipin])));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find all the nets mapped to each outputs */
|
|
|
|
for (int iport = 0; iport < pb_graph_node->num_output_ports; ++iport) {
|
|
|
|
for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ++ipin) {
|
|
|
|
const t_pb_graph_pin* output_pin = &(pb_graph_node->output_pins[iport][ipin]);
|
|
|
|
/* Skip the output ports that are not driven by LUT MUXes */
|
|
|
|
CircuitPortId circuit_port = device_annotation.pb_circuit_port(output_pin->port);
|
|
|
|
if (true == circuit_lib.port_is_harden_lut_port(circuit_port)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AtomNetId output_net = physical_pb.pb_graph_pin_atom_net(lut_pb_id, output_pin);
|
|
|
|
/* Bypass unmapped pins */
|
|
|
|
if (AtomNetId::INVALID() == output_net) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-02-18 18:18:34 -06:00
|
|
|
/* Exclude all the LUTs that
|
|
|
|
* - have been used as wires
|
|
|
|
* - the driver atom block of the output_net is part of the atom blocks
|
|
|
|
* If so, the driver atom block is already mapped to this pb
|
|
|
|
* and the LUT is not used for wiring
|
|
|
|
*/
|
|
|
|
if (true == physical_pb.is_wire_lut_output(lut_pb_id, output_pin)) {
|
|
|
|
continue;
|
2021-02-18 20:37:17 -06:00
|
|
|
}
|
2021-02-18 18:18:34 -06:00
|
|
|
|
|
|
|
std::vector<AtomBlockId> pb_atom_blocks = physical_pb.atom_blocks(lut_pb_id);
|
|
|
|
|
|
|
|
if (pb_atom_blocks.end() != std::find(pb_atom_blocks.begin(), pb_atom_blocks.end(), atom_ctx.nlist.net_driver_block(output_net))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bypass the net is NOT routed through the LUT */
|
|
|
|
if (false == is_wired_lut(input_nets, output_net)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print debug info */
|
|
|
|
VTR_LOGV(verbose,
|
|
|
|
"Identify physical pb_graph pin '%s.%s[%d]' as wire LUT output created by repacker\n",
|
|
|
|
output_pin->parent_node->pb_type->name,
|
|
|
|
output_pin->port->name,
|
|
|
|
output_pin->pin_number);
|
|
|
|
|
|
|
|
/* Label the pins in physical_pb as driven by wired LUT*/
|
|
|
|
physical_pb.set_wire_lut_output(lut_pb_id, output_pin, true);
|
|
|
|
wire_lut_counter++;
|
2021-02-18 20:37:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return wire_lut_counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-21 18:47:27 -06:00
|
|
|
} /* end namespace openfpga */
|