4. Your names

Teach the tool which nets are rails, and what a legal name looks like on your boards.

Everything so far used only the shipped rules, which know nothing about your team. This rung is the first of four that change that, and it is deliberately first because it changes what the later ones can see at all.

From here on the ladder runs inside a project, examples/jetson-project, which wraps a team’s configuration around the same Jetson board. Copy the board into it once, after make samples-oracle at the repo root:

cd examples/jetson-project
make board

Run the commands on this page and the next ones from that folder. The board is copied into designs/jetson/, beside the project’s own design.yaml.

The vocabulary is the conventions: section of project.yaml, and it carries two halves that are wired very differently.

The problem

This project already carries the conventions: section. To show what goes wrong without it, the runs below first delete it from their scratch copy of the project. Ask the board how many of its nets are power rails:

$ sed -i.orig '/^conventions:/,/^$/d' project.yaml
$ agni query designs/jetson 'net.rail(?n) => count(?n)'
count(n)  provenance
43        designs/jetson/jetson-agx-thor-baseboard.kicad_sch

1 result(s)

The built-in vocabulary finds a lot. Most boards name their rails VCC, VDD, +3V3 or 5V_OUT, the vocabulary is anchored on those names, and most of this board’s rails are named that way. Now look at the nets whose names mention a voltage or a supply but which it did not call a rail:

$ sed -i.orig '/^conventions:/,/^$/d' project.yaml
$ agni query designs/jetson 'net.pin_count(?n, ?c), not net.rail(?n) => ?n'
n                                           provenance
/CSI/CSIA_3V3                               designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/CSI/CSIA_5V                                designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/CSI/CSIB_3V3                               designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/CSI/CSIB_5V                                designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/CSI/VSYNC_CAM0_3V3                         designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/Peripherals/FAN_VDD                        designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/Recovery USB/USBC2_VBUS                    designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/SoM_IO/I2C0_SCL_1V8                        designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/SoM_IO/I2C0_SDA_1V8                        designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/USB DP Alt mode/USBC1_VBUS                 designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/USB Debug, PD/PDC_LDO_3V3                  designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/USB Debug, PD/USBC0_VBUS                   designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/USB Hub/USBC3_VBUS                         designs/jetson/jetson-agx-thor-baseboard.kicad_sch
/USB Hub/USBC4_VBUS                         designs/jetson/jetson-agx-thor-baseboard.kicad_sch

Two kinds of net are in that list. I2C0_SCL_1V8 and VSYNC_CAM0_3V3 are signals named for their logic level, and the vocabulary is right to skip them, because the voltage in the name is a fact about the wire’s swing rather than a supply. CSIA_3V3, CSIA_5V, the USBCn_VBUS nets, FAN_VDD and PDC_LDO_3V3 are rails, named for what they feed, and the vocabulary cannot tell them from the signals by spelling alone.

The consequence is quiet. A rule that finds its rails BY NAME simply sees fewer of them. It does not warn you, and it does not fail. A rail with no test point is not reported, because as far as that rule is concerned the net is not a rail.

The two halves

conventions:
  name: jetson
  lexicon:
    net:
      rail:
        patterns:
          - "^CSI[AB]_([0-9]+V[0-9]*|I2C_VCC)$"
          - "^USBC[0-9]_VBUS$"
          - "^FAN_VDD$"
          - "^(PDC_LDO|FTDI)_[0-9]+V[0-9]*$"
  rules:
    - name: active-low-overbar
      severity: warning
      why: "house style writes an active-low signal with KiCad's overbar, ~{NAME}, never a trailing n"
      allow: ["[^n]$", "[a-z]n$"]

lexicon teaches the engine which of your names mean what. It is applied when the design is read, before any rule runs, so it changes the input every rule sees. It is also the half people skip. Each pattern here names a rail by its function rather than matching any trailing voltage, since a pattern as loose as _3V3$ would sweep the logic-level signals in too.

It is grouped by what is being named, because the named thing decides whether a pattern is right. net holds rail, ground and feedback, matched against NET names. pin holds supply, gate, source and drain, matched against a component’s PIN names. A supply pin is called VDD or VIN while a rail net is called 3V3 or +5V, so a pattern that belongs in one group is wrong in the other. There is also a class map, which marks a PART as belonging to a component class.

rules adds checks. They join the catalog namespaced under the config’s name, so this one appears as jetson/active-low-overbar. Most people opening a conventions section expect only this half. This board writes most active-low signals with KiCad’s overbar, ~{CLKREQ_E}, and a few with a trailing n, and the rule asks for one spelling.

Both halves, visible

Both runs leave out rung 6’s parameter corpus. A datasheet that names a part’s supply pins also tells the engine which nets are rails, and here we want to see what the conventions alone change. Without the section:

$ sed -i.orig '/^conventions:/,/^$/d' project.yaml
$ sed -i.orig '/^params:/d' project.yaml
$ agni check designs/jetson
findings by rule:
  bulk-cap               1
  esd-protection         10
  floating-input         3
  i2c-pull-up            30
  i2c-redundant-pull-up  4
  rail-not-classified    1
  reverse-blocking-absent 1
  single-pin-net         2
  test-point-coverage    27

79 finding(s) total
24227 subject(s) considered by 38 rule(s), 615 not considered (--verdicts for the detail)

With it:

$ sed -i.orig '/^params:/d' project.yaml
$ agni check designs/jetson
findings by rule:
  bulk-cap               1
  esd-protection         10
  floating-input         3
  i2c-pull-up            28
  i2c-redundant-pull-up  4
  jetson/active-low-overbar 2
  reverse-blocking-absent 1
  single-pin-net         2
  test-point-coverage    36

87 finding(s) total
25600 subject(s) considered by 39 rule(s), 615 not considered (--verdicts for the detail)

Several things changed, and they come from both halves.

jetson/active-low-overbar appears, with two findings. That is the rules half:

$ agni check designs/jetson --rule jetson/active-low-overbar
findings by rule:
  jetson/active-low-overbar 2

first 2:
  [warning] jetson/active-low-overbar: /USB Debug, PD/PDC_I2C1_IRQn (net name matches no allowed naming pattern)
  [warning] jetson/active-low-overbar: /USB Debug, PD/PDC_I2C2_IRQn (net name matches no allowed naming pattern)

2 finding(s) total
1355 subject(s) considered by 1 rule(s) (--verdicts for the detail)

The rest is the lexicon half. test-point-coverage found more rails without a test point, because nets that were invisible to it became rails. rail-not-classified is gone too. It is the shipped catalog’s own tripwire for this gap, and it had named FTDI_3V3. That net’s name declares 3.3 V, and it feeds four supply pins, but it carries no rail role. It is already in the count above, since net.rail also counts any net that something drives as a power net. The rules read the rail ROLE, though, and only the lexicon gives the net one. And two i2c-pull-up errors disappeared:

$ agni check --verdicts --rule i2c-pull-up --format csv designs/jetson
verdict_id,url,rule,outcome,subjects,statement,context,terms,reason
i2c-pull-up:(net:/USB Debug%2C PD/FTDI_CBUS0{slash}SDA),,i2c-pull-up,pass,"net:/USB Debug, PD/FTDI_CBUS0{slash}SDA","/USB Debug, PD/FTDI_CBUS0{slash}SDA reaches rail /USB Debug, PD/FTDI_3V3 through R114","pull-up=R114|rail=/USB Debug, PD/FTDI_3V3",,
i2c-pull-up:(net:/USB Debug%2C PD/FTDI_CBUS3{slash}SCL),,i2c-pull-up,pass,"net:/USB Debug, PD/FTDI_CBUS3{slash}SCL","/USB Debug, PD/FTDI_CBUS3{slash}SCL reaches rail /USB Debug, PD/FTDI_3V3 through R113","pull-up=R113|rail=/USB Debug, PD/FTDI_3V3",,

Both lines were pulled up all along, through R113 and R114 to FTDI_3V3. The rule asks for a pull-up to a RAIL, and until the lexicon named that net a rail, the resistors led nowhere the rule recognized. Two of rung 2’s thirty errors were the vocabulary’s blind spot rather than the design’s.

Every tier follows that pattern. Supplying a tier adds its own findings. It can also change what your existing rules are able to see, in both directions.

Reading the lexicon directly

Inferring the lexicon from finding counts that moved is indirect. You can ask the fact base itself, with the same question that opened this rung:

$ agni query designs/jetson 'net.rail(?n) => count(?n)'
count(n)  provenance
57        designs/jetson/jetson-agx-thor-baseboard.kicad_sch

1 result(s)

Nothing was added to the design and no rule ran. The lexicon changed what the engine believes a rail is, and every relation derived from that role now answers differently.

Write a lexicon in a loop. Ask, compare the answer against the board’s known rails, adjust the pattern, and ask again. A query reads only the lexicon half, since it runs no rules.

Writing your own

Start with the lexicon, not the rules. Run agni query <design> 'net.rail(?n) => ?n' --conventions <your file> on a real board and compare the list against the board’s known rails. The file holds the section’s keys (name, lexicon, rules) at its top level. For that run it replaces any conventions declared by the design’s project, so you can try a candidate without editing the project. Whatever is missing tells you the pattern you need. Repeat until the list is right, move it into the project’s conventions: section, and only then write naming rules.

A rule’s allow is a list of patterns, and a net name passes if it matches any of them. Getting this backwards is easy, because allow describes what is legal and the finding is a name matching none of them.

Distinct name spaces need distinct lexicon entries. Rail net names and supply pin names are usually named differently, so net.rail and pin.supply are separate dimensions rather than one shared pattern list.

Next

Your interfaces, which declares a bus once and checks every board against it.