SDC support roadmap

There are several good reasons for Yosys to support various use cases that involve SDC. These use cases can be implemented in various ways. Here are my notes on the topic with some updates on how we’re going about to kick this off. ASIC flows are of particular interest at the moment but FPGA designers should benefit as well, just not from day 1.

Intro to SDC

SDC (Synopsys Design Constraints) are a “standardized” format for constraining timing characteristics of digital logic designs. It’s based on Tcl. The core two kinds of SDC commands are “object access commands” (“getters”) and “timing constraints”. For example create_clock -period 10 [get_ports clk] uses the get_ports getter to create an argument value for the create_clock constraint. ASIC designers generally tend to use SDC files in weirder, more creative ways.

Use cases in Yosys

Smart flattening

The flatten command has large benefits in terms of quality of results in synthesis, but if the constraints for a design refer through the hierarchy, after synthesis, the constraints must still be valid and refer to the same objects. The keep_hierarchy attribute can be added to modules to prevent flattening on all module boundaries above all SDC-referred objects to ensure this. Yosys could do the absolute minimal amount of flattening to keep the constraints applicable by looking at what objects are referred to in the SDC.

For example with these constraints:

current_instance top/submod1
do_constrain [get_pins leaf/A]

And a module hierarchy like this:

top
├── submod1
| └── leaf
└── submod2

submod1 and leaf would get the keep_hierarchy attribute and submod2 wouldn’t. As a result, the hierarchy would be flattened to this:

top
├── submod1
└── leaf

To which the original constraints still apply.

Flattening with rewriting

The SDC could instead be automatically rewritten to reflect changes to the hierarchy. In the previous example, all hierarchy could be flattened, but the constraints would be rewritten to something like this:

do_constrain [get_pins top_submod1_leaf_A]

As an alternative approach, in practice, constraints are usually applied on meaningful boundaries, like IP blocks in a design. This means that the constraints are a good hint for which hierarchy should actually be kept. In this case, keep_hierarchy would be applied to submod1 only. Whether the kept boundary is given by current_instance (keep submod1) vs by the bottom boundary (leaf) in a hierarchical pattern is to be determined. OpenSTA doesn’t support current_instance yet.

current_instance top/submod1
do_constrain [get_pins leaf_A]
top
└── submod1

abc clock domains

Currently, the abc pass takes a -D <picoseconds> delay target for optimization. Instead, create_clock constraints could be interpreted to set synthesis delay goals for each clock domain individually for an abc run.

Constraining module-internal objects with rewriting

Generally, constraints are only applied on the interfaces on modules. Incidentally, there is no risk of Yosys invalidating these by changing their names, for example. But if cells or wires internal to modules were to be referred to (for example with all_registers), things get more complicated, as we might have to retain more structure that we ordinarily do. Path constraint endpoints will probably have to refer to optimization barriers added to the design so that we don’t move logic out of a constrained path when we optimize and synthesize. At the end of this flow, the rewritten SDC would be outputted with possibly significantly different design objects being referred to.

Implementation components

Expanding SDC with OpenSTA

Yosys should avoid committing to quirks of its own or any other tool. For ASIC flows that use OpenROAD, the SDC consumer is OpenSTA, which can be used to expand SDC if the design is first prepared for OpenSTA compatibility. As the SDC is interpreted by OpenSTA, the SDC OpenSTA outputs is simplified with no wildcards or odd uses of hierarchical flags. Ingesting such SDC is easier for Yosys. However, the features that we’re avoiding to implement this way can be implemented for the sake of other flows.

Uninterpreted constraints

SDC with simple usage of getters, such as SDC expanded by OpenSTA, can be read in by Yosys without any interpretation of the actual constraints. By instrumenting the Tcl unknown function, and even redirecting list to it, we can construct a graph (set of trees) of unevaluated SDC/Tcl function calls (the “call graph”), where the leaves are sets of design objects like nets and cells. This means that we’re only interpreting trivial usage of the getters.

Rewriting SDC with uninterpreted constraints

The call graph can be dumped out as valid SDC again, with new getters emitted for the design objects. Before that, it can be (in effect) rewritten with updated locations of the referred objects via flattening or general synthesis. As mentioned above, optimization barriers should help cover more use cases here. A particular feature of this flow is that Yosys won’t be checking if the constraint commands are used correctly or not.

Typed tracked constraints

Particularly for FPGA flows, function stubs could be rewritten in Tcl that “declare” the signatures of SDC functions supported by a vendor or open source tool. By signature, I mean the “types” of object expected by their various arguments. Then, FPGA users could get some meaningful errors directly from Yosys. The call graph would be passed out of the sdc reading machinery in some way and then interpreted with stubbed constraint commands. What we’d be storing as an internal view of the constraints would be just the actual constraint commands with their evaluated arguments, but we wouldn’t be applying the constraints to the design since we’d only know their signatures, rather than try to match their behavior vs some tool outside of Yosys. Adding function stubs for various vendor FPGA tools might be a good match for external contributors with experience with these tools, once the internal scaffolding and examples for writing these are added.

Interpreting constraints

Eventually, we might be able to let Yosys actually interpret constraints commands in various tool compatibility modes.

What now?

The OpenSTA expansion have already been up as drafts (see above). As for the uninterpreted approach, I have a prototype for it including the basic smart flattening and will be bringing it up as a separate PR as it’s not strictly dependent on expansion. I’ve been considering the FPGA flavor interpretation an integral part to be developed within the same pass as the uninterpreted approach, but I think it’s fine consider it a separate subsequent stage.

Results of this work will be likely fairly buggy at first. It’s all possible that some of the use cases will stay unserved and implementation components unimplemented, since this depends on whether prototypes turn out promising, and on the degree of maintainer capacity to handle these features.

Currently, the goal is to get the basic smart flattening productized and to see how that works for real designs.

2 Likes

That looks like great work, thank you!

I suspect it might prove problematic if we commit as a community to only using a proprietary format (Though of course compatibility is important)

Perhaps a start for a community owned format would be an easily parsable representation of the call graph? We could serialise in such a way we have space for adding semantics, and version it.

Overdue update, uninterpreted SDC is up

A proprietary format that is IMHO braindead. How convoluted can you make the definition of this signal is a clock of 50MHz or this output signal will be used after three clock cycles on this input signal ?