Documentation menu

Quantities, types and constraints

In most modeling tools a latency is a text field. In ArcLang latency: 25 ms is a duration: the compiler knows its dimension, converts between units, and refuses to compare it with a data rate. This page shows the three mechanisms built on that: typed attributes, user-defined types and constraints.

Quantities

A quantity is a number followed by a unit: 25 ms, 100 Mbps, 512 MB, 12 V, 40 %. The unit table is part of the metamodel: time, frequency, data rate, data size, length, mass, force, voltage, current, power, energy, pressure, temperature, speed and ratio.

A unit the table does not know is a compile error, located in the source:

unknown_unit.arc
logical_architecture "Timing" {
  component "Controller" {
    function "Plan" { latency: 25 parsecs }
  }
}
$ arclang check unknown_unit.arc
Checking unknown_unit.arc...
✗ Check failed: Parser error: unknown_unit.arc: unknown unit 'parsecs' in quantity '25 parsecs' (known units include ms, s, Hz, Mbps, MB, m, kg, N, V, W) at line 3, column 35
Error: Compilation error: Parser error: unknown_unit.arc: unknown unit 'parsecs' in quantity '25 parsecs' (known units include ms, s, Hz, Mbps, MB, m, kg, N, V, W) at line 3, column 35

Typed attributes

The metamodel declares which attributes expect which type. latency, wcet and period are times, bandwidth is a data rate, safety_level and asil are enumerations. A value of the wrong type does not stop compilation: it is a metamodel: warning, and a blocker in the production gate.

wrong_dimension.arc
logical_architecture "Timing" {
  component "Controller" {
    asil: "High"
    function "Plan" { latency: 135 MHz }
  }
}
$ arclang check wrong_dimension.arc
Checking wrong_dimension.arc...
✓ No compilation errors

⚠ Compilation warnings:
  metamodel: LogicalComponent 'LC-Controller'.asil: 'High' is not a valid Asil (expected one of: QM, ASIL-A, ASIL-B, ASIL-C, ASIL-D)
  metamodel: LogicalFunction 'Plan'.latency: '135 MHz' is a frequency, expected a time (e.g. `latency: 1 s`)
… (3 more lines)

Legacy text values such as latency: "25 ms" still compile, and are read as quantities.

User-defined types

A type is a reusable definition. An element takes it with is:, inherits its attributes, and can redefine them. A type can list the attributes its instances must provide, and can specialize another type with extends.

types.arc
type "ECU" {
  required: ["ram"]
  voltage: 12 V
}

type "Safety ECU" extends "ECU" {
  safety_level: "ASIL-B"
}

type "CAN FD bus" {
  protocol: "CAN FD"
  bandwidth: 2 Mbps
}

physical_architecture "Hardware" {
  node "Camera ECU" { id: "PN-CAM" is: "ECU" ram: 512 MB }
  node "Chassis ECU" { id: "PN-CHS" is: "Safety ECU" ram: 64 MB voltage: 24 V }
  link "Chassis CAN" { from: "PN-CAM" to: "PN-CHS" is: "CAN FD bus" }
}

The chassis ECU is a safety ECU, so it carries safety_level: "ASIL-B" without writing it, and it redefines the supply voltage. For components, a type can also declare ports, which its instances inherit. An element can take several types: is: ["ECU", "Sealed unit"].

Inheritance is resolved before analysis, so the gate, the constraints and every export see the effective values.

The compiler rejects what would make a type meaningless: an unknown or duplicate type, a cycle of extends, a missing required attribute, a redefinition that changes the dimension of a quantity, and two types that define the same attribute differently unless the element settles it. Here the node omits the attribute its type requires:

missing_required.arc
type "ECU" {
  required: ["ram"]
  voltage: 12 V
}

physical_architecture "Hardware" {
  node "Camera ECU" { id: "PN-CAM" is: "ECU" }
}
$ arclang check missing_required.arc
Checking missing_required.arc...
✗ Check failed: Semantic error: PhysicalNode 'PN-CAM' is a 'ECU' but does not provide required attribute(s): ram
Error: Compilation error: Semantic error: PhysicalNode 'PN-CAM' is a 'ECU' but does not provide required attribute(s): ram

Port, interface and allocation definitions

A type declaring ports is a port definition when a port takes it: the port holds the type's ports, and carries its attributes, interface: first. A type taken by an exchange is an interface definition: its protocol, bandwidth and items go to every exchange of that type. A type declaring from_kind and to_kind is an allocation definition: an allocates trace that takes it must go from an element of the first kind to one of the second, and carries the type's attributes. In SysML v2 they are a port def, a connection def and an allocation def, and the usages are typed by them.

definitions.arc
type "HV connector" {
  interface: "HVIL 800 V"
  port inout "pole" { multiplicity: 2 }
}
type "HV power" { protocol: "DC 800 V" }
type "Software deployment" {
  from_kind: "PhysicalNode"
  to_kind: "LogicalComponent"
  partition: "ASIL-C partition"
}

logical_architecture "EV" {
  component "Battery" { id: "LC-BATT" port inout "hv" { is: "HV connector" } }
  component "Charger" { id: "LC-CHG" port inout "hv" { is: "HV connector" } }
  component "Battery management" { id: "LC-BMS" }
  component_exchange "Charging power" {
    is: "HV power"
    from_port: "LC-CHG.hv.pole"
    to_port: "LC-BATT.hv.pole"
  }
}

physical_architecture "EV hardware" {
  node "Battery ECU" { id: "PN-BECU" }
}

trace "PN-BECU" allocates "LC-BMS" { is: "Software deployment" }

Each hv port holds two pole ports without writing them, and the exchange joins two of them. Reversing the trace (trace "LC-BMS" allocates "PN-BECU") is an error: the allocation goes from a logical component, not a physical node.

Constraints

A constraint is an assertion over typed attributes. It compares two values of the same dimension, built from attributes of elements, arithmetic, and the functions sum, min, max and count over a chain, a path or a capability.

budget.arc
system_analysis "Lane keeping" {
  function "Detect lane markings" { id: "SF-DET" latency: 30 ms wcet: 35 ms }
  function "Estimate departure" { id: "SF-EST" latency: 15 ms wcet: 18 ms }
  function "Command steering" { id: "SF-CMD" latency: 10 ms wcet: 12 ms }

  functional_chain "Lane departure reaction" {
    id: "FC-LKA"
    involves: ["SF-DET", "SF-EST", "SF-CMD"]
    latency_budget: 80 ms
  }
}

physical_architecture "Hardware" {
  node "Camera ECU" { id: "PN-CAM" }
  node "Chassis ECU" { id: "PN-CHS" }
  link ChassisCAN {
    from: "PN-CAM"
    to: "PN-CHS"
    protocol: "CAN FD"
    bandwidth: 2 Mbps
    load: 600 kbps
  }
}

constraint "Reaction time margin" {
  description: "Function latencies stay within 80 % of the chain budget"
  assert: sum("FC-LKA", latency) <= "FC-LKA".latency_budget * 0.8
}

constraint "Worst-case reaction time" {
  assert: sum("FC-LKA", wcet) <= "FC-LKA".latency_budget
}

constraint "CAN bus load" {
  assert: ChassisCAN.load / ChassisCAN.bandwidth <= 0.4
}

arclang check evaluates each constraint and prints both sides:

$ arclang check budget.arc
Checking budget.arc...
✓ No compilation errors

Constraints:
  ✓ Reaction time margin — sum("FC-LKA", latency) <= "FC-LKA".latency_budget * 0.8  [55 ms vs 64 ms]
  ✓ Worst-case reaction time — sum("FC-LKA", wcet) <= "FC-LKA".latency_budget  [65 ms vs 80 ms]
  ✓ CAN bus load — ChassisCAN.load / ChassisCAN.bandwidth <= 0.4  [0.3 vs 0.4]

Three things are worth noting.

  • Units convert. A sum can mix ms and s; the result is exact.
  • Dimensions derive. A load divided by a bandwidth is a ratio, so it compares with 0.4. A voltage times a current is a power. A frame size divided by a data rate is a time.
  • Mistakes are errors. Comparing a time with a data rate, or naming an element or an attribute that does not exist, is a compile error, not a constraint that silently holds.
bad_constraint.arc
system_analysis "Lane keeping" {
  function "Detect lane markings" { id: "SF-DET" latency: 30 ms }
}

physical_architecture "Hardware" {
  node "Camera ECU" { id: "PN-CAM" }
  node "Chassis ECU" { id: "PN-CHS" }
  link ChassisCAN { from: "PN-CAM" to: "PN-CHS" bandwidth: 2 Mbps }
}

constraint "Nonsense" {
  assert: "SF-DET".latency <= ChassisCAN.bandwidth
}
$ arclang check bad_constraint.arc
Checking bad_constraint.arc...
✗ Check failed: Semantic error: constraint 'Nonsense': cannot apply '<=' to a time and a data rate
Error: Compilation error: Semantic error: constraint 'Nonsense': cannot apply '<=' to a time and a data rate

A constraint that evaluates to false is a warning when you compile and a blocker in the production gate, which also reports how many constraints hold. The repository has a complete example, examples/features/timing-constraints/timing_constraints.arc:

$ arclang gate examples/features/timing-constraints/timing_constraints.arc
Production-readiness gate (ISO26262) — examples/features/timing-constraints/timing_constraints.arc
  Requirements: 1 total, 1 satisfied, 1 verified
  Constraints: 5 declared, 5 hold

PRODUCTION GATE: PASS ✓

Multiplicity

multiplicity: 4 on a logical component or a physical node says how many of it its owner has. A range is written as text: "0..1", "1..*", "*". An ill-formed range is reported like any typed attribute. Multiplicity is a statement about the architecture: constraints and budgets do not multiply by it.

Every ArcLang sample on this page is compiled, and every command is run, with the released compiler when the site is built.