Skip to content

[Packer] Pack Pattern Get Sink #2991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 24, 2025
116 changes: 71 additions & 45 deletions vpr/src/pack/prepack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,11 @@ static void init_molecule_chain_info(const AtomBlockId blk_id,
const AtomNetlist& atom_nlist);

static AtomBlockId get_sink_block(const AtomBlockId block_id,
const t_model_ports* model_port,
const BitIndex pin_number,
const t_pack_pattern_connections& connections,
const AtomNetlist& atom_nlist);

static AtomBlockId get_driving_block(const AtomBlockId block_id,
const t_model_ports* model_port,
const BitIndex pin_number,
const t_pack_pattern_connections& connections,
const AtomNetlist& atom_nlist);

static void print_chain_starting_points(t_pack_patterns* chain_pattern);
Expand Down Expand Up @@ -1047,17 +1045,13 @@ static bool try_expand_molecule(t_pack_molecule& molecule,
// this block is the driver of this connection
if (block_connection->from_block == pattern_block) {
// find the block this connection is driving and add it to the queue
auto port_model = block_connection->from_pin->port->model_port;
auto ipin = block_connection->from_pin->pin_number;
auto sink_blk_id = get_sink_block(block_id, port_model, ipin, atom_nlist);
auto sink_blk_id = get_sink_block(block_id, *block_connection, atom_nlist);
// add this sink block id with its corresponding pattern block to the queue
pattern_block_queue.push(std::make_pair(block_connection->to_block, sink_blk_id));
// this block is being driven by this connection
} else if (block_connection->to_block == pattern_block) {
// find the block that is driving this connection and it to the queue
auto port_model = block_connection->to_pin->port->model_port;
auto ipin = block_connection->to_pin->pin_number;
auto driver_blk_id = get_driving_block(block_id, port_model, ipin, atom_nlist);
auto driver_blk_id = get_driving_block(block_id, *block_connection, atom_nlist);
// add this driver block id with its corresponding pattern block to the queue
pattern_block_queue.push(std::make_pair(block_connection->from_block, driver_blk_id));
}
Expand All @@ -1076,62 +1070,94 @@ static bool try_expand_molecule(t_pack_molecule& molecule,
/**
* Find the atom block in the netlist driven by this pin of the input atom block
* If doesn't exist return AtomBlockId::INVALID()
* Limitation: The block should be driving only one sink block
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this limitation still there? Based on your code changes, it appears as though you have just handled the special case where this is a chain pattern.

Is it possible for these changes to be generalized? If its complicated I would leave as a TODO at least so people are aware of this limitation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave some thought to making it more general, but the only viable way I can think of to prevent the issue I mentioned, where both the registered and unregistered outputs of a LUT are used, is to allow breaking a molecule during packing. As you definitely know (!), that’s not a trivial task.

I’ve added the following TODO comment:

TODO: Limitation — For pack patterns other than chains, 
the block should be driven by only one block

* TODO: Limitation — For pack patterns other than chains,
* the block should be driven by only one block
* block_id : id of the atom block that is driving the net connected to the sink block
* model_port : the model of the port driving the net
* pin_number : the pin_number of the pin driving the net (pin index within the port)
* connections : pack pattern connections from the given block
*/
static AtomBlockId get_sink_block(const AtomBlockId block_id,
const t_model_ports* model_port,
const BitIndex pin_number,
const t_pack_pattern_connections& connections,
const AtomNetlist& atom_nlist) {
auto port_id = atom_nlist.find_atom_port(block_id, model_port);

if (port_id) {
auto net_id = atom_nlist.port_net(port_id, pin_number);
if (net_id && atom_nlist.net_sinks(net_id).size() == 1) { /* Single fanout assumption */
auto net_sinks = atom_nlist.net_sinks(net_id);
auto sink_pin_id = *(net_sinks.begin());
return atom_nlist.pin_block(sink_pin_id);
}
const t_model_ports* from_port_model = connections.from_pin->port->model_port;
const int from_pin_number = connections.from_pin->pin_number;
auto from_port_id = atom_nlist.find_atom_port(block_id, from_port_model);

const t_model_ports* to_port_model = connections.to_pin->port->model_port;
const int to_pin_number = connections.to_pin->pin_number;
const auto& to_pb_type = connections.to_block->pb_type;

if (!from_port_id.is_valid()) {
return AtomBlockId::INVALID();
}

return AtomBlockId::INVALID();
auto net_id = atom_nlist.port_net(from_port_id, from_pin_number);
if (!net_id.is_valid()) {
return AtomBlockId::INVALID();
}

const auto& net_sinks = atom_nlist.net_sinks(net_id);
// Iterate through all sink blocks and check whether any of them
// is compatible with the block specified in the pack pattern.
bool connected_to_latch = false;
AtomBlockId pattern_sink_block_id = AtomBlockId::INVALID();
for (const auto& sink_pin_id : net_sinks) {
auto sink_block_id = atom_nlist.pin_block(sink_pin_id);
if (atom_nlist.block_model(sink_block_id)->name == std::string(MODEL_LATCH)) {
connected_to_latch = true;
}
if (primitive_type_feasible(sink_block_id, to_pb_type)) {
auto to_port_id = atom_nlist.find_atom_port(sink_block_id, to_port_model);
auto to_pin_id = atom_nlist.find_pin(to_port_id, BitIndex(to_pin_number));
if (to_pin_id == sink_pin_id) {
pattern_sink_block_id = sink_block_id;
}
}
}
// If the number of sinks is greater than 1, and one of the connected blocks is a latch,
// then we drop the block to avoid a situation where only registers or unregistered output
// of the block can use the output pin.
// TODO: This is a conservative assumption, and ideally we need to do analysis of the architecture
// before to determine which pattern is supported by the architecture.
if (connected_to_latch && net_sinks.size() > 1) {
pattern_sink_block_id = AtomBlockId::INVALID();
}
return pattern_sink_block_id;
}

/**
* Find the atom block in the netlist driving this pin of the input atom block
* If doesn't exist return AtomBlockId::INVALID()
* Limitation: This driving block should be driving only the input block
* block_id : id of the atom block that is connected to a net driven by the driving block
* model_port : the model of the port driven by the net
* pin_number : the pin_number of the pin driven by the net (pin index within the port)
* connections : pack pattern connections from the given block
*/
static AtomBlockId get_driving_block(const AtomBlockId block_id,
const t_model_ports* model_port,
const BitIndex pin_number,
const t_pack_pattern_connections& connections,
const AtomNetlist& atom_nlist) {
auto port_id = atom_nlist.find_atom_port(block_id, model_port);

if (port_id) {
auto net_id = atom_nlist.port_net(port_id, pin_number);
if (net_id && atom_nlist.net_sinks(net_id).size() == 1) { /* Single fanout assumption */
auto to_port_model = connections.to_pin->port->model_port;
auto to_pin_number = connections.to_pin->pin_number;
auto to_port_id = atom_nlist.find_atom_port(block_id, to_port_model);

auto driver_blk_id = atom_nlist.net_driver_block(net_id);
if (!to_port_id.is_valid()) {
return AtomBlockId::INVALID();
}

if (model_port->is_clock) {
auto driver_blk_type = atom_nlist.block_type(driver_blk_id);
auto net_id = atom_nlist.port_net(to_port_id, to_pin_number);
if (net_id && atom_nlist.net_sinks(net_id).size() == 1) { /* Single fanout assumption */
auto driver_blk_id = atom_nlist.net_driver_block(net_id);

// TODO: support multi-clock primitives.
// If the driver block is a .input block, this assertion should not
// be triggered as the sink block might have only one input pin, which
// would be a clock pin in case the sink block primitive is a clock generator,
// resulting in a pin_number == 0.
VTR_ASSERT(pin_number == 1 || (pin_number == 0 && driver_blk_type == AtomBlockType::INPAD));
}
if (to_port_model->is_clock) {
auto driver_blk_type = atom_nlist.block_type(driver_blk_id);

return atom_nlist.net_driver_block(net_id);
// TODO: support multi-clock primitives.
// If the driver block is a .input block, this assertion should not
// be triggered as the sink block might have only one input pin, which
// would be a clock pin in case the sink block primitive is a clock generator,
// resulting in a pin_number == 0.
VTR_ASSERT(to_pin_number == 1 || (to_pin_number == 0 && driver_blk_type == AtomBlockType::INPAD));
}

return driver_blk_id;
}

return AtomBlockId::INVALID();
Expand Down