5. Your interfaces

Declare a bus once, check every board against it, and understand what superseding a built-in profile costs.

A bus like PCIe is the same set of requirements on every board that has one. An interface profile declares that set once: which signals make up the bus, how they are named here, and what has to be true of them. Then any board can be checked against it.

The engine ships profiles for CAN, LIN, eMMC, SD, PCIe, SGMII, SPI-NOR, MDIO and A2B. This rung is about adding your own, and about the two things that surprise people when they do.

What the built-in profiles already find

This run deletes the profiles line from the project’s project.yaml first, so what you see is the built-in tier on its own. The first number counts the profile rules with any verdict at all:

$ sed -i.orig '/^profiles:/d' project.yaml
$ agni check --verdicts designs/jetson
0
87 finding(s) total
25512 subject(s) considered by 40 rule(s), 647 not considered (--verdicts for the detail)

None. The board has four PCIe links, to the two M.2 sockets and the FMC+ header, and the shipped PCIe profile says nothing about any of them. It recognizes a PCIe signal by a suffix such as _PETP or _REFCLKN, and this board draws each link as a KiCad bus whose members are named /M.2/PCIe_{C1x1}.TX0+ and /Expansion connector/PCIe_{C2x1}.RX0-. Nothing on the board looks like PCIe to it, so it reports nothing, and a findings list cannot tell you that apart from four healthy links.

Absence is not a pass

Before adding anything, look at that silence, because it is the more important half. The profile tier gates on presence. Each profile has an anchor signal, and the anchor’s presence means the bus exists at all. A profile is in use only when its anchor signal is on the board. If an interface’s anchor is nowhere, the interface is reported as unevaluated rather than passed, and the review layer in rung 9 keeps that apart from pass.

So a silent profile never raises the question “is the bus fine”. It raises “did the profile recognize the bus”, and on this board the answer is no.

Adding your own

profiles/pcie.yaml in the project declares this team’s PCIe, in this board’s naming:

name: PCIe
signals:
  - {name: TXP,    regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.TX[0-9]\+$', anchor: true}
  - {name: TXN,    regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.TX[0-9]-$'}
  - {name: RXP,    regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.RX[0-9]\+$'}
  - {name: RXN,    regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.RX[0-9]-$'}
  - {name: CLKP,   regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.CLK\+$'}
  - {name: CLKN,   regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.CLK-$'}
  - {name: RST,    regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.~\{RST\}$'}
  - {name: CLKREQ, regex: '(^|/)PCIe_\{C[0-9]x[0-9]\}\.~\{CLKREQ\}$'}
requirements:
  - {type: signal-missing}
  - {type: signal-dangling}
  - {type: esd}

A signal says how a net is recognized as playing that role. A suffix is the readable default, and a regex handles naming that a suffix cannot express, such as a bus member whose role comes after a dot. KiCad puts a sheet path in front of a name drawn on a sub-sheet, and (^|/) lets the member carry that path. anchor: true marks the signal whose presence means the bus exists.

There is no flag. The project names its own profiles/ directory, so agni composes it for every design under designs/, the same way it composes params/ and the conventions: section of project.yaml. Putting the file in place is the whole step:

$ agni check designs/jetson
87 finding(s) total
25616 subject(s) considered by 42 rule(s), 647 not considered (--verdicts for the detail)
note: jetson-profiles supersedes 4 rule(s): profile/pcie-signal-missing, profile/pcie-host-incomplete, profile/pcie-signal-dangling, profile/pcie-esd-missing

The findings did not move, and the considered count did. The four links were complete all along, and now something has looked.

Ask a requirement what it concluded and it tells you, whether or not it found anything:

$ agni check --verdicts --rule jetson-profiles/pcie-signal-missing designs/jetson
jetson-profiles/pcie-signal-missing  56 pass
    pass  /Expansion connector/PCIe_{C2x1}.TX0+ + CLKN    PCIe interface (anchored at net
                                                          /Expansion connector/PCIe_{C2x1}.TX0+)
                                                          carries required signal CLKN
    pass  /Expansion connector/PCIe_{C2x1}.TX0+ + CLKP    PCIe interface (anchored at net
                                                          /Expansion connector/PCIe_{C2x1}.TX0+)
                                                          carries required signal CLKP
    pass  /Expansion connector/PCIe_{C2x1}.TX0+ + CLKREQ  PCIe interface (anchored at net
                                                          /Expansion connector/PCIe_{C2x1}.TX0+)
                                                          carries required signal CLKREQ
    pass  /Expansion connector/PCIe_{C2x1}.TX0+ + RST     PCIe interface (anchored at net
                                                          /Expansion connector/PCIe_{C2x1}.TX0+)
                                                          carries required signal RST
    pass  /Expansion connector/PCIe_{C2x1}.TX0+ + RXN     PCIe interface (anchored at net
                                                          /Expansion connector/PCIe_{C2x1}.TX0+)
    ... and 36 more, not shown here. The tally beside the rule name is the full count.

56 verdicts across 1 rule(s), 56 pass

Each anchor, one per lane, comes back with one answer per required signal, and the list stops at twenty passes per rule and says how many it folded away. Read what a pass claims, though. Presence is matched across the board, so /M.2/PCIe_{C1x1}.TX0+ + CLKN says a PCIe clock exists, not that this link’s clock does. A profile describes one bus, and four links named alike read as one bus four lanes wide.

esd says nothing at all, and that is correct. Every PCIe line here runs from the module socket J1, through its coupling capacitors, to the M.2 and FMC+ connectors, and the project’s design.yaml declares all of those internal, so no PCIe net leaves the product. Rung 7 is about that declaration.

Supersession, and what it costs

The note in that run says jetson-profiles supersedes four rules, profile/pcie-signal-missing, profile/pcie-host-incomplete, profile/pcie-signal-dangling and profile/pcie-esd-missing.

Your profile has the same name as a built-in one, so it replaces it rather than running alongside it. Every rule the built-in PCIe profile contributed is gone, and only what your file declares is checked. Supersession is per interface, not per run, so the shipped CAN and eMMC profiles are untouched.

Picture two profiles that both claim to be PCIe and both fire on the same nets. They would double-report everything, and there would be no way to say your naming differs from the built-in assumption.

But supersession means an omission is silent. Your file lists three requirements, and the built-in declared four. host-incomplete asks whether a component DECLARED as a PCIe host carries every signal, and this board declares no host, so the file leaves it out on purpose. Had it left out esd by accident, ESD on PCIe would simply stop being checked on every board this project reviews, and nothing would say so. The run would look healthier, because there would be fewer findings.

So write the file as the whole declaration rather than the delta, and read the supersession note whenever you add one. It names every rule your file replaced.

What the rung gave you

Compare the last lines of the two runs on this page. The findings are the same and the considered count rose by about a hundred subjects, with two more rules having an opinion about this board. Those are the PCIe signals, and nothing looked at them before this file.

Watch the second number as you add tiers. Findings tell you what is wrong today. The considered count tells you how much of the board anything is looking at, and declaring an interface raises it.

Next

Part limits, where checks start comparing your design against what a part’s datasheet actually allows.