-
Notifications
You must be signed in to change notification settings - Fork 427
Add Parsing Code For Scatter-Gather Patterns #3212
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
Changes from all commits
adfec61
fdbbfae
95abde3
cb36d65
dbd63ce
15b2824
9c6cc76
4fe5dfa
396e848
3426204
b1eb6fc
d018b75
9eb6d02
25d1db6
5d523a8
3c2e09b
1f5457f
e1a777a
351b3a3
44d1078
0be355d
bf244ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2485,7 +2485,7 @@ The full format is documented below. | |
Defined under the ``<switchfuncs>`` XML node, one or more ``<func...>`` entries is used to specify permutation functions that connect different sides of a switch block. | ||
|
||
|
||
.. arch:tag:: <wireconn num_conns="expr" from_type="string, string, string, ..." to_type="string, string, string, ..." from_switchpoint="int, int, int, ..." to_switchpoint="int, int, int, ..." from_order="{fixed | shuffled}" to_order="{fixed | shuffled}" switch_override="string"/> | ||
.. arch:tag:: <wireconn num_conns="expr" from_type="string, string, string, ..." to_type="string, string, string, ..." from_switchpoint="int, int, int, ..." to_switchpoint="int, int, int, ..." from_order="{fixed | shuffled}" to_order="{fixed | shuffled}" switch_override="string" side="string"/> | ||
|
||
:req_param num_conns: | ||
Specifies how many connections should be created between the from_type/from_switchpoint set and the to_type/to_switchpoint set. | ||
|
@@ -2592,6 +2592,10 @@ The full format is documented below. | |
By using a zero-delay and zero-resistance switch one can also create T and L shaped wire segments. | ||
|
||
**Default:** If no override is specified, the usual wire_switch that drives the ``to`` wire will be used. | ||
|
||
:opt_param side: | ||
Specifies the sides that connections are gathered from or scattered to. Valid sides are right, left, top and bottom and are represented by characters 'r', 'l', 't' and 'b'. | ||
For example, to select connections from right, left and bottom set this attribute to "rlb". Note that this attribute is used only for :ref:`scatter_gather_patterns` and does not do anything for other usages of this tag. | ||
|
||
.. arch:tag:: <from type="string" switchpoint="int, int, int, ..."/> | ||
|
||
|
@@ -2634,6 +2638,110 @@ The full format is documented below. | |
The 'to' set is all L4 switchpoint 0's. | ||
Note that since different switchpoints are selected from different segment types it is not possible to specify this without using ``<from>`` sub-tags. | ||
|
||
.. _scatter_gather_patterns: | ||
|
||
Scatter-Gather Patterns | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recall during the weekly VTR meetings, we discussed putting this in the layout tag. I believe that this tag should be a part of the layout in some way, since the location of these SG patterns do depend on the layout of the device (see my comment on sg_location). This should be updated in the text somewhere in here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the way custom switchblocks work. It's also a bit easier to implement since it leaves the layout parsing code untouched. |
||
--------------------- | ||
|
||
The content under the ``<scatter_gather_list>`` tag consists of one or more ``<sg_pattern>`` tags that are used to specify a scatter-gather pattern. | ||
Scatter-gather patterns can be used to specify multi-level switch patterns, rather than the direct wire-to-wire switch patterns of conventional switch blocks. | ||
These additional switches, wires and/or muxes will be added to the architecture, augmenting wires created using segment specifications and swiches created using switch box specifications. | ||
The number of any additional wires or muxes created by scatter-gather specifications will not vary with routing channel width. | ||
|
||
.. figure:: scatter_gather_images/scattergather_diagram.svg | ||
|
||
Overview of how scatter-gather patterns work. First, connections from a switchblock location are selected according to the specification. | ||
These selected connection are then muxed and passed through the scatter-gather node, which is typically a wire segment. The scatter-gather node then fans out or scatters in another switchblock location. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Give high-level context on why we need this (specify multi-level switch patterns, rather than the direct wire-to-wire switch patterns of conventional switch blocks). These additional switches, wires and/or muxes will be added to the architecture, augmenting wires created using segment specifications and swiches created using switch box specifications. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. High-level spec needs a picture showing the gather pattern, the scatter-gather node, and the scatter pattern. Link that to the high-level description. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a figure giving a visual overview of scatter-gather patterns. |
||
.. note:: Scatter-Gather patterns are work in progress and experimental. Currently, VPR does not support this specification and using this tag would not result in any modifications to the RR-Graph. | ||
|
||
When instantiated, a scatter-gather pattern gathers connections from a switchblock and passes the connection through a multiplexer and the scatter-gather node which is typically a wire segment, then scatters or fans out somewhere else in the device. These patterns can be used to define 3D switchblocks. An example is shown below: | ||
|
||
.. code-block:: xml | ||
AmirhosseinPoolad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<scatter_gather_list> | ||
<sg_pattern name="name" type="unidir"> | ||
<gather> | ||
<!-- Gather 30 connections from the 0, 4, 8 or 12 position of L16 wires of all four sides of a switchblock location --> | ||
<wireconn num_conns="30" from_type="L16" from_switchpoint="0,12,8,4" side="rltb"/> | ||
<gather/> | ||
|
||
<scatter> | ||
<!-- Scatter 30 connections to the starting position of L16 wires of all four sides of a switchblock location --> | ||
<wireconn num_conns="30" to_type="L16" to_switchpoint="0" side="rtlb"/> | ||
<scatter/> | ||
|
||
<sg_link_list> | ||
<!-- Link going up one layer, using the '3D_SB_MUX' multiplexer to gather connections from the bottom layer and using the 'TSV' node/wire to move up one layer --> | ||
<sg_link name="L_UP" z_offset="1" x_offset="0" y_offset="0" mux="3D_SB_MUX" seg_type="TSV"/> | ||
<!-- Same as above but moving one layer down --> | ||
<sg_link name="L_DOWN" z_offset="-1" mux="3D_SB_MUX" seg_type="TSV"/> | ||
<sg_link_list/> | ||
|
||
<!-- Instantiate 10 'L_UP' sg_links per switchblock location everywhere on the device --> | ||
<sg_location type="EVERYWHERE" num="10" sg_link="L_UP"/> | ||
<!-- Instantiate 10 'L_DOWN' sg_links per switchblock location everywhere on the device --> | ||
<sg_location type="EVERYWHERE" num="10" sg_link="L_DOWN"/> | ||
<sg_pattern/> | ||
|
||
<sg_pattern name="interposer_conn_sg" type="bidir"> | ||
... <!-- Another scatter-gather pattern specification --> | ||
<sg_pattern/> | ||
<scatter_gather_list/> | ||
|
||
.. arch:tag:: <sg_pattern name="string" type={unidir|bidir}> | ||
|
||
:req_param name: A unique alphanumeric string | ||
:req_param type: ``unidir`` or ``bidir``. | ||
AmirhosseinPoolad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
'unidir': Added connections are unidirectional; all the gather connections are combined in a mux that then drives the scatter-gather node which in turn drives the wires specified in the scatter specification. | ||
|
||
'bidir': The gather and scatter connections are mirrored; the same scatter pattern is implemented at each end of the scatter-gather pattern. This implies the two muxes driving the scatter-gather node can have their outputs tri-stated. | ||
|
||
.. arch:tag:: <gather> | ||
|
||
Contains a <wireconn> tag specifying how the fan-in or gather connections are selected. See ``wireconn`` for the relevant specification. | ||
This <wireconn> tag supports an additioinal 'side' attribute which selects the sides that connections are gathered from (e.g. any of the top, bottom, left and right). | ||
|
||
.. arch:tag:: <scatter> | ||
|
||
Contains a <wireconn> tag specifying how the fan-out or scatter connections are selected. See ``wireconn`` for the relevant specification. | ||
This <wireconn> tag supports an additioinal 'side' attribute which selects the sides that connections are scattered to (e.g. any of the top, bottom, left and right). | ||
|
||
.. arch:tag:: <sg_link_list> | ||
|
||
Contains one or more <sg_link> tags specifying how the pattern of how connections move from the gather location to the scatter location i.e. defines the used mux and scatter-gather node. | ||
|
||
.. note:: <sg_link> tags are not instantiations of the pattern and would not result in any changes to the device. instead, the <sg_location> tag instantiates the pattern and selects one of the <sg_link> tags to be used for the instantiation. | ||
|
||
.. arch:tag:: <sg_link name="string" x_offset="int" y_offset="int" z_offset="int" mux="string" seg_type="string"> | ||
|
||
:req_param name: A unique alphanumeric string | ||
:req_param mux: Name of the multiplexer used to gather connections | ||
:req_param seg_type: Name of the segment/wire used to move through the device to the scatter location (i.e. the type of the scatter-gather node) | ||
|
||
:opt_param x_offset: Offset of the scatter relative to the gather in the x-axis | ||
:opt_param y_offset: Offset of the scatter relative to the gather in the y-axis | ||
:opt_param z_offset: Offset of the scatter relative to the gather in the z-axis | ||
|
||
.. note:: One and only one of the offset fields for the sg_link tag should be set. The magnitude of the offset will generally be chosen by the architecture file creator to match the length of the sg_link segment type. | ||
|
||
.. arch:tag:: <sg_location type="string" num="int" sg_link_name="string"> | ||
|
||
Instantiates the scatter-gather pattern with the specified sg_link. | ||
|
||
:req_param num: number of scatter-gather instances per matching location (e.g. per switch block) | ||
:req_param sg_link_name: name of the sg_link used in the instantiation | ||
:req_param type: Can be one of the following strings: | ||
|
||
* ``EVERYWHERE`` – at each switch block of the FPGA | ||
* ``PERIMETER`` – at each perimeter switch block (x-directed and/or y-directed channel segments may terminate here) | ||
* ``CORNER`` – only at the corner switch blocks (both x and y-directed channels terminate here) | ||
* ``FRINGE`` – same as PERIMETER but excludes corners | ||
* ``CORE`` – everywhere but the perimeter | ||
Sets the location on the FPGA where the connections described by this scatter-gather pattern be instantiated. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a little confused how this corresponds with "location". For me, location would imply an (x, y) tile position of some sort, not general groups. Is there any way that we can allow this specification to allow the user to only allow a particular SG pattern to exist at a specified tile coordinate? Perhaps it can be similar to the fixed-layout specification of tiles. It seems a little rigid to assign locations this way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses the same syntax as the custom switch blocks. There is an XY_SPECIFIED option which offers more control but since it's not documented (yet) I didn't want to add it here. |
||
|
||
|
||
.. _arch_metadata: | ||
|
||
Architecture metadata | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,19 +34,6 @@ using vtr::t_formula_data; | |
/**** Function Declarations ****/ | ||
/*---- Functions for Parsing Switchblocks from Architecture ----*/ | ||
|
||
/** | ||
* @brief Parses a wireconn node and returns a `t_wireconn_inf` structure. | ||
* | ||
* Determines whether the wireconn is in inline or multi-node format and dispatches | ||
* to the appropriate parsing subroutine. | ||
* | ||
* @param node XML node representing the wireconn. | ||
* @param loc_data Location data for error reporting. | ||
* @param switches List of architecture switch definitions (used for switch overrides). | ||
* @return A `t_wireconn_inf` structure populated with parsed data. | ||
*/ | ||
static t_wireconn_inf parse_wireconn(pugi::xml_node node, const pugiutil::loc_data& loc_data, const std::vector<t_arch_switch_inf>& switches); | ||
|
||
//Process the desired order of a wireconn | ||
static void parse_switchpoint_order(const char* order, SwitchPointOrder& switchpoint_order); | ||
|
||
|
@@ -60,11 +47,15 @@ static void parse_switchpoint_order(const char* order, SwitchPointOrder& switchp | |
* @param node XML node containing inline wireconn attributes. | ||
* @param loc_data Location data for error reporting. | ||
* @param switches List of architecture switch definitions (used for switch overrides). | ||
* @param can_skip_from_or_to Determines if the from or to attributes are optional or mandatory. | ||
* <wireconn> tags for switch blocks require both from and to attributes while those for | ||
* scatter-gather use one or the other. | ||
* @return A `t_wireconn_inf` structure populated with parsed data. | ||
*/ | ||
static t_wireconn_inf parse_wireconn_inline(pugi::xml_node node, | ||
const pugiutil::loc_data& loc_data, | ||
const std::vector<t_arch_switch_inf>& switches); | ||
const std::vector<t_arch_switch_inf>& switches, | ||
bool can_skip_from_or_to); | ||
|
||
/** | ||
* @brief Parses a multi-node `<wireconn>` definition with `<from>` and `<to>` children. | ||
|
@@ -137,49 +128,77 @@ void read_sb_wireconns(const std::vector<t_arch_switch_inf>& switches, | |
} | ||
} | ||
|
||
static t_wireconn_inf parse_wireconn(pugi::xml_node node, | ||
const pugiutil::loc_data& loc_data, | ||
const std::vector<t_arch_switch_inf>& switches) { | ||
t_wireconn_inf parse_wireconn(pugi::xml_node node, | ||
const pugiutil::loc_data& loc_data, | ||
const std::vector<t_arch_switch_inf>& switches, | ||
bool can_skip_from_or_to) { | ||
|
||
size_t num_children = count_children(node, "from", loc_data, ReqOpt::OPTIONAL); | ||
num_children += count_children(node, "to", loc_data, ReqOpt::OPTIONAL); | ||
|
||
t_wireconn_inf wireconn; | ||
if (num_children == 0) { | ||
return parse_wireconn_inline(node, loc_data, switches); | ||
wireconn = parse_wireconn_inline(node, loc_data, switches, can_skip_from_or_to); | ||
} else { | ||
VTR_ASSERT(num_children > 0); | ||
return parse_wireconn_multinode(node, loc_data, switches); | ||
wireconn = parse_wireconn_multinode(node, loc_data, switches); | ||
} | ||
|
||
// Parse the optional "side" field of the <wireconn> tag | ||
std::string sides_string = get_attribute(node, "side", loc_data, pugiutil::OPTIONAL).as_string(); | ||
|
||
if (sides_string.find_first_not_of("rtlbRTLB") != std::string::npos) { | ||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Unknown side specified: %s\n", sides_string.c_str()); | ||
} | ||
for (char side_char : sides_string) { | ||
wireconn.sides.insert(CHAR_SIDE_MAP.at(side_char)); | ||
} | ||
|
||
return wireconn; | ||
} | ||
|
||
static t_wireconn_inf parse_wireconn_inline(pugi::xml_node node, | ||
const pugiutil::loc_data& loc_data, | ||
const std::vector<t_arch_switch_inf>& switches) { | ||
const std::vector<t_arch_switch_inf>& switches, | ||
bool can_skip_from_or_to) { | ||
|
||
// Parse an inline wireconn definition, using attributes | ||
expect_only_attributes(node, {"num_conns", "from_type", "to_type", "from_switchpoint", "to_switchpoint", "from_order", "to_order", "switch_override"}, loc_data); | ||
expect_only_attributes(node, | ||
{"num_conns", "from_type", "to_type", "from_switchpoint", | ||
"to_switchpoint", "from_order", "to_order", "switch_override", "side"}, | ||
loc_data); | ||
|
||
t_wireconn_inf wc; | ||
|
||
ReqOpt from_to_required = can_skip_from_or_to ? ReqOpt::OPTIONAL : ReqOpt::REQUIRED; | ||
|
||
// get the connection style | ||
const char* char_prop = get_attribute(node, "num_conns", loc_data).value(); | ||
parse_num_conns(char_prop, wc); | ||
|
||
// get from type | ||
char_prop = get_attribute(node, "from_type", loc_data).value(); | ||
parse_comma_separated_wire_types(char_prop, wc.from_switchpoint_set); | ||
char_prop = get_attribute(node, "from_type", loc_data, from_to_required).value(); | ||
if (!can_skip_from_or_to) { | ||
parse_comma_separated_wire_types(char_prop, wc.from_switchpoint_set); | ||
} | ||
|
||
// get to type | ||
char_prop = get_attribute(node, "to_type", loc_data).value(); | ||
parse_comma_separated_wire_types(char_prop, wc.to_switchpoint_set); | ||
char_prop = get_attribute(node, "to_type", loc_data, from_to_required).value(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if someone did not specify either a from_type or a to_type? Is that acceptable? If not, we should throw an error here during parsing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the way I did this is the can_skip_from_or_to argument. By default it is set to false and from/to_type are required attributes, this is used by custom switch blocks and therefore the behavior for those aren't changed at all. For scatter-gather specifications, the error checking is done outside of this function and in process_sg_tag. Added checking for completely empty fields in process_sg_tag. |
||
if (!can_skip_from_or_to) { | ||
parse_comma_separated_wire_types(char_prop, wc.to_switchpoint_set); | ||
} | ||
|
||
// get the source wire point | ||
char_prop = get_attribute(node, "from_switchpoint", loc_data).value(); | ||
parse_comma_separated_wire_points(char_prop, wc.from_switchpoint_set); | ||
char_prop = get_attribute(node, "from_switchpoint", loc_data, from_to_required).value(); | ||
if (!can_skip_from_or_to) { | ||
parse_comma_separated_wire_points(char_prop, wc.from_switchpoint_set); | ||
} | ||
|
||
// get the destination wire point | ||
char_prop = get_attribute(node, "to_switchpoint", loc_data).value(); | ||
parse_comma_separated_wire_points(char_prop, wc.to_switchpoint_set); | ||
char_prop = get_attribute(node, "to_switchpoint", loc_data, from_to_required).value(); | ||
if (!can_skip_from_or_to) { | ||
parse_comma_separated_wire_points(char_prop, wc.to_switchpoint_set); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More error checking is needed. For example, for scatter-gather patterns, a combination of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This checking is done in the "process_sg_tag" function. I don't think it makes sense for the wireconn tag to legalize itself based on what's around it, the parent tag should be the one doing the checking. |
||
|
||
char_prop = get_attribute(node, "from_order", loc_data, ReqOpt::OPTIONAL).value(); | ||
parse_switchpoint_order(char_prop, wc.from_switchpoint_order); | ||
|
@@ -315,66 +334,31 @@ static void parse_num_conns(std::string num_conns, t_wireconn_inf& wireconn) { | |
|
||
//set sides for a specific conn for custom switch block pattern | ||
static void set_switch_func_type(SBSideConnection& conn, const char* func_type) { | ||
if (0 == strcmp(func_type, "lt")) { | ||
conn.set_sides(LEFT, TOP); | ||
} else if (0 == strcmp(func_type, "lr")) { | ||
conn.set_sides(LEFT, RIGHT); | ||
} else if (0 == strcmp(func_type, "lb")) { | ||
conn.set_sides(LEFT, BOTTOM); | ||
} else if (0 == strcmp(func_type, "la")) { | ||
conn.set_sides(LEFT, ABOVE); | ||
} else if (0 == strcmp(func_type, "lu")) { | ||
conn.set_sides(LEFT, UNDER); | ||
} else if (0 == strcmp(func_type, "tl")) { | ||
conn.set_sides(TOP, LEFT); | ||
} else if (0 == strcmp(func_type, "tb")) { | ||
conn.set_sides(TOP, BOTTOM); | ||
} else if (0 == strcmp(func_type, "tr")) { | ||
conn.set_sides(TOP, RIGHT); | ||
} else if (0 == strcmp(func_type, "ta")) { | ||
conn.set_sides(TOP, ABOVE); | ||
} else if (0 == strcmp(func_type, "tu")) { | ||
conn.set_sides(TOP, UNDER); | ||
} else if (0 == strcmp(func_type, "rt")) { | ||
conn.set_sides(RIGHT, TOP); | ||
} else if (0 == strcmp(func_type, "rl")) { | ||
conn.set_sides(RIGHT, LEFT); | ||
} else if (0 == strcmp(func_type, "rb")) { | ||
conn.set_sides(RIGHT, BOTTOM); | ||
} else if (0 == strcmp(func_type, "ra")) { | ||
conn.set_sides(RIGHT, ABOVE); | ||
} else if (0 == strcmp(func_type, "ru")) { | ||
conn.set_sides(RIGHT, UNDER); | ||
} else if (0 == strcmp(func_type, "bl")) { | ||
conn.set_sides(BOTTOM, LEFT); | ||
} else if (0 == strcmp(func_type, "bt")) { | ||
conn.set_sides(BOTTOM, TOP); | ||
} else if (0 == strcmp(func_type, "br")) { | ||
conn.set_sides(BOTTOM, RIGHT); | ||
} else if (0 == strcmp(func_type, "ba")) { | ||
conn.set_sides(BOTTOM, ABOVE); | ||
} else if (0 == strcmp(func_type, "bu")) { | ||
conn.set_sides(BOTTOM, UNDER); | ||
} else if (0 == strcmp(func_type, "al")) { | ||
conn.set_sides(ABOVE, LEFT); | ||
} else if (0 == strcmp(func_type, "at")) { | ||
conn.set_sides(ABOVE, TOP); | ||
} else if (0 == strcmp(func_type, "ar")) { | ||
conn.set_sides(ABOVE, RIGHT); | ||
} else if (0 == strcmp(func_type, "ab")) { | ||
conn.set_sides(ABOVE, BOTTOM); | ||
} else if (0 == strcmp(func_type, "ul")) { | ||
conn.set_sides(UNDER, LEFT); | ||
} else if (0 == strcmp(func_type, "ut")) { | ||
conn.set_sides(UNDER, TOP); | ||
} else if (0 == strcmp(func_type, "ur")) { | ||
conn.set_sides(UNDER, RIGHT); | ||
} else if (0 == strcmp(func_type, "ub")) { | ||
conn.set_sides(UNDER, BOTTOM); | ||
} else { | ||
/* unknown permutation function */ | ||
archfpga_throw(__FILE__, __LINE__, "Unknown permutation function specified: %s\n", func_type); | ||
|
||
if (std::string(func_type).length() != 2) { | ||
archfpga_throw(__FILE__, __LINE__, "Custom switchblock func type must be 2 characters long: %s\n", func_type); | ||
} | ||
|
||
// Only valid sides are right, top, left, bottom, above and under | ||
if (std::string(func_type).find_first_not_of("rtlbauRTLBAU") != std::string::npos) { | ||
AmirhosseinPoolad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
archfpga_throw(__FILE__, __LINE__, "Unknown direction specified: %s\n", func_type); | ||
} | ||
|
||
e_side from_side = CHAR_SIDE_MAP.at(func_type[0]); | ||
e_side to_side = CHAR_SIDE_MAP.at(func_type[1]); | ||
|
||
// Can't go from side to same side | ||
if (to_side == from_side) { | ||
archfpga_throw(__FILE__, __LINE__, "Unknown permutation function specified, cannot go from side to same side: %s\n", func_type); | ||
} | ||
|
||
// We don't allow specification of patterns that imply edges going over 2 or more layers | ||
// (this doesn't seem electrically logical), so we disallow going from above/under to above/under. | ||
if ((to_side == ABOVE || to_side == UNDER) && (from_side == ABOVE || from_side == UNDER)) { | ||
archfpga_throw(__FILE__, __LINE__, "Unknown permutation function specified, cannot go from above/under to above/under: %s\n", func_type); | ||
} | ||
|
||
conn.set_sides(from_side, to_side); | ||
} | ||
|
||
void read_sb_switchfuncs(pugi::xml_node node, t_switchblock_inf& sb, const pugiutil::loc_data& loc_data) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.