[solved] Custom differencing rule not working [DRC]

I am trying to make a rule to control each differential pair in each different layer, however following the documentation I do not find any result


This is an example to comply with the documentation.

(version 1)
(rule “space between tracks”
(condition “A.inDiffPair(‘USB’) && AB.isCoupledDiffPair()”)
(constraint diff_pair_gap(min 0.35mm))
)

As you can see the tracks belong to a differential pair (USB_P and USB_N), however this rule does not apply.

but if I use the condition (’*’) instead of the name of a differential pair if the condition works but it does not apply the rule of space between differential pair.

and I know it works because instead of restricting the spacing between diff pairs I restrict the spacing between tracks if applicable.

(version 1)
(rule “space between tracks”
(condition “A.inDiffPair(’*’) && AB.isCoupledDiffPair()”)
(constraint diff_pair_gap(min 0.35mm))
#(constraint clearance(min 0.35mm))
)

this does not work

(version 1)
(rule “space between tracks”
(condition “A.inDiffPair(’*’) && AB.isCoupledDiffPair()”)
#(constraint diff_pair_gap(min 0.35mm))
(constraint clearance(min 0.35mm))
)

This works

I have 2 days trying to make this work, I hope you can help me.

I haven’t used custom rules for diff pairs, so I’m just going off of the documentation, but what if you use the opt token instead of min? So that would be:

constraint diff_pair_gap(opt 0.35mm))

This is based on the example in the syntax help:

# Specify an optimal gap for a particular diff-pair
(rule "dp clock gap"
    (constraint diff_pair_gap (opt "0.8mm"))
    (condition "A.inDiffPair('CLK') && AB.isCoupledDiffPair()"))
# Specify a larger clearance around any diff-pair
(rule "dp clearance"
    (constraint clearance (min "1.5mm"))
    (condition "A.inDiffPair('*') && !AB.isCoupledDiffPair()"))

and some documentation on this from the manual:

Many constraints take arguments that specify a physical measurement or quantity. These constraints support minimum, optimal, and maximum value specification (abbreviated “min/opt/max”). The minimum and maximum values are used for design rule checking: if the actual value is less than the minimum or is greater than the maximum value in the constraint, a DRC error is created. The optimal value is only used for some constraints, and informs KiCad of a “best” value to use by default. For example, the optimal diff_pair_gap is used by the router when placing new differential pairs. No errors will be created if the differential pair is later modified such that the gap between the pair is different from the optimal value, as long as the gap is between the minimum and maximum values (if these are specified). In all cases where a min/opt/max value is accepted, any or all of the minimum, optimal, and maximum value can be specified.

Min/opt/max values are specified as (min <value>), (opt <value>), and (max <value>). For example, a track width constraint may be written as (constraint track_width (min 0.5mm) (opt 0.5mm) (max 1.0mm)) or simply (constraint track_width (min 0.5mm)) if only the minimum width is to be constrained.

(version 1)
(rule "space between tracks"
(condition "A.inDiffPair('*') && AB.isCoupledDiffPair()")
(constraint diff_pair_gap(min 0.35mm)(opt 0.4mm)(max 0.5mm))
#(constraint clearance(min 0.35mm))
)

I made this modification and it still does not give me any warning or error

As you can see in the image, an error should occur, but it does not occur.

I’m seeing the same thing as you in my test project.

The router and DRC are following the diff pair gap & width in the netclass settings, but seem to be ignoring the custom rules.

For what it’s worth, I’m not sure what a custom rule here gets you that you can’t accomplish with regular netclass rules, but it’s strange that this doesn’t seem to work, or we both are missing something.

The clearance and constraint inspector windows are useful for debugging a lot of custom rule issues, but I don’t see any useful information in them here.

Perhaps we can summon @jeffyoung and get an adult to help us :slight_smile:

diff_pair_test.zip (7.3 KB)

2 Likes

The diff_pair_gap constraint values determine whether or not the diff pair is coupled. DRC violations are generated when the un-coupled length is greater than some max.

You can probably use a diff_pair_uncoupled constraint of (max 0mm) to flag a violation anytime the diff_pair_gap min/max are violated, but I’ve never tried it.

DRC (in contrast) tests the uncoupled length.

1 Like

Ah, thanks for the explanation. Let me make sure I understand:

  • diff_pair_gap max/min sets the range of gaps that DRC considers to be coupled vs. uncoupled, but doesn’t in and of itself lead to any violations. (Does the opt token do anything in that case? I ask because the syntax help uses opt but not min/max)
  • diff_pair_gap is only meaningful when used in combination with diff_pair_uncoupled. diff_pair_uncoupled can get you DRC errors if your uncoupled length is too long, where uncoupled is defined by diff_pair_gap.

But then I don’t understand why there are two separate DRC severities for “Differential pair gap out of range” and “Differential uncoupled length too long”. Based on my (possibly flawed) understanding above, I think those are the same thing?

I get no DRC errors with this project.
Here are my rules:

(version 1)
(rule "diff pair"
	(constraint diff_pair_gap (min "0.9mm") (opt "1mm") (max "1.1mm"))
	(condition "A.inDiffPair('*') && AB.isCoupledDiffPair()"))
(rule "uncoupled"
	(constraint diff_pair_uncoupled (max "0.1mm"))
	(condition "A.inDiffPair('*') && AB.isCoupledDiffPair()"))

and the board:

Project:
diff_pair_test.zip (7.7 KB)

diff_pair_gap opt is used by the router.

If an uncoupled violation is generated then we’ll also generate a pair-gap-out-of-range so you can see what the range in use was. One could argue that would be better moved to Inspect > Clearance Resolution.

(And even if it didn’t move, one could argue that it doesn’t need its own severity, but then we’ve got to special-case it.)

Looks like the DP processing got smarter so it does its own isCoupledDiffPair() matching (but one side at a time, so actually putting it in the condition messes things up).

I got good results with:

(version 1)
(rule "diff pair"
	(constraint diff_pair_gap (min "4.9mm") (opt "5mm") (max "5.1mm"))
	(condition "A.inDiffPair('/rdiff_')"))
(rule "uncoupled"
	(constraint diff_pair_uncoupled (max "0.1mm"))
	(condition "A.inDiffPair('/rdiff_')"))
2 Likes

Ah, ok, that makes sense. Thanks for the explanation.

With those rules I get the expected results. The custom rules override the netclass settings and DRC gives violations as expected. Nice.

So the difference is that the isCoupledDiffPair needs to be removed. That makes the rules simpler as well :slight_smile:

Another thing I noticed is that the netname matching is quite sensitive. '/rdiff_' as you used works, but '/rdiff' without the underscore doesn’t (although the syntax help says it should work). 'rdiff_' without the leading slash doesn’t work either. I’m not sure if those are bugs or expected behavior. It’d be nice if it was a little more flexible but I guess it’s probably best for the system to be as precise as possible to avoid issues with pathological names (rdiff_P and rdiff__P?). Does the leading slash have to be necessary?

Shall I do a MR to update the syntax help then?

  • remove isCoupledDiffPair() from the diff pair examples
  • update the net naming to include the leading slash and the underscore

I’m actually fixing the engine to be agnostic about a single trailing ‘_’.

/rdiff and rdiff are actually different nets. In a hierarchical schematic you might have /sheet1/rdiff and /sheet2/rdiff (which you probably want to treat the same from a diff pair perspective).

So I think we should encourage people to write *rdiff.

But yes, an MR along those lines would be great. (Note that we do want the !AB.isCoupledDiffPair() part in the second example as clearance constraints are evaluated with both items.)

1 Like

Note: I also think knowing when to include AB.isCoupledDiffPair() and when not to is a tall ask, so I’m changing it to return true for unary rules if A is part of a diff pair.

So this means that including AB.isCoupledDiffPair() in the diff_pair_gap and diff_pair_uncoupled conditions won’t matter either way?

Correct.

But we might as well leave it out of the examples just to make them simpler…

What changes if any are you planning to backport to 6.0? I can update the syntax there as well, but I’m not sure what’s correct…

edit: and a documentation update here: Update diff pair custom rule syntax (!842) · Merge requests · KiCad / KiCad Web Services / kicad-doc · GitLab

I cherry-picked all the changes into 6.0, so its docs will need the same updates.

I’ll leave the documentation cherrypicking to whoever merges the MRs unless someone tells me otherwise.

The doc repo only has a master branch.

I did cherry-pick the syntax help back to 6.0.

Thanks!

Yeah, I missed that you already merged it. Thanks :slight_smile:

But docs does have a 6.0 branch these days. I’m not aware of any differences between 6.0 and master, but it exists and you can pick between them on the docs.kicad.org website.

thank you very much sir!
I just tried it and it works, my goal is to evaluate the impedance matching in each layer, since there is no way to make the tracks automatically change width and space.

(version 1)
(rule "pair diff"
(constraint diff_pair_gap (min "0.1mm") (opt "0.12mm") (max "0.16mm"))
(constraint track_width (min "5mm"))
(condition "A.inDiffPair('/USB_')"))
(rule "uncopled lenght"
(constraint diff_pair_uncoupled (max "0.1mm"))
(condition "A.inDiffPair('/USB_')"))