Skip to content
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

Override edge attributes in RR graph #2930

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

soheilshahrouz
Copy link
Contributor

@soheilshahrouz soheilshahrouz commented Mar 12, 2025

Adds a new command line option to override attributes of specific edges in the RR graph.

Description

The user can pass a text file using --read_rr_edge_override, which modifies delay and electrical characteristics of selected edges. The expected file format:

# edge    Tdel      R       Cin       Cout    Cinternal
64812     5.9e-11   450    6.0e-16   4.82e-15   3.82e-15
9981      4.2e-11   450    6.0e-16   4.82e-15   3.82e-15
1234      7.1e-11   
4321      9.4e-11   658
(42, 64)  7.3e-11

The first line must start with # and is always ignored by the parser.
Each following line must include an RR edge ID and delay (Tdel). Other attributes (resistance, capacitances) are optional. Edges can also be specified using source/sink node pairs.

How Has This Been Tested?

By manual inspection of generated RR graph files to see if the edge attributes are overridden correctly.

Types of changes

  • Bug fix (change which fixes an issue)
  • New feature (change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed

@github-actions github-actions bot added VPR VPR FPGA Placement & Routing Tool libarchfpga Library for handling FPGA Architecture descriptions lang-cpp C/C++ code labels Mar 12, 2025
@soheilshahrouz soheilshahrouz requested a review from amin1377 March 12, 2025 20:27
@soheilshahrouz
Copy link
Contributor Author

@petergrossmann21 I'd apprecite any feedback on this

Copy link
Contributor

@amin1377 amin1377 left a comment

Choose a reason for hiding this comment

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

Overall, this looks good to me. I’ve added several small comments, mainly related to adding comments. :)

One more thing—I think it would be good to add a test for this PR. As we discussed, testing this requires additional functionality beyond what the current infrastructure provides. I suggest creating an issue to track it, and we can implement it in a separate PR.

@@ -264,6 +264,10 @@ class RRGraphBuilder {
node_storage_.alloc_and_load_edges(rr_edges_to_create);
}

inline void override_edge_switch(RREdgeId edge_id, RRSwitchId switch_id) {
Copy link
Contributor

Choose a reason for hiding this comment

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

To maintain consistency with other functions, I think it would be better to add a Doxygen comment. (just add a brief description)

@@ -731,6 +730,8 @@ class t_rr_graph_storage {
*/
void partition_edges(const vtr::vector<RRSwitchId, t_rr_switch_inf>& rr_switches);

void override_edge_switch(RREdgeId edge_id, RRSwitchId switch_id);
Copy link
Contributor

Choose a reason for hiding this comment

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

DoxyGen comment (@brief only) similar to other functions

@@ -413,6 +413,10 @@ class RRGraphView {
return node_storage_.edge_switch(id, iedge);
}

inline short edge_switch(RREdgeId id) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

DoxyGen

}
}

if (!edge_id.is_valid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

To make it more concise, I suggest using VTR_LOGV_ERROR(expr, ...).

bool read_edge_metadata,
bool do_check_rr_graph,
bool echo_enabled,
const char* echo_file_name,
bool is_flat);

void load_rr_edge_overrides(std::string_view filename,
Copy link
Contributor

Choose a reason for hiding this comment

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

Doxygen comment

RRGraphBuilder& rr_graph_builder,
const RRGraphView& rr_graph) {
std::ifstream file(filename.data());
if (!file) {
Copy link
Contributor

Choose a reason for hiding this comment

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

VTR_LOGV_ERROR

return edge_id;
}

struct t_rr_switch_inf_hash {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think a better way to implement a hash function for t_rr_switch_inf is to define the hash function within the struct itself. This ensures that if someone adds a new field to the data structure, they can update the hash function directly in the same place.

Additionally, you can add the following specialization in the same file:

namespace std {
    template <>
    struct hash<t_rr_switch_inf> {
        size_t operator()(const t_rr_switch_inf& s) const {
            return s.hash();
        }
    };
}

An added benefit of this approach is that you wouldn't need to explicitly pass the hash function when using data structures like std::unordered_map or std::unordered_set, as it will be automatically recognized.

}
};

struct t_rr_switch_inf_equal {
Copy link
Contributor

Choose a reason for hiding this comment

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

To ensure that any additional fields added to this data structure are properly handled, I suggest overloading the operator within the struct itself.

VTR_LOG_ERROR("Failed to open the RR edge override file: %s\n", filename.data());
}

std::unordered_map<t_rr_switch_inf, RRSwitchId, t_rr_switch_inf_hash, t_rr_switch_inf_equal> unique_switch_info;
Copy link
Contributor

Choose a reason for hiding this comment

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

I also needed to do something similar when creating edges while collapsing RR Node chains in the flat router. If you could create an issue to remind me to implement a function for this, that would be helpful.

RRSwitchId new_switch_id;
auto it = unique_switch_info.find(switch_override_info);
if (it == unique_switch_info.end()) {
new_switch_id = rr_graph_builder.add_rr_switch(switch_override_info);
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that I think about it, I'm pretty sure I wrote a function that retrieves switch info, searches the RR Node switches for one with similar characteristics, and, if none is found, creates a new one and returns the corresponding RR switch ID (I think the name was something like get_or_create_rr_switch_id). In the issue I mentioned, if you could reference this function, I'll update this part of the code myself.

@petergrossmann21
Copy link
Contributor

@soheilshahrouz Some initial thoughts:

  • I think it might be beneficial to separate RC parameters from Tdel parameters. While it's nice to lay out all the parameters together, I don't think per-edge RC annotation makes sense. R and C are node properties rather than edge properties (see https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#routing-resource-graph-file-format-xml) . So even if both parameter types are specified in a single file, I would recommend capturing them on separate lines of text, each with their own syntax. Separate files would also be fine, if slightly less elegant (see aside below)
  • The (src_node #, sink_node #) notation is definitely useful, as it addresses scenarios where the generator of the file does not have integer IDs for edges available during delay annotation (this happens to be the case for me, but is more generically useful because the node IDs can be reliably associated with the nets in the FPGA circuit that are used in delay capture, whereas I think edge ID associations are less reliable).
  • A quick sanity check on file format size: in the general case, there will be some number of bytes per edge to specify the edge ID and delay parameters. I currently develop relatively small, routing-rich architectures and am typically working with node counts at O(10K) and edge counts at O(100K). If 6 digits of precision are supplied per delay parameter, plus five characters to format in scientific notation ('.', 'e-XX'), then each delay parameter formatted in (src_node #, sink_node #) notation uses up to 27 bytes (5 for each node ID, 5 more to format them and space them apart from the delay, and then 11 for the delay constant and one for the carriage return). For a 500K-edge graph that works out to 13.5 MB -- not small, but not unworkable. This supports an architecture with O(1K) LUTs. for something with O(100K) LUTs, we would expect the file to be 100X larger, i.e. O(1GB).

I think the file size analysis above is sufficient to motivate implementing some method of shorthand in the file format so that the architect can express when multiple edges have the same delay parameter. My first instinct is that some kind of relationship between the physical tile definitions and the graph edges can help with this. Currently, the way that I do that is by relating switches in the <switchlist> section of the architecture XML to different physical tile types in my XML generator.

An aside: Note that in automatic place and route flows for ASICs, the R and C values are either not generated at all (because the wire delay is lumped with the "intrinsic" delay of the logic gates and so the VPR model will set all the RC values to 0) OR the intrinsic delay is captured independently of the RC values and thus it's convenient to annotate them separately anyway.

@petergrossmann21
Copy link
Contributor

@soheilshahrouz one additional thought: if the delay annotation is to be performed as a standalone step, having a dedicated executable utility to run it will be helpful--this will avoid having to call vpr for this step and having to pass in an arbitrary blif file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang-cpp C/C++ code libarchfpga Library for handling FPGA Architecture descriptions VPR VPR FPGA Placement & Routing Tool
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants