10. Interfaces and what they require

A bus is a contract, not two wires. Declaring what it requires once, and a silence at the end that carries the whole course.

The last of the three system chapters. Chapter 8 was how a board is fed, chapter 9 was when and in what configuration, and this one is how it talks to anything else.

Prerequisites: Chapter 8 for the idea of a declaration.

Levels on this page: EE6. It links to what that level means.

A bus is a contract (EE6)

“This board has CAN on it” sounds like a statement about two wires. It is closer to a statement about a specification, and the specification requires things.

CAN needs a differential pair, termination at each end of the bus, a transceiver between the bus and the processor, and protection on anything leaving the board through a connector. Chapter 1 taught all four as jobs a part does. What is new here is that CAN requires them, and requires them together.

A CAN link drawn as the set of parts the interface requires. A processor connects to a transceiver over TXD, RXD and STB. The transceiver drives the differential pair CANH and CANL, which carries a 120 ohm termination across it and an ESD clamp to ground before it leaves the board through a connector. Each feature is annotated with the profile requirement that checks it. A netlist missing any of them is still a valid netlist. ground processor transceiver J1 off the board TXD RXD STB CANH CANL 120 ohm ESD clamp signal-missing, host-incomplete termination, at both ends esd a board with any of these missing is still a perfectly valid netlist, which is why the profile declares them

None of that is visible in a netlist as a requirement. A board with CAN wired wrongly is a perfectly valid netlist. (This is the chapter where the tool stops being able to work anything out for itself, and it has been heading that way since chapter 8.) So, exactly as in the last two chapters, somebody has to declare what the interface is and what it demands.

Declared once, checked everywhere (EE6)

That declaration is a profile, and the useful property is that it is written once and applies to every design carrying the interface:

name: CAN
host: {attr: interface, value: CAN}
signals:
  - {name: CANH, suffix: _CANH, anchor: true}
  - {name: CANL, suffix: _CANL}
  - {name: TXD,  suffix: _TXD}
  - {name: RXD,  suffix: _RXD}
  - {name: STB,  suffix: _STB}
requirements:
  - {type: signal-missing}
  - {type: host-incomplete}
  - {type: termination, params: {high: _CANH, low: _CANL}}
  - {type: signal-dangling}
  - {type: esd}

host is how a part gets recognised as a CAN device. Chapter 1 noted that the tool could not tell what kind of chip U4 was, and this is the answer: the design carries an interface: CAN attribute on it, and the profile says that attribute is what makes something a host.

Run it against the board:

$ agni check --verdicts --rule gateway-profiles/can-host-incomplete --rule gateway-profiles/can-esd-missing examples/tutorial-project/designs/gateway/gateway.edn
gateway-profiles/can-esd-missing  2 fail
    fail  CAN1_CANH  CAN signal net CAN1_CANH is exposed on a connector with no ESD protection
                     in reach
    fail  CAN1_CANL  CAN signal net CAN1_CANL is exposed on a connector with no ESD protection
                     in reach

gateway-profiles/can-host-incomplete  1 fail, 4 pass
    fail  U4 + STB   CAN host U4 declares the interface but is missing required signal STB
    pass  U4 + CANH  CAN host U4 is wired to required signal CANH
    pass  U4 + CANL  CAN host U4 is wired to required signal CANL
    pass  U4 + RXD   CAN host U4 is wired to required signal RXD
    pass  U4 + TXD   CAN host U4 is wired to required signal TXD

7 verdicts across 2 rule(s), 4 pass, 3 fail

U4 declares itself a CAN host and has no _STB net, so host-incomplete fires. Note that STB is a house requirement rather than a CAN one: standby is how this organisation puts a bus to sleep, and the standard has no opinion about it. That is the point of a profile living in your project rather than in the tool.

The ESD finding is the other kind, a genuine requirement of anything with a connector on it. Both CAN nets reach the outside world through J1 with no clamp in reach.

The trap in that file (EE6)

Worth pausing on, because it is the kind of thing that costs a real board.

This project’s profile is named CAN, and so is the built-in one. Sharing the name is what makes the project’s version supersede the built-in rather than run alongside it. That is deliberate and it is what lets a house add its STB requirement.

It also means that anything the built-in checked and this file omits stops being checked, silently. The file’s own comment says so, which is why its signals and requirements lists repeat the built-in set rather than listing only the delta. A profile that listed just the STB addition would have quietly switched off termination, ESD and everything else.

That shape recurs anywhere configuration replaces rather than merges, and the failure is always the same: the run gets quieter and nothing says why.

What a satisfied requirement says (EE6)

Now the thing this chapter is really for.

Look at the profile’s termination requirement, then look at what the board has. Chapter 1 opened the course by reading three resistors off a query, and the first of them was R1, a 120 Ω part bridging CAN1_CANH and CAN1_CANL. That is what the requirement asks for.

$ agni query examples/tutorial-project/designs/gateway/gateway.edn 'component.class(?r, "resistor"), component.mpn(?r, ?m), component-on-net(?r, ?n) => ?r, ?m, ?n'
r   m              n               provenance
R1  ACME-RES-120R  CAN1_CANH       designs/gateway/gateway.edn ; designs/gateway/gateway.edn:CAN1_CANH
R1  ACME-RES-120R  CAN1_CANL       designs/gateway/gateway.edn ; designs/gateway/gateway.edn:CAN1_CANL
$ agni check --verdicts --rule gateway-profiles/can-termination-missing examples/tutorial-project/designs/gateway/gateway.edn
gateway-profiles/can-termination-missing  1 pass
    pass  CAN1_CANH  CAN bus (net CAN1_CANH) is bridged to its _CANL twin by a series passive
1 verdicts across 1 rule(s), 1 pass

R1 is there, the requirement is satisfied, and the rule says so, naming the net and why.

That last part is worth more than it looks, because for most of this tool’s life it did not happen. Profile rules are compiled from datalog queries, and a datalog goal yields the rows that satisfy it. unterminated(?h) produces unterminated buses; there is no complement that falls out of the same query and produces “the buses that were fine”. So the rule reported the violations and claimed nothing about anything else.

Sit with what that meant, because it is the observation the whole course is built on. A clean result from such a rule and a board with no CAN on it produce identical output. Delete R1 and the termination rule fires. Delete the entire CAN bus and it says nothing, which is exactly what it says about a correctly terminated one. Silence cannot tell those apart, so a reader cannot either.

The fix is not cleverness about the query. It is that the rule now declares the set it examined, separately from the answer it reached, and the tool reports the difference. Nothing infers the scope from the goal, because for these rules the scope is not recoverable from the goal: signal-missing carries two negated conditions and only one of them is the test, and signal-dangling ends in a comparison with no negation at all. An author knows which half is which. A derivation would guess, and guessing wrong reports the failures as the coverage.

Everything in this tool that looked fussy earlier is downstream of the same idea. Chapter 5’s insistence that a pass is a pass about one question, the not-considered verdicts in chapters 4 and 8, and the summary line that counts what a run considered, are all the same thing: a report is only worth something if you can tell what it looked at.

Try it on the interface as a whole, and note that a house requirement and a protocol one are answered in the same voice:

agni check --verdicts --rule gateway-profiles/can-host-incomplete designs/gateway/gateway.edn

U4 gets one verdict per required signal rather than one verdict as a part, because a host wired to four of its five lines is four right answers and one wrong one. Each is addressable on its own, and gateway-profiles/can-host-incomplete:(component:U4,signal:STB) is a question you can type before running anything. That is the same pair-shaped subject chapter 9 used for strap collisions, for the same reason: the answer belongs to a relation rather than to either thing in it.

What you can now answer

  • Why “this board has CAN” is a claim about a specification rather than about two wires. (EE6)
  • How a part gets recognised as an interface host when a netlist cannot say what a chip is. (EE6)
  • Why a profile that lists only what it adds silently switches off everything it omits. (EE6)
  • Why a satisfied requirement used to produce no output, and why that made a clean run and an absent bus indistinguishable. (EE6)
  • Why the set a rule examined has to be declared by its author rather than inferred from the check it runs. (EE6)

The rules this page explains

Rule Severity What it catches
profile/signal-missing error a signal the interface declares, absent from the design
profile/termination warning a bus needing termination with none across its pair
profile/esd warning an interface signal leaving the board with no clamp
profile/signal-dangling warning an interface net reaching fewer than two connections
profile/missing-pullup warning an interface signal needing a pull-up and reaching no rail

A project’s own profile compiles these under its own name, which is why the board above reports gateway-profiles/can-esd-missing rather than profile/esd.

Next: crystals and oscillators, a short chapter on the part whose value matters as much as its presence.