Getting started › Concepts

Getting started

Concepts: the model before the syntax

arche-lang describes a system with a few ideas. Each one is small. Learn these five and the rest of the language is detail. Each section has a picture and a model that compiles (the test test/teach.test.js checks every one).

The one-page syntax is in the cheat sheet. The full language is in the specification and the language reference.

1. Definition and instance: component and part#

A component is a definition: a kind of thing, written once. A part is an instance of it, placed in a system. Two parts of one component are two things, each with its own state and variables.

flowchart LR
  Lamp["component Lamp<br/>(the definition)"]
  subgraph Room["system Room"]
    desk["part desk: Lamp"]
    ceiling["part ceiling: Lamp"]
  end
  Lamp -. instance of .-> desk
  Lamp -. instance of .-> ceiling
component Lamp "Lamp" {
  var level: uint8 = 0
}

system Room "Room" {
  part desk: Lamp
  part ceiling: Lamp
}

A system is what Arche shows on its canvas and what the simulator runs. A system is also a definition: a part can be a system, so a model nests.

A part talks to another through its ports. A port has a direction (in, out, inout), a type (the medium: can, ethernet, gpio…) and the messages it sends or receives. A message is the data that travels. A link a.p -> b.q joins two ports of two parts. archec checks that what one end sends, the other receives.

flowchart LR
  button["button: Button"] -- "click → cmd<br/>PRESS { count }" --> lamp["lamp: Lamp"]
message PRESS "Press" { count: uint8 }

component Button "Button" {
  out click: can sends PRESS
}

component Lamp "Lamp" {
  in cmd: can receives PRESS
}

system Room "Room" {
  part button: Button
  part lamp: Lamp
  button.click -> lamp.cmd
}

The ports belong to the component; the link belongs to the system. The same Lamp can be wired one way in one system and another way in another.

3. State machines and triggers#

A component can have one state machine. It starts in its first state. A transition A -> B on TRIGGER [guard] is taken when the trigger comes and the guard holds. A trigger is a message that arrives on a port, or a named event. A function's body can also change state, with goto, and test it, with state == On.

stateDiagram-v2
  [*] --> Off
  Off --> On: PRESS
  On --> Off: PRESS
  On --> Off: main, after 10 s (goto Off)
message PRESS "Press" { count: uint8 }

component Button "Button" {
  out click: can sends PRESS
}

component Lamp "Lamp" {
  in cmd: can receives PRESS
  states {
    state Off
    state On
    Off -> On on PRESS
    On -> Off on PRESS
  }
  fn main "Night mode" ```arche
    wait 10s
    if state == On {
      goto Off
    }

}

system Room "Room" { part button: Button part lamp: Lamp button.click -> lamp.cmd }


The body is written in the action language, in a ```` ```arche ```` block.
archec checks the names, the types, the states and the ports it uses, and
translates it to the Python the simulator runs, and to C, Rust and Go. `main`
runs from the start, as the part's own thread.

## 4. Requirement, satisfies, verified-by, test plan

A `requirement` says what must hold. A part `satisfies` it: that part is
where it is met. A test plan's case `verifies` it: that test shows it holds.
`verified-by` on the requirement names the test plan from the other side.
archec reports a requirement nothing satisfies or nothing verifies.

```mermaid
flowchart LR
  req["requirement REQ-ON"]
  lamp["component Lamp"] -- satisfies --> req
  tp["test-plan TP-ON<br/>case TC-ON-01"] -- verifies --> req
  req -. verified-by .-> tp
message PRESS "Press" { count: uint8 }

requirement REQ-ON "Turn on" {
  text "The lamp shall turn on within 50 ms of a press."
  verified-by TP-ON
}

component Button "Button" {
  out click: can sends PRESS
}

component Lamp "Lamp" {
  satisfies REQ-ON
  in cmd: can receives PRESS
  states {
    state Off
    state On
    Off -> On on PRESS
    On -> Off on PRESS
  }
}

system Room "Room" {
  part button: Button
  part lamp: Lamp
  button.click -> lamp.cmd
}

test-plan TP-ON "Turning on" {
  case TC-ON-01 "A press turns it on" {
    verifies REQ-ON
    at 0ms do send(button.click, PRESS(count=1))
    within 50ms expect lamp.state == "On"
  }
}

archec test runs the case in the simulator. A test step is Python, as the simulator runs it: do acts, expect checks.

5. System, deployment, generated code#

The logical system says what the parts do. A second system, the platform, says what they run on: processors and buses. A deployment places each part of the first on the second — a binary on a processor, a thread that runs the part — without changing either. The generators read the deployment and write the code: one program per binary.

flowchart LR
  logical["system Room<br/>(what it does)"] --> dep["deployment Board"]
  hw["system RoomHw<br/>(what it runs on)"] --> dep
  dep --> gen["archec gen --target c<br/>embassy · go · ros2 …"]
  gen --> code["lamp_fw: C, Rust, Go…"]
message PRESS "Press" { count: uint8 }

component Button "Button" {
  out click: can sends PRESS
}

component Lamp "Lamp" {
  in cmd: can receives PRESS
  var level: uint8 = 0
  fn dim "Dim" {
    ```arche
    if level > 0 {
      level -= 1
    }
    ```
    wcet 50us
  }
}

system Room "Room" {
  part button: Button
  part lamp: Lamp
  button.click -> lamp.cmd
}

component Mcu "Lamp MCU" {
  kind processor
  cores 1
  inout can0: can "CAN"
}

system RoomHw "Room hardware" {
  part mcu: Mcu
}

deployment Board "On the board" {
  system Room
  platform RoomHw
  binary lamp_fw "lamp.elf" on mcu {
    thread ctl "Control" { runs lamp; period 10ms; step dim }
  }
}
archec gen room.arche --target c -o generated

writes generated/c/: lamp.c, the part, with dim translated from its ```arche body (if (self->level > 0) { self->level -= 1; }), the types of the messages, and lamp_fw/, the program of the binary: its main, the thread that calls dim every 10 ms, and a Makefile. See code generation for every target.

Where next#