Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
85b82b1
[test] add a strong test where clb, dsp and bram in a same tile, to v…
tangxifan Oct 24, 2025
1009339
[test] deploy new test to strong reg test
tangxifan Oct 24, 2025
6672c7a
[test] add new test
tangxifan Oct 24, 2025
9da2e46
[test] add new arch from openfpga
tangxifan Oct 24, 2025
83540ce
[test] add new blif benchmark for tileable direct connection strong t…
tangxifan Oct 24, 2025
90de0e8
[test] add new example arch to validate tileable direct connections
tangxifan Oct 24, 2025
615eb82
[test] reorg tasks
tangxifan Oct 24, 2025
da7c322
[test] deploy new tests to validate supertile with z_offset in direct…
tangxifan Oct 24, 2025
cafdcd1
[test] debug
tangxifan Oct 24, 2025
27d3040
[test] debug
tangxifan Oct 24, 2025
6fc15ec
[core] debug
tangxifan Oct 24, 2025
881255c
[core] now search all the sub tiles which contains the ports defined …
tangxifan Oct 24, 2025
df2a7de
[core] syntax
tangxifan Oct 24, 2025
2ea4ad0
[test] debug
tangxifan Oct 24, 2025
2107714
[test] update golden
tangxifan Oct 24, 2025
55ad286
[test] add missing golden results
tangxifan Oct 24, 2025
1067e11
Merge branch 'master' into xt_tileable_direct_strong
tangxifan Oct 24, 2025
e8ebafb
[core] add doxygen-style comments to vpr utility function
tangxifan Oct 24, 2025
d93f036
[core] simplify swap in if condition
tangxifan Oct 24, 2025
97c7bc8
Merge branch 'master' into xt_tileable_direct_strong
tangxifan Oct 24, 2025
9baa66e
[test] add arch file to validate direct connections across subtiles f…
tangxifan Oct 24, 2025
dcbe115
[test] add new testcases for strong reg tests about direct connection…
tangxifan Oct 24, 2025
bd589a9
[core] fixed the bug where IPIN coordinate does not consider pin offs…
tangxifan Oct 24, 2025
2ffbff3
[core] should sit with root location for IPIN
tangxifan Oct 24, 2025
68d6e23
[core] debug
tangxifan Oct 24, 2025
e11a7ee
[core] debug
tangxifan Oct 24, 2025
daa754f
[core] debug
tangxifan Oct 24, 2025
4c9b8fa
[test] update golden
tangxifan Oct 24, 2025
8d3ab0e
Merge branch 'xt_tileable_direct_strong' of github.com:verilog-to-rou…
tangxifan Oct 24, 2025
aa76d71
[doc] add examples
tangxifan Oct 24, 2025
35eeb2e
[doc] typo
tangxifan Oct 24, 2025
07e8b6f
[core] typo
tangxifan Oct 24, 2025
7ffcdf0
[doc] typo
tangxifan Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 83 additions & 4 deletions doc/src/arch/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2365,6 +2365,8 @@ Direct Inter-block Connections
The content within the ``<directlist>`` tag consists of a group of ``<direct>`` tags.
The ``<direct>`` tag and its contents are described below.

.. note:: ``from_pin`` and ``to_pin`` only support big endian! For example, ``clb.out[8:0]``

.. arch:tag:: <direct name="string" from_pin="string" to_pin="string" x_offset="int" y_offset="int" z_offset="int" switch_name="string" from_side="{left|right|top|bottom}" to_side="{left|right|top|bottom}"/>

:req_param name: is a unique alphanumeric string to name the connection.
Expand All @@ -2384,12 +2386,89 @@ The ``<direct>`` tag and its contents are described below.
The ``from_side`` and ``to_side`` options can usually be left unspecified.
However they can be used to explicitly control how direct connections to physically equivalent pins (which may appear on multiple sides) are handled.

**Example:**
Consider a carry chain where the ``cout`` of each CLB drives the ``cin`` of the CLB immediately below it, using the delay-less switch one would enter the following:
**Example: Inter-tile connection**
Consider a carry chain where the ``cout`` of each CLB drives the ``cin`` of the CLB immediately below it, using the delay-less switch one would enter the following:

.. code-block:: xml
.. code-block:: xml

<direct name="adder_carry" from_pin="clb.cout" to_pin="clb.cin" x_offset="0" y_offset="-1" z_offset="0"/>

**Example: Inner-tile feedback**

Consider a feedback connection where the ``out`` of each CLB drives the ``in`` of the CLB in the same location, using the connection block switch one would enter the following:

.. code-block:: xml

<direct name="feedback" from_pin="clb.out" to_pin="clb.in" x_offset="0" y_offset="0" z_offset="0" switch_name="cb_mux"/>

**Example: Cross-sub-tile connection**

In this example, a tile ``cim8_1k`` is defined, under which there are two types of sub-tiles:

- ``mult_8``: the first sub-tile
- ``memory``: the second, and the third sub-tile

.. code-block:: xml

<tile name="cim8_1k" height="2" area="396000">
<sub_tile name="mult_8" capacity="1">
<equivalent_sites>
<site pb_type="mult_8" pin_mapping="direct"/>
</equivalent_sites>
<input name="a" num_pins="8"/>
<input name="b" num_pins="8"/>
<output name="out" num_pins="16"/>
<fc in_type="frac" in_val="0.15" out_type="frac" out_val="0.10">
<fc_override port_name="out" fc_type="frac" fc_val="0"/>
</fc>
<pinlocations pattern="custom">
<loc side="left"/>
<loc side="top"/>
<loc side="right" yoffset="0">mult_8.a[0:2] mult_8.b[0:2] mult_8.out[0:5]</loc>
<loc side="right" yoffset="1">mult_8.a[3:5] mult_8.b[3:5] mult_8.out[6:10]</loc>
<loc side="bottom">mult_8.a[6:7] mult_8.b[6:7] mult_8.out[11:15]</loc>
</pinlocations>
</sub_tile>
<sub_tile name="memory" capacity="2">
<equivalent_sites>
<site pb_type="memory"/>
</equivalent_sites>
<input name="waddr" num_pins="7"/>
<input name="raddr" num_pins="7"/>
<input name="data_in" num_pins="8"/>
<input name="wen" num_pins="1"/>
<input name="ren" num_pins="1"/>
<output name="data_out" num_pins="8"/>
<clock name="clk" num_pins="1"/>
<fc in_type="frac" in_val="0.15" out_type="frac" out_val="0.10">
<fc_override port_name="clk" fc_type="frac" fc_val="0"/>
<fc_override port_name="data_in" fc_type="frac" fc_val="0"/>
</fc>
<pinlocations pattern="custom">
<loc side="left" yoffset="0">memory.clk memory.waddr[0:0] memory.raddr[0:0] memory.data_in[0:0] memory.data_out[0:0]</loc>
<loc side="left" yoffset="1">memory.waddr[1:1] memory.raddr[1:1] memory.data_in[1:1] memory.data_out[1:1]</loc>
<loc side="top" yoffset="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]</loc>
<loc side="right" yoffset="0">memory.waddr[4:4] memory.raddr[4:4] memory.data_in[4:4] memory.data_out[4:4]</loc>
<loc side="right" yoffset="1">memory.waddr[5:5] memory.raddr[5:5] memory.data_in[5:5] memory.data_out[5:5]</loc>
<loc side="bottom" yoffset="0">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]</loc>
</pinlocations>
</sub_tile>
</tile>

As shown in :numref:`fig_example_subtile_direct_connection`, consider a connection where the ``out`` of a sub tile ``mult_8`` of tile ``cim8_1k`` drives the ``data_in`` of the sub tile ``memory`` of tile ``cim8_1k`` with an offset, using the delayless switch one would enter the following:

.. code-block:: xml

<direct name="cim_direct0" from_pin="cim8_1k.out[7:0]" to_pin="cim8_1k.data_in[7:0]" x_offset="0" y_offset="0" z_offset="1"/>
<direct name="cim_direct1" from_pin="cim8_1k.out[15:8]" to_pin="cim8_1k.data_in[7:0]" x_offset="0" y_offset="0" z_offset="2"/>

.. _fig_example_subtile_direct_connection:

.. figure:: ./example_subtile_direct_connection.png
:width: 60%
:alt: Example of direct connections across sub-tiles

<direct name="adder_carry" from_pin="clb.cout" to_pin="clb.cin" x_offset="0" y_offset="-1" z_offset="0"/>
Example of direct connections across sub-tiles

.. _custom_switch_blocks:

Expand Down
5 changes: 5 additions & 0 deletions vpr/src/route/rr_graph_generation/clb2clb_directs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ std::vector<t_clb_to_clb_directs> alloc_and_load_clb_to_clb_directs(const std::v
clb_to_clb_directs[i].from_clb_type = physical_tile;

t_physical_tile_port tile_port = find_tile_port_by_name(physical_tile, port_name);
/* Find the sub tile indices */
clb_to_clb_directs[i].from_sub_tiles = find_sub_tile_indices_by_port_name(physical_tile, port_name);
if (clb_to_clb_directs[i].from_sub_tiles.empty()) {
VPR_THROW(VPR_ERROR_ARCH, "Unable to find sub tile under tile '%s' which contains the port %s.\n", tile_name.c_str(), port_name.data());
}

if (start_pin_index == UNDEFINED) {
VTR_ASSERT(start_pin_index == end_pin_index);
Expand Down
1 change: 1 addition & 0 deletions vpr/src/route/rr_graph_generation/clb2clb_directs.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
struct t_clb_to_clb_directs {
t_physical_tile_type_ptr from_clb_type;
std::vector<int> from_sub_tiles;
int from_clb_pin_start_index;
int from_clb_pin_end_index;
t_physical_tile_type_ptr to_clb_type;
Expand Down
12 changes: 9 additions & 3 deletions vpr/src/route/rr_graph_generation/rr_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2939,7 +2939,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder,
auto [z, relative_opin] = get_capacity_location_from_physical_pin(curr_type, opin);
VTR_ASSERT(z >= 0 && z < curr_type->capacity);
const int num_directs = directs.size();

// Iterate through all direct connections
for (int i = 0; i < num_directs; i++) {
// Find matching direct clb-to-clb connections with the same type as current grid location
Expand Down Expand Up @@ -3012,15 +3012,21 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder,
// Add new ipin edge to list of edges
std::vector<RRNodeId> inodes;

int target_width_offset = device_ctx.grid.get_width_offset({x + directs[i].x_offset, y + directs[i].y_offset, layer});
int target_height_offset = device_ctx.grid.get_height_offset({x + directs[i].x_offset, y + directs[i].y_offset, layer});
int final_ipin_x = x + directs[i].x_offset - target_width_offset + target_type->pin_width_offset[ipin];
int final_ipin_y = y + directs[i].y_offset - target_height_offset + target_type->pin_height_offset[ipin];

if (directs[i].to_side != NUM_2D_SIDES) {
//Explicit side specified, only create if pin exists on that side
RRNodeId inode = rr_graph_builder.node_lookup().find_node(layer, x + directs[i].x_offset, y + directs[i].y_offset, e_rr_type::IPIN, ipin, directs[i].to_side);
RRNodeId inode = rr_graph_builder.node_lookup().find_node(layer, final_ipin_x, final_ipin_y,
e_rr_type::IPIN, ipin, directs[i].to_side);
if (inode) {
inodes.push_back(inode);
}
} else {
//No side specified, get all candidates
inodes = rr_graph_builder.node_lookup().find_nodes_at_all_sides(layer, x + directs[i].x_offset, y + directs[i].y_offset, e_rr_type::IPIN, ipin);
inodes = rr_graph_builder.node_lookup().find_nodes_at_all_sides(layer, final_ipin_x, final_ipin_y, e_rr_type::IPIN, ipin);
}

if (inodes.size() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1722,74 +1722,81 @@ void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph,
}

/* get every opin in the range */
for (int opin = min_index; opin <= max_index; ++opin) {
int offset = opin - min_index;
for (int relative_opin = min_index; relative_opin <= max_index; ++relative_opin) {
int offset = relative_opin - min_index;
//Capacity location determined by pin number relative to pins per capacity instance
auto [z, relative_opin] = get_capacity_location_from_physical_pin(grid_type, opin);
VTR_ASSERT(z >= 0 && z < grid_type->capacity);

if ((to_grid_coordinate.x() < grids.width() - 1)
&& (to_grid_coordinate.y() < grids.height() - 1)) {
int relative_ipin = UNDEFINED;
if (clb_to_clb_directs[i].to_clb_pin_start_index
> clb_to_clb_directs[i].to_clb_pin_end_index) {
if (true == swap) {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset;
for (int z : clb_to_clb_directs[i].from_sub_tiles) {
int opin = get_physical_pin_from_capacity_location(grid_type, relative_opin, z);
VTR_ASSERT(z >= 0 && z < grid_type->capacity);

if ((to_grid_coordinate.x() < grids.width() - 1)
&& (to_grid_coordinate.y() < grids.height() - 1)) {
int relative_ipin = UNDEFINED;
if (clb_to_clb_directs[i].to_clb_pin_start_index
> clb_to_clb_directs[i].to_clb_pin_end_index) {
if (swap) {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset;
} else {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset;
}
} else {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset;
if (swap) {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset;
} else {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset;
}
}
} else {
if (true == swap) {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset;
} else {
relative_ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset;

/* Get the pin index in the rr_graph */
t_physical_tile_loc from_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer);
t_physical_tile_loc to_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer);

/* Find the side of grid pins, the pin location should be unique!
* Pin location is required by searching a node in rr_graph
*/
std::vector<e_side> opin_grid_side = find_grid_pin_sides(grids, layer, from_grid_coordinate.x() + grid_type->pin_width_offset[opin], from_grid_coordinate.y() + grid_type->pin_height_offset[opin], opin);
if (1 != opin_grid_side.size()) {
VPR_FATAL_ERROR(VPR_ERROR_ARCH, "[Arch LINE %d] From pin (index=%d) of direct connection '%s' does not exist on any side of the programmable block '%s'.\n", directs[i].line, opin, directs[i].from_pin.c_str());
}
}

/* Get the pin index in the rr_graph */
t_physical_tile_loc from_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer);
t_physical_tile_loc to_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer);
/* directs[i].sub_tile_offset is added to from_capacity(z) to get the target_capacity */
int to_subtile_cap = z + directs[i].sub_tile_offset;
/* Iterate over all sub_tiles to get the sub_tile which the target_cap belongs to. */
const t_sub_tile* to_sub_tile = nullptr;
for (const t_sub_tile& sub_tile : to_grid_type->sub_tiles) {
if (sub_tile.capacity.is_in_range(to_subtile_cap)) {
to_sub_tile = &sub_tile;
break;
}
}
VTR_ASSERT(to_sub_tile != nullptr);
if (relative_ipin >= to_sub_tile->num_phy_pins) continue;
// If this block has capacity > 1 then the pins of z position > 0 are offset
// by the number of pins per capacity instance
int ipin = get_physical_pin_from_capacity_location(to_grid_type, relative_ipin, to_subtile_cap);
std::vector<e_side> ipin_grid_side = find_grid_pin_sides(grids, layer, to_grid_coordinate.x() + to_grid_type->pin_width_offset[ipin], to_grid_coordinate.y() + to_grid_type->pin_height_offset[ipin], ipin);
if (1 != ipin_grid_side.size()) {
VPR_FATAL_ERROR(VPR_ERROR_ARCH, "[Arch LINE %d] To pin (index=%d) of direct connection '%s' does not exist on any side of the programmable block '%s'.\n", directs[i].line, relative_ipin, directs[i].to_pin.c_str());
}

/* Find the side of grid pins, the pin location should be unique!
* Pin location is required by searching a node in rr_graph
*/
std::vector<e_side> opin_grid_side = find_grid_pin_sides(grids, layer, from_grid_coordinate.x() + grid_type->pin_width_offset[opin], from_grid_coordinate.y() + grid_type->pin_height_offset[opin], opin);
if (1 != opin_grid_side.size()) {
VPR_FATAL_ERROR(VPR_ERROR_ARCH, "[Arch LINE %d] From pin (index=%d) of direct connection '%s' does not exist on any side of the programmable block '%s'.\n", directs[i].line, opin, directs[i].from_pin.c_str());
}

/* directs[i].sub_tile_offset is added to from_capacity(z) to get the target_capacity */
int to_subtile_cap = z + directs[i].sub_tile_offset;
/* Iterate over all sub_tiles to get the sub_tile which the target_cap belongs to. */
const t_sub_tile* to_sub_tile = nullptr;
for (const t_sub_tile& sub_tile : to_grid_type->sub_tiles) {
if (sub_tile.capacity.is_in_range(to_subtile_cap)) {
to_sub_tile = &sub_tile;
break;
RRNodeId opin_node_id = rr_graph.node_lookup().find_node(layer,
from_grid_coordinate.x() + grid_type->pin_width_offset[opin],
from_grid_coordinate.y() + grid_type->pin_height_offset[opin],
e_rr_type::OPIN, opin, opin_grid_side[0]);
RRNodeId ipin_node_id = rr_graph.node_lookup().find_node(layer,
to_grid_coordinate.x() + to_grid_type->pin_width_offset[ipin],
to_grid_coordinate.y() + to_grid_type->pin_height_offset[ipin],
e_rr_type::IPIN, ipin, ipin_grid_side[0]);

/* add edges to the opin_node */
if (!opin_node_id) {
VTR_ASSERT(opin_node_id);
}
if (!ipin_node_id) {
VTR_ASSERT(opin_node_id);
}
rr_graph_builder.create_edge_in_cache(opin_node_id, ipin_node_id, RRSwitchId(clb_to_clb_directs[i].switch_index), false);
}
VTR_ASSERT(to_sub_tile != nullptr);
if (relative_ipin >= to_sub_tile->num_phy_pins) continue;
// If this block has capacity > 1 then the pins of z position > 0 are offset
// by the number of pins per capacity instance
int ipin = get_physical_pin_from_capacity_location(to_grid_type, relative_ipin, to_subtile_cap);
std::vector<e_side> ipin_grid_side = find_grid_pin_sides(grids, layer, to_grid_coordinate.x() + to_grid_type->pin_width_offset[ipin], to_grid_coordinate.y() + to_grid_type->pin_height_offset[ipin], ipin);
if (1 != ipin_grid_side.size()) {
VPR_FATAL_ERROR(VPR_ERROR_ARCH, "[Arch LINE %d] To pin (index=%d) of direct connection '%s' does not exist on any side of the programmable block '%s'.\n", directs[i].line, relative_ipin, directs[i].to_pin.c_str());
}

RRNodeId opin_node_id = rr_graph.node_lookup().find_node(layer,
from_grid_coordinate.x() + grid_type->pin_width_offset[opin],
from_grid_coordinate.y() + grid_type->pin_height_offset[opin],
e_rr_type::OPIN, opin, opin_grid_side[0]);
RRNodeId ipin_node_id = rr_graph.node_lookup().find_node(layer,
to_grid_coordinate.x() + to_grid_type->pin_width_offset[ipin],
to_grid_coordinate.y() + to_grid_type->pin_height_offset[ipin],
e_rr_type::IPIN, ipin, ipin_grid_side[0]);

/* add edges to the opin_node */
VTR_ASSERT(opin_node_id && ipin_node_id);
rr_graph_builder.create_edge_in_cache(opin_node_id, ipin_node_id, RRSwitchId(clb_to_clb_directs[i].switch_index), false);
}
}
}
Expand Down
Loading