hector-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hector/__init__.py +4 -0
- hector/__main__.py +7 -0
- hector/commands/__init__.py +35 -0
- hector/commands/base.py +111 -0
- hector/commands/export.py +26 -0
- hector/commands/init.py +15 -0
- hector/commands/run.py +27 -0
- hector/commands/test.py +52 -0
- hector/commands/validate.py +15 -0
- hector/connections.py +130 -0
- hector/core.py +259 -0
- hector/dependencies.py +41 -0
- hector/docker.py +191 -0
- hector/generator.py +292 -0
- hector/hubs.py +196 -0
- hector/mappings.py +167 -0
- hector/modules.py +201 -0
- hector/peripherals.py +168 -0
- hector/pipeline.py +693 -0
- hector/reporters.py +111 -0
- hector/runners.py +380 -0
- hector/scaffold.py +75 -0
- hector/validator.py +414 -0
- hector_cli-0.1.0.dist-info/METADATA +1401 -0
- hector_cli-0.1.0.dist-info/RECORD +29 -0
- hector_cli-0.1.0.dist-info/WHEEL +5 -0
- hector_cli-0.1.0.dist-info/entry_points.txt +2 -0
- hector_cli-0.1.0.dist-info/licenses/LICENSE +661 -0
- hector_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hector-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: YAML-driven Renode + Verilator simulation orchestrator for embedded hardware CI
|
|
5
|
+
Home-page: https://hector-ci.com
|
|
6
|
+
Author: Nemesis
|
|
7
|
+
Author-email: info@hector-ci.com
|
|
8
|
+
License: AGPL-3.0-or-later
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
12
|
+
Classifier: Topic :: Software Development :: Testing
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: author-email
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: description
|
|
23
|
+
Dynamic: description-content-type
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: license
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-dist
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Hector
|
|
32
|
+
|
|
33
|
+
**`hector`** is the open-source CLI at the core of the Hector platform: a
|
|
34
|
+
YAML-driven orchestrator for simulating embedded hardware systems. It combines
|
|
35
|
+
[Renode](https://renode.io/) machine emulation with
|
|
36
|
+
[Verilator](https://www.veripool.org/verilator/) co-simulated HDL modules,
|
|
37
|
+
wiring them together from a single `.hector.yaml` file. Everything runs inside
|
|
38
|
+
the official `antmicro/renode` Docker image, so builds are reproducible
|
|
39
|
+
regardless of host toolchain.
|
|
40
|
+
|
|
41
|
+
One config drives every mode — interactive simulation (`hector run`), automated
|
|
42
|
+
testing (`hector test`), and command export (`hector export`) — and the same
|
|
43
|
+
file is what [Hector CI](https://hector-ci.com) runs in continuous integration.
|
|
44
|
+
|
|
45
|
+
Licensed under **AGPL-3.0-or-later** — see [LICENSE](LICENSE) and
|
|
46
|
+
[LICENSING.md](LICENSING.md).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Contents
|
|
51
|
+
|
|
52
|
+
- [Requirements](#requirements)
|
|
53
|
+
- [Quick start](#quick-start)
|
|
54
|
+
- [Invocation modes](#invocation-modes)
|
|
55
|
+
- [Configuration reference](#configuration-reference)
|
|
56
|
+
- [Top-level fields](#top-level-fields)
|
|
57
|
+
- [arguments](#arguments)
|
|
58
|
+
- [matrix](#matrix)
|
|
59
|
+
- [modules](#modules)
|
|
60
|
+
- [hubs](#hubs)
|
|
61
|
+
- [machines](#machines)
|
|
62
|
+
- [peripherals](#peripherals)
|
|
63
|
+
- [connections (per-machine)](#connections-per-machine)
|
|
64
|
+
- [mappings](#mappings)
|
|
65
|
+
- [artifacts](#artifacts)
|
|
66
|
+
- [connections (global)](#connections-global)
|
|
67
|
+
- [mappings (global)](#mappings-global)
|
|
68
|
+
- [build](#build)
|
|
69
|
+
- [tests](#tests)
|
|
70
|
+
- [quantum](#quantum)
|
|
71
|
+
- [ci](#ci)
|
|
72
|
+
- [Connection syntax](#connection-syntax)
|
|
73
|
+
- [Interpolation](#interpolation)
|
|
74
|
+
- [Generated artifacts](#generated-artifacts)
|
|
75
|
+
- [Complete example](#complete-example)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
- Python 3.10+ with `pyyaml` (`pip install pyyaml`)
|
|
82
|
+
- Docker (the Renode image is pulled automatically on first run)
|
|
83
|
+
- Git — only needed when a config builds `modules:` or uses `${RENODE_DIR}`; the framework then clones the Renode source on first run (see [Renode source checkout](#renode-source-checkout))
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
project/
|
|
91
|
+
├── .hector
|
|
92
|
+
└── platforms/
|
|
93
|
+
└── boards/
|
|
94
|
+
└── stm32f4_discovery.repl # your Renode platform file
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Bootstrap a new project:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
hector init
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then simulate or test:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
hector run # simulate (interactive Renode monitor)
|
|
107
|
+
hector test # run the tests: section
|
|
108
|
+
hector export # print the run command without executing it
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Generated files go in `.hector/` — add it to your `.gitignore`. The Renode **binary** runs from the Docker image; the Renode **source** is only cloned when a config needs it (see [Renode source checkout](#renode-source-checkout)).
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Invocation modes
|
|
116
|
+
|
|
117
|
+
| Command | Behaviour |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `hector run` | Run simulation interactively (Renode monitor) |
|
|
120
|
+
| `hector run --set BIN=fw.elf` | Override a config argument at the command line |
|
|
121
|
+
| `hector run --set BIN=fw.elf --set BOARD=nucleo` | Override multiple arguments |
|
|
122
|
+
| `hector run --debug boardA:3333` | Halt a CPU and open a GDB server |
|
|
123
|
+
| `hector run --debug boardA:3333 --debug boardB:3334` | Halt multiple CPUs on different ports |
|
|
124
|
+
| `hector run --renode-args '--console'` | Pass extra flags verbatim to Renode |
|
|
125
|
+
| `hector run --gather-execution-metrics` | Enable Renode's CPU execution profiler for all machines |
|
|
126
|
+
| `hector run --gather-execution-metrics boardA` | Enable profiler for one specific machine |
|
|
127
|
+
| `hector run --gather-execution-metrics boardA --gather-execution-metrics boardB` | Enable profiler for selected machines |
|
|
128
|
+
| `hector run --renode-version 1.16.1` | Override the Renode version from `.hector.yaml` |
|
|
129
|
+
| `hector run --renode-dir /cache/renode` | Use/cache the Renode source checkout at this path (only fetched when needed) |
|
|
130
|
+
| `hector run --renode-integration-dir /cache/integration` | Likewise for the verilator-integration checkout |
|
|
131
|
+
| `hector run --no-docker` | Use locally installed `renode` / `renode-test` instead of Docker |
|
|
132
|
+
| `hector run --workspace-mount /mnt` | Override the container path the project root is mounted at |
|
|
133
|
+
| `hector run --snapshot path/to/snapshot.save` | Load a snapshot instead of booting from the resc |
|
|
134
|
+
| `hector run --snapshot snap.save --renode-version 1.16.1` | Load a snapshot without a `.hector.yaml` |
|
|
135
|
+
| `hector test` | Run the `tests:` section; exit 1 on any failure |
|
|
136
|
+
| `hector test --fail-fast` | Stop after the first failing test |
|
|
137
|
+
| `hector test --test-file tests/boot.robot` | Run a specific `.robot` file instead of the YAML tests |
|
|
138
|
+
| `hector test --test-name "Button press"` | Run only tests whose name contains the given substring |
|
|
139
|
+
| `hector test --test-name "boot" --test-name "uart"` | Run tests matching any of the given names |
|
|
140
|
+
| `hector test --live` | Stream bash output line-by-line and enable verbose keyword output for robot tests |
|
|
141
|
+
| `hector test --snapshot path/to/snapshot.save` | Load a snapshot at the start of every test |
|
|
142
|
+
| `hector test --reporters junit --reporters json` | Select which reporters emit after tests (repeatable) |
|
|
143
|
+
| `hector test --output my-results` | Write test results and artifacts to `my-results/` instead of `results/` |
|
|
144
|
+
| `hector test --renode-test-args '--loglevel DEBUG'` | Pass extra flags verbatim to `renode-test` |
|
|
145
|
+
| `hector run --job BOARD=stm32f7` | Select the matrix combination where BOARD=stm32f7 |
|
|
146
|
+
| `hector run --job BOARD=stm32f7,FW=release.elf` | Select the exact combination (pin every matrix variable) |
|
|
147
|
+
| `hector export` | Generate the files and print the run command instead of executing it |
|
|
148
|
+
| `hector export --no-docker` | Print the bare `renode <resc>` command instead of the `docker run …` one |
|
|
149
|
+
| `hector validate` | Validate `.hector.yaml` without running anything |
|
|
150
|
+
| `hector init` | Scaffold a starter `.hector.yaml` |
|
|
151
|
+
| `hector --version` | Print the tool version |
|
|
152
|
+
|
|
153
|
+
`hector` is structured as subcommands: `run` (simulate), `test` (run the `tests:`
|
|
154
|
+
section), `export` (emit the command without running it), plus `init` and
|
|
155
|
+
`validate`. Run `hector <command> --help` for the flags each one accepts.
|
|
156
|
+
|
|
157
|
+
### Debug mode (`--debug NODE:PORT`)
|
|
158
|
+
|
|
159
|
+
Repeat the flag for each machine you want to debug. ([Renode docs: GDB debugging](https://renode.readthedocs.io/en/latest/debugging/gdb.html))
|
|
160
|
+
|
|
161
|
+
- Adds `machine StartGdbServer <port>` to the generated resc while that machine is active.
|
|
162
|
+
- Adds `cpu IsHalted true` to freeze the CPU until a debugger attaches.
|
|
163
|
+
- Exposes the port via `-p PORT:PORT` in Docker.
|
|
164
|
+
|
|
165
|
+
All other machines boot and run normally.
|
|
166
|
+
|
|
167
|
+
`--debug` also works alongside `--snapshot`: the loaded snapshot opens the same GDB
|
|
168
|
+
server(s) and halts the debugged CPU(s) before resuming, so you can attach to a
|
|
169
|
+
restored state. (A GDB server is a live connection, never part of saved snapshot
|
|
170
|
+
state, so it is always (re)started on load.)
|
|
171
|
+
|
|
172
|
+
Connect with:
|
|
173
|
+
```bash
|
|
174
|
+
arm-none-eabi-gdb firmware.elf
|
|
175
|
+
(gdb) target extended-remote localhost:3333
|
|
176
|
+
(gdb) break main
|
|
177
|
+
(gdb) continue
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Or use VS Code with the Cortex-Debug extension pointed at `localhost:3333`.
|
|
181
|
+
|
|
182
|
+
### Test mode (`hector test`)
|
|
183
|
+
|
|
184
|
+
Runs every step in the `tests:` section in declaration order. All steps run to completion even if one fails. The process exits with code 1 if any step failed, making it usable directly in CI.
|
|
185
|
+
|
|
186
|
+
Add `--fail-fast` to stop after the first failing job when running a matrix.
|
|
187
|
+
|
|
188
|
+
#### Filtering tests by name (`--test-name`)
|
|
189
|
+
|
|
190
|
+
Run only the tests whose name contains the given substring. The flag is repeatable — any match runs:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# run one test
|
|
194
|
+
hector test --test-name "Button press"
|
|
195
|
+
|
|
196
|
+
# run two tests by name fragment
|
|
197
|
+
hector test --test-name "boot" --test-name "uart"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Matching is case-sensitive substring search against the `name:` field in the YAML (or the basename of `--test-file`).
|
|
201
|
+
|
|
202
|
+
#### Streaming output live (`--live`)
|
|
203
|
+
|
|
204
|
+
By default bash test output is captured and printed as a block after the step finishes, and robot tests show only a per-test summary line. With `--live`:
|
|
205
|
+
|
|
206
|
+
- **bash steps** — each output line is printed as it is written by the script.
|
|
207
|
+
- **robot steps** — `renode-test` runs with `--verbose`, printing every keyword call and its timing as it executes.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
hector test --live
|
|
211
|
+
|
|
212
|
+
# combine with --test-name to focus on one test with full output
|
|
213
|
+
hector test --test-name "Button press" --live
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Both flags are independent and can be used together or separately.
|
|
217
|
+
|
|
218
|
+
#### Loading a snapshot (`--snapshot`)
|
|
219
|
+
|
|
220
|
+
Load a previously saved Renode snapshot instead of booting from the generated resc. The snapshot restores the complete emulation state (all machines, memory, peripheral registers) and resumes execution.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# simulation mode: drop into the monitor with state restored
|
|
224
|
+
hector run --snapshot results/step_1_Linux_boots/snapshot.save
|
|
225
|
+
|
|
226
|
+
# test mode: every test starts from the snapshot instead of booting
|
|
227
|
+
hector test --snapshot results/step_1_Linux_boots/snapshot.save
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
When used with `--renode-version`, no `.hector.yaml` is needed at all — useful for inspecting a snapshot outside of any project:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
hector run --snapshot /path/to/snapshot.save --renode-version 1.16.1
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The CLI flag overrides any per-test `snapshot:` field in the YAML (see [tests](#tests)).
|
|
237
|
+
|
|
238
|
+
**Saving a snapshot** from the interactive Renode monitor:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
Save @/home/user/projects/myboard/.hector/snapshots/post_boot.save
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Use the actual host path to your project directory (the same path you see on the host — the container mounts it at the identical location). Create the directory first.
|
|
245
|
+
|
|
246
|
+
#### Running a specific `.robot` file (`--test-file`)
|
|
247
|
+
|
|
248
|
+
Pass any `.robot` file directly instead of the YAML-defined tests. Hector generates the resc from your YAML config, then runs the file against it. The generated resc path is injected as `${RESC}` so the file can load the emulation:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
hector test --test-file tests/boot.robot
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`tests/boot.robot` must include the resc itself:
|
|
255
|
+
```robot
|
|
256
|
+
*** Settings ***
|
|
257
|
+
Library Collections
|
|
258
|
+
Resource ${RENODEKEYWORDS}
|
|
259
|
+
Suite Teardown Reset Emulation
|
|
260
|
+
|
|
261
|
+
*** Test Cases ***
|
|
262
|
+
Boot sequence
|
|
263
|
+
Execute Command include @${RESC}
|
|
264
|
+
Create Terminal Tester sysbus.usart2 machine=boardA
|
|
265
|
+
Wait For Line On Uart Board initialized timeout=30
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Changing the output directory (`--output`)
|
|
269
|
+
|
|
270
|
+
By default Hector writes test results, Robot Framework XML/HTML reports, and JUnit XML to `results/`. Pass `--output` to redirect everything to a different path:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
hector test --output ci-results
|
|
274
|
+
hector test --output /tmp/run-42
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
In simulation mode the flag controls the directory pre-created for Renode to write file-mapped outputs (e.g. UART logs from `file:results/uart.log`). Note that your YAML `file:` paths should match whatever directory you choose here.
|
|
278
|
+
|
|
279
|
+
#### Test reporters (`--reporters`)
|
|
280
|
+
|
|
281
|
+
After all steps complete, Hector calls one or more reporters with the full result list. Two are built in:
|
|
282
|
+
|
|
283
|
+
- **`junit`** (default) — writes `<output>/junit.xml`, compatible with GitLab CI, GitHub Actions, and Jenkins.
|
|
284
|
+
- **`json`** — writes `<output>/manifest.json`, a machine-readable index for dashboards / CI: one entry per test with `status`, `duration`, `commands`, and relative paths to its detail artifacts (`report_html`, plus `log_html` / `robot_xml` for robot tests).
|
|
285
|
+
|
|
286
|
+
The flag is repeatable (repeat it once per reporter):
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
hector test # default: junit
|
|
290
|
+
hector test --reporters junit --reporters json # also emit the dashboard manifest
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
To add a custom reporter, register it in Python before invoking the pipeline:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from hector.reporters import REPORTERS
|
|
297
|
+
|
|
298
|
+
@REPORTERS.register("csv")
|
|
299
|
+
def csv_reporter(results, output_dir):
|
|
300
|
+
import csv, os
|
|
301
|
+
with open(os.path.join(output_dir, "results.csv"), "w") as f:
|
|
302
|
+
w = csv.DictWriter(f, fieldnames=["name", "type", "passed", "duration"])
|
|
303
|
+
w.writeheader()
|
|
304
|
+
for r in results:
|
|
305
|
+
w.writerow({"name": r.name, "type": r.type,
|
|
306
|
+
"passed": r.passed, "duration": r.duration})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Then pass its name with `--reporters csv`. The flag is repeatable, so combine reporters by repeating it: `--reporters junit --reporters csv`.
|
|
310
|
+
|
|
311
|
+
#### Passing extra flags to `renode-test` (`--renode-test-args`)
|
|
312
|
+
|
|
313
|
+
Forwards additional flags verbatim to the `renode-test` invocation for every test step. Accepts any flag that Robot Framework accepts:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Enable verbose logging for debugging
|
|
317
|
+
hector test --renode-test-args '--loglevel DEBUG'
|
|
318
|
+
|
|
319
|
+
# Inject a variable into every robot step
|
|
320
|
+
hector test --renode-test-args '--variable FOO:bar'
|
|
321
|
+
|
|
322
|
+
# Run only tests tagged "smoke"
|
|
323
|
+
hector test --renode-test-args '--include smoke'
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
These are appended after any per-step `args:` keys defined in the YAML, so per-step args take precedence.
|
|
327
|
+
|
|
328
|
+
### Simulation mode
|
|
329
|
+
|
|
330
|
+
Launches one Renode emulation per job in the Docker container. Each machine declared in the YAML becomes a named Renode machine in that emulation. The Renode monitor is interactive; type `help` at the prompt for available commands.
|
|
331
|
+
|
|
332
|
+
#### Gathering execution metrics (`--gather-execution-metrics`)
|
|
333
|
+
|
|
334
|
+
Enables Renode's built-in CPU execution profiler. Without a machine name, enables it for all machines. Repeatable to target specific machines:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# all machines
|
|
338
|
+
hector run --gather-execution-metrics
|
|
339
|
+
|
|
340
|
+
# specific machines only
|
|
341
|
+
hector run --gather-execution-metrics boardA
|
|
342
|
+
hector run --gather-execution-metrics boardA --gather-execution-metrics boardB
|
|
343
|
+
|
|
344
|
+
# combine with --output
|
|
345
|
+
hector run --gather-execution-metrics --output ci-results
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
For each targeted machine, Hector injects `cpu EnableProfiler @<path>` into the generated resc. Profiler output is written to the output directory, one binary file per machine per job:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
results/
|
|
352
|
+
├── metrics_boardA_job_1.bin
|
|
353
|
+
└── metrics_boardB_job_1.bin
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Works in both simulation and test mode. In test mode each test step re-runs the resc, so the metrics file for each machine reflects only the most recent test step's execution.
|
|
357
|
+
|
|
358
|
+
Analyse the output with Renode's [ExecutionMetricsAnalyzer](https://renode.readthedocs.io/en/latest/advanced/execution-metrics.html).
|
|
359
|
+
|
|
360
|
+
#### Passing extra flags to Renode (`--renode-args`)
|
|
361
|
+
|
|
362
|
+
Forwards additional flags verbatim to the `renode` process:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
hector run --renode-args '--console'
|
|
366
|
+
hector run --renode-args '--hide-log --config my.conf'
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### Running without Docker (`--no-docker`)
|
|
370
|
+
|
|
371
|
+
Call the locally installed `renode` and `renode-test` binaries directly instead of launching a Docker container. `build:` steps and `shell` tests also run on the host (their `image:` is ignored, with a warning), as do module builds.
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
hector run --no-docker
|
|
375
|
+
hector test --no-docker
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Requires `renode` and `renode-test` to be on your `PATH` — and, since images are ignored, whatever toolchains your `build:`/`shell` scripts call (e.g. a compiler). **`--no-docker` and `--workspace-mount` are mutually exclusive** — they both control how the project root is made accessible and cannot be combined.
|
|
379
|
+
|
|
380
|
+
#### Overriding the Renode version (`--renode-version`)
|
|
381
|
+
|
|
382
|
+
Override the `renode_version:` from `.hector.yaml` without editing the file:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
hector run --renode-version 1.15.0
|
|
386
|
+
hector test --renode-version 1.15.0
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
When combined with `--snapshot` in simulation mode, `.hector.yaml` is not required at all — the two flags together are self-contained:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
hector run --snapshot path/to/snapshot.save --renode-version 1.16.1
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### Renode source checkout
|
|
396
|
+
|
|
397
|
+
Running a simulation does **not** need the Renode source — the binary and its bundled platforms come from the `antmicro/renode` Docker image. Hector clones the Renode source (and the verilator-integration repo) into `.hector/` **only when a config actually needs it**:
|
|
398
|
+
|
|
399
|
+
- it builds `modules:` (Verilated / C# co-simulated peripherals, which compile against the Renode source), or
|
|
400
|
+
- it interpolates `${RENODE_DIR}` / `${INTEGRATION_DIR}`.
|
|
401
|
+
|
|
402
|
+
For everything else — plain or YAML-defined machines, firmware URLs, `build:`/`shell` steps — nothing is cloned, so runs start immediately.
|
|
403
|
+
|
|
404
|
+
When the source *is* needed, point hector at a cached or shared checkout instead of re-cloning each time:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
hector test --renode-dir /cache/renode --renode-integration-dir /cache/renode-integration
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
An existing directory is reused as-is; a missing one is cloned into. The location may live **outside the project** (e.g. a persistent CI cache) — hector bind-mounts an out-of-tree checkout into every container 1:1 (at the same path), so `modules:` builds and `${RENODE_DIR}` references still resolve. A path under the project works too (it's covered by the normal workspace mount).
|
|
411
|
+
|
|
412
|
+
#### Overriding the workspace mount path (`--workspace-mount`)
|
|
413
|
+
|
|
414
|
+
By default the project root is bind-mounted into the container at the **same absolute path** as on the host (e.g. `/home/user/projects/myboard`), so paths look identical inside and outside the container. Override this only if the host path is unavailable at that location inside the image:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
hector run --workspace-mount /workspace
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Cannot be combined with `--no-docker`** — if you are running locally there is no container mount to configure.
|
|
421
|
+
|
|
422
|
+
### Selecting a matrix combination (`--job`)
|
|
423
|
+
|
|
424
|
+
**hector runs exactly one matrix combination per invocation — it does not expand the matrix itself.** Iterating combinations (and running them in parallel) is the CI's job: it reads the `matrix:`, then calls hector once per combination.
|
|
425
|
+
|
|
426
|
+
- **No matrix:** a single invocation runs the one (empty) job — no `--job` needed.
|
|
427
|
+
- **Matrix defined:** you must pin it to one combination with `--job KEY=VALUE`, repeating keys with commas to constrain every variable:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
hector test --job BOARD=stm32f7 # one variable pins it
|
|
431
|
+
hector test --job BOARD=stm32f7,FW=release.elf # pin every variable
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
If `--job` is missing (or matches more than one combination), hector lists the combinations and exits without running:
|
|
435
|
+
|
|
436
|
+
```
|
|
437
|
+
[ERROR] The matrix produces 3 combinations; a run targets one. Select it with
|
|
438
|
+
--job KEY=VALUE (e.g. --job BOARD=stm32f4).
|
|
439
|
+
Combinations:
|
|
440
|
+
BOARD=stm32f4 FW=debug.elf
|
|
441
|
+
BOARD=stm32f7 FW=debug.elf
|
|
442
|
+
BOARD=stm32f4 FW=release.elf
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Values match as strings, so `--job RATE=2` selects a numeric `2` in the matrix.
|
|
446
|
+
### Export mode (`hector export`)
|
|
447
|
+
|
|
448
|
+
Builds everything a run would — the per-machine `.repl` files and the emulation
|
|
449
|
+
`.resc` — but, instead of launching, prints the exact command that *would* run it:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# the docker invocation hector would execute
|
|
453
|
+
hector export
|
|
454
|
+
# → docker run -it --rm … antmicro/renode:1.16.1 renode .hector/resc/job_1.resc
|
|
455
|
+
|
|
456
|
+
# with --no-docker, the bare local command instead
|
|
457
|
+
hector export --no-docker
|
|
458
|
+
# → renode .hector/resc/job_1.resc
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Useful for inspecting or wrapping the command (custom Docker flags, a different
|
|
462
|
+
runner, CI plumbing). With a matrix, one command per job is printed. The same
|
|
463
|
+
build flags as `run` apply (`--set`, `--debug`, `--snapshot`, `--renode-args`, …).
|
|
464
|
+
|
|
465
|
+
### Validate mode (`hector validate`)
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
hector validate
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Parses and validates `.hector.yaml` without touching Docker. All errors are collected and reported at once:
|
|
472
|
+
|
|
473
|
+
```
|
|
474
|
+
[VALIDATE] Checking .hector.yaml ...
|
|
475
|
+
ERROR modules.uart0.type: Required: the Renode class this module exposes.
|
|
476
|
+
ERROR hubs.mylink.type: Unknown hub type 'spi'. Available: ble, can, ...
|
|
477
|
+
WARN machines.boardA.peripherals.btn: Unknown machine key 'tyep'.
|
|
478
|
+
[VALIDATE] FAILED — 2 error(s), 1 warning(s).
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Configuration reference
|
|
484
|
+
|
|
485
|
+
The configuration lives in `.hector.yaml`.
|
|
486
|
+
|
|
487
|
+
### Top-level fields
|
|
488
|
+
|
|
489
|
+
| Field | Required | Description |
|
|
490
|
+
|---|---|---|
|
|
491
|
+
| `version` | No | Schema version (currently `"0.1"`). Warns if unrecognised. |
|
|
492
|
+
| `renode_version` | Yes | Renode version, no leading `v` (e.g. `1.16.1`). Selects both the git tag for cloning and the Docker image tag. |
|
|
493
|
+
| `arguments` | No | Scalar defaults, overridable by env var or `--set`. |
|
|
494
|
+
| `build` | No | Pre-sim shell steps run once per job in containers (compile firmware, fetch/generate files, …). Interpolated with the job's arguments/matrix variables. |
|
|
495
|
+
| `matrix` | No | Cross-product job expansion. |
|
|
496
|
+
| `modules` | No | Verilated / C# peripheral type definitions. |
|
|
497
|
+
| `hubs` | No | Emulation-level connection objects (uart, can, ethernet, gpio, usb, wireless, ble, wisun). |
|
|
498
|
+
| `machines` | No | The machines to simulate. Optional: a config may be build-only or run sim-independent (`requires_sim: false`) shell tests. `run`/`export` and sim-backed tests require at least one. |
|
|
499
|
+
| `connections` | No | Global signal wiring. |
|
|
500
|
+
| `mappings` | No | Emulated peripheral → host resource bindings. |
|
|
501
|
+
| `tests` | No | Test steps run by `hector test`. |
|
|
502
|
+
| `quantum` | No | Override the global time quantum (seconds). |
|
|
503
|
+
| `ci` | No | CI pipeline definitions: when and how a CI server runs this config. |
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
### arguments
|
|
508
|
+
|
|
509
|
+
Scalar parameters with defaults. Usable anywhere in the YAML as `${NAME}`. Matrix variables take precedence over arguments when names collide.
|
|
510
|
+
|
|
511
|
+
```yaml
|
|
512
|
+
arguments:
|
|
513
|
+
BOARD: stm32f4_discovery
|
|
514
|
+
FW: firmware.elf
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Override at runtime — three ways, in increasing precedence order:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
# 1. config default (declared above)
|
|
521
|
+
|
|
522
|
+
# 2. environment variable — same name as the argument key
|
|
523
|
+
BOARD=stm32f7_discovery hector run
|
|
524
|
+
|
|
525
|
+
# 3. --set flag — highest precedence, beats env vars
|
|
526
|
+
hector run --set BOARD=stm32f7_discovery
|
|
527
|
+
hector run --set BOARD=stm32f7_discovery --set FW=release.elf
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
`--set` can also introduce keys not declared in `arguments:` — they are merged into the interpolation context and can be referenced in the YAML as `${KEY}`.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
### matrix
|
|
535
|
+
|
|
536
|
+
Declares the cross-product of values that make up the build/test space. Each combination is one complete simulation run. **hector does not run the whole matrix** — a single invocation runs one combination, selected with [`--job`](#selecting-a-matrix-combination---job); iterating the matrix is the CI's job. The `matrix:` block is the source of truth the CI reads to enumerate combinations.
|
|
537
|
+
|
|
538
|
+
```yaml
|
|
539
|
+
matrix:
|
|
540
|
+
variables:
|
|
541
|
+
BOARD: ["stm32f4_discovery", "stm32f7_discovery"]
|
|
542
|
+
FW: ["debug.elf", "release.elf"]
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
This declares four combinations (run one with e.g. `--job BOARD=stm32f4_discovery,FW=debug.elf`). An `exclude` block can remove specific combinations:
|
|
546
|
+
|
|
547
|
+
```yaml
|
|
548
|
+
matrix:
|
|
549
|
+
variables:
|
|
550
|
+
BOARD: ["stm32f4_discovery", "stm32f7_discovery"]
|
|
551
|
+
FW: ["debug.elf", "release.elf"]
|
|
552
|
+
exclude:
|
|
553
|
+
- BOARD: stm32f7_discovery
|
|
554
|
+
FW: debug.elf
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
A 1×1 matrix is a convenient way to keep the field present in the YAML while running only one job:
|
|
558
|
+
|
|
559
|
+
```yaml
|
|
560
|
+
matrix:
|
|
561
|
+
variables:
|
|
562
|
+
VARIANT: ["default"]
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
### modules
|
|
568
|
+
|
|
569
|
+
Defines reusable peripheral **types**. A module is built once per job, producing a loadable artifact (`.so` for Verilator, `.dll` for C#). Modules are referenced by name from a machine's `peripherals:` section.
|
|
570
|
+
|
|
571
|
+
#### kind: renode-verilator
|
|
572
|
+
|
|
573
|
+
Builds a verilated co-simulation peripheral into a `.so` inside the Renode Docker container (guaranteeing ABI compatibility). Loaded by Renode via its co-simulation interface. ([Renode docs: co-simulation with HDL](https://renode.readthedocs.io/en/latest/advanced/co-simulating-with-an-hdl-simulator.html))
|
|
574
|
+
|
|
575
|
+
```yaml
|
|
576
|
+
modules:
|
|
577
|
+
uartlite:
|
|
578
|
+
kind: renode-verilator # default if omitted
|
|
579
|
+
type: CoSimulated.CoSimulatedUART
|
|
580
|
+
source: "${INTEGRATION_DIR}/samples/uartlite"
|
|
581
|
+
cmake_flags: "" # optional extra CMake arguments
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
| Field | Required | Description |
|
|
585
|
+
|---|---|---|
|
|
586
|
+
| `kind` | No (default: `renode-verilator`) | `renode-verilator` or `csharp`. |
|
|
587
|
+
| `type` | Yes | Fully qualified Renode class name the artifact exposes. |
|
|
588
|
+
| `source` | Yes | Path to the CMake project root. `${INTEGRATION_DIR}` and `${RENODE_DIR}` are available. |
|
|
589
|
+
| `cmake_flags` | No | Extra flags appended to the `cmake` invocation. |
|
|
590
|
+
|
|
591
|
+
`${INTEGRATION_DIR}` resolves to the managed clone of [renode-verilator-integration](https://github.com/antmicro/renode-verilator-integration) inside `.hector/`. Its `samples/` directory contains ready-to-build examples.
|
|
592
|
+
|
|
593
|
+
**Multiple instances** of one module are declared as separate `peripherals:` entries. The framework copies the built `.so` per instance so each gets independent simulation state.
|
|
594
|
+
|
|
595
|
+
#### kind: csharp
|
|
596
|
+
|
|
597
|
+
Builds (or locates) a C# Renode peripheral plugin. The resulting `.dll` is loaded at emulation scope via `i @<path>` before any machine block, making the type available to all machines in the job. ([Renode docs: writing peripherals](https://renode.readthedocs.io/en/latest/advanced/writing-peripherals.html))
|
|
598
|
+
|
|
599
|
+
Two usage modes:
|
|
600
|
+
|
|
601
|
+
**Pre-built DLL** — point `source` directly at the `.dll`:
|
|
602
|
+
|
|
603
|
+
```yaml
|
|
604
|
+
modules:
|
|
605
|
+
my_periph:
|
|
606
|
+
kind: csharp
|
|
607
|
+
type: My.Namespace.MyPeripheral
|
|
608
|
+
source: prebuilt/my_periph.dll
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
**Build from source** — point `source` at a directory containing a `.csproj`. `dotnet build` runs inside the Renode container (which ships with Mono/dotnet):
|
|
612
|
+
|
|
613
|
+
```yaml
|
|
614
|
+
modules:
|
|
615
|
+
my_periph:
|
|
616
|
+
kind: csharp
|
|
617
|
+
type: My.Namespace.MyPeripheral
|
|
618
|
+
source: hw/cs/MyPeripheral
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
| Field | Required | Description |
|
|
622
|
+
|---|---|---|
|
|
623
|
+
| `kind` | Yes | Must be `csharp`. |
|
|
624
|
+
| `type` | Yes | Fully qualified C# class name as it will appear in the generated `.repl`. |
|
|
625
|
+
| `source` | Yes | Path to a `.csproj` directory **or** a pre-built `.dll` file. |
|
|
626
|
+
|
|
627
|
+
The `type` field is what you would write in a `.repl` entry — it must match the class exposed by the compiled DLL. Multiple peripherals in the same job can share one DLL (different `type` values, same `source`); Hector emits a single `i @<path>` import even when multiple instances reference the same DLL.
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
### hubs
|
|
632
|
+
|
|
633
|
+
Emulation-level connection objects instantiated at the Renode emulation scope, spanning all machines. Declared here and referenced from `connections:`.
|
|
634
|
+
|
|
635
|
+
```yaml
|
|
636
|
+
hubs:
|
|
637
|
+
uartlink: { type: uart }
|
|
638
|
+
canbus: { type: can }
|
|
639
|
+
lan: { type: ethernet }
|
|
640
|
+
irqline: { type: gpio }
|
|
641
|
+
usblink: { type: usb }
|
|
642
|
+
radio: { type: wireless }
|
|
643
|
+
ble_net: { type: ble }
|
|
644
|
+
mesh: { type: wisun }
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
| Type | Renode object | Operator | Description |
|
|
648
|
+
|---|---|---|---|
|
|
649
|
+
| `uart` | `CreateUARTHub` | `<->` | Symmetric UART medium; N machines can connect |
|
|
650
|
+
| `can` | `CreateCANHub` | `<->` | Symmetric CAN bus |
|
|
651
|
+
| `ethernet` | [`CreateSwitch`](https://renode.readthedocs.io/en/latest/networking/wired.html) | `<->` | Ethernet switch |
|
|
652
|
+
| `gpio` | `CreateGPIOConnector` | `->` | Directional GPIO link; one source and one destination |
|
|
653
|
+
| `usb` | [`CreateUSBConnector`](https://renode.readthedocs.io/en/latest/tutorials/usbip.html) | `->` | Asymmetric USB; device side → hub → controller side |
|
|
654
|
+
| `wireless` | [`CreateIEEE802_15_4Medium`](https://renode.readthedocs.io/en/latest/networking/wireless.html) | `<->` | IEEE 802.15.4 mesh (ZigBee, Thread, Matter) |
|
|
655
|
+
| `ble` | [`CreateBLEMedium`](https://renode.readthedocs.io/en/latest/networking/wireless.html) | `<->` | Bluetooth Low Energy |
|
|
656
|
+
| `wisun` | `CreateWiSUNMedium` | `<->` | Wi-SUN IEEE 802.11ah mesh (smart grid IoT) |
|
|
657
|
+
|
|
658
|
+
When any hub is present, Hector automatically sets `emulation SetGlobalQuantum` to 10 µs to ensure cross-machine communication is deterministic. Override with the top-level `quantum:` field.
|
|
659
|
+
|
|
660
|
+
#### USB connections
|
|
661
|
+
|
|
662
|
+
USB is asymmetric — the arrow direction picks the role. Both forms are equivalent:
|
|
663
|
+
|
|
664
|
+
```yaml
|
|
665
|
+
connections: |
|
|
666
|
+
# chained one-liner — device -> hub -> controller:
|
|
667
|
+
mcu.usb -> usblink -> host.usb
|
|
668
|
+
|
|
669
|
+
# …or the two endpoint→hub lines it expands to:
|
|
670
|
+
mcu.usb -> usblink # mcu is the USB DEVICE (periph → hub)
|
|
671
|
+
usblink -> host.usb # host is the USB CONTROLLER (hub → periph)
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Each `usb` hub is a **1-to-1 connector** and can carry only one device. The USB host controller, however, supports multiple devices — each on its own address. To attach multiple devices to the same host, declare one hub per device and point all of them at the same host peripheral:
|
|
675
|
+
|
|
676
|
+
```yaml
|
|
677
|
+
hubs:
|
|
678
|
+
usb_kbd: { type: usb }
|
|
679
|
+
usb_storage: { type: usb }
|
|
680
|
+
|
|
681
|
+
connections: |
|
|
682
|
+
board.usb_keyboard -> usb_kbd
|
|
683
|
+
usb_kbd -> board.usb_host
|
|
684
|
+
board.usb_msc -> usb_storage
|
|
685
|
+
usb_storage -> board.usb_host
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Hector generates a `connector Connect` for the device side and a `RegisterInController` for the host side of each hub. The host enumerates devices sequentially and assigns each a unique USB address.
|
|
689
|
+
|
|
690
|
+
#### Wireless positioning and range models
|
|
691
|
+
|
|
692
|
+
Both `wireless` and `ble` (and `wisun`) support optional per-machine positioning and packet delivery models ([Renode docs: wireless networking](https://renode.readthedocs.io/en/latest/networking/wireless.html)). Add raw Renode monitor commands in a machine's `commands:` block:
|
|
693
|
+
|
|
694
|
+
```yaml
|
|
695
|
+
machines:
|
|
696
|
+
nodeA:
|
|
697
|
+
commands: |
|
|
698
|
+
wireless SetPosition sysbus.radio 0.0 0.0 0.0
|
|
699
|
+
wireless SetMediumFunction RangeWirelessFunction 10.0
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Available medium functions: `SimpleWirelessFunction` (default, all delivered), `RangeWirelessFunction [maxRange]`, `RangeLossWirelessFunction [lossRange txRatio rxRatio]`.
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
### machines
|
|
707
|
+
|
|
708
|
+
Each entry under `machines:` becomes a named machine in the Renode emulation.
|
|
709
|
+
|
|
710
|
+
```yaml
|
|
711
|
+
machines:
|
|
712
|
+
sensor_mcu:
|
|
713
|
+
backend: renode # optional; 'renode' is the only supported backend
|
|
714
|
+
platform:
|
|
715
|
+
- platforms/boards/${BOARD}.repl
|
|
716
|
+
firmware: ${FW}
|
|
717
|
+
peripherals: { ... }
|
|
718
|
+
connections: |
|
|
719
|
+
...
|
|
720
|
+
mappings: |
|
|
721
|
+
...
|
|
722
|
+
commands: |
|
|
723
|
+
...
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
| Field | Description |
|
|
727
|
+
|---|---|
|
|
728
|
+
| `backend` | Must be `renode` (default). |
|
|
729
|
+
| `platform` | List of [`.repl` platform description](https://renode.readthedocs.io/en/latest/advanced/platform_description_format.html) files loaded in order. |
|
|
730
|
+
| `firmware` | ELF file loaded with [`sysbus LoadELF`](https://renode.readthedocs.io/en/latest/basic/machines.html). Omit or set to `none` to skip. |
|
|
731
|
+
| `peripherals` | Peripheral instances composed onto this machine. See below. |
|
|
732
|
+
| `connections` | Signal wiring scoped to this machine. Covers GPIO, IRQ, and bracket-range connections. Bare peripheral names are accepted. |
|
|
733
|
+
| `mappings` | Per-machine host resource bindings. Same syntax as global `mappings:`. |
|
|
734
|
+
| `commands` | Raw [Renode monitor](https://renode.readthedocs.io/en/latest/basic/monitor-syntax.html) commands inserted into the resc after hardware loads. |
|
|
735
|
+
|
|
736
|
+
> **Note:** `artifacts` is now a **top-level** key, not a per-machine one. See [artifacts](#artifacts).
|
|
737
|
+
|
|
738
|
+
#### peripherals
|
|
739
|
+
|
|
740
|
+
Each entry declares one peripheral instance on this machine. The `type` field is either a built-in Renode class name, a `renode-verilator` module name, or a `csharp` module name. Properties are written directly as flat keys alongside `type` and `at`.
|
|
741
|
+
|
|
742
|
+
```yaml
|
|
743
|
+
peripherals:
|
|
744
|
+
uart0: # instance name
|
|
745
|
+
type: uartlite # module name OR built-in Renode class
|
|
746
|
+
at: "sysbus <0x70000000, +0x100>"
|
|
747
|
+
frequency: 100000000 # peripheral property — passed through to the .repl
|
|
748
|
+
|
|
749
|
+
extra_ram:
|
|
750
|
+
type: Memory.MappedMemory
|
|
751
|
+
at: "sysbus <0x90000000, +0x10000>"
|
|
752
|
+
size: 0x10000
|
|
753
|
+
|
|
754
|
+
Btn:
|
|
755
|
+
type: Miscellaneous.Button
|
|
756
|
+
at: gpioPortC 13
|
|
757
|
+
invert: true
|
|
758
|
+
|
|
759
|
+
custom_accel:
|
|
760
|
+
type: my_accel # references a csharp module
|
|
761
|
+
at: "sysbus <0x40010000, +0x100>"
|
|
762
|
+
|
|
763
|
+
nvic:
|
|
764
|
+
type: IRQControllers.NVIC
|
|
765
|
+
at: sysbus 0xE000E000
|
|
766
|
+
priorityMask: 0xF0
|
|
767
|
+
init: |
|
|
768
|
+
someNvicSetupCommand
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
The `at` field is passed through as-is:
|
|
772
|
+
|
|
773
|
+
| Form | Example | Used for |
|
|
774
|
+
|---|---|---|
|
|
775
|
+
| Bus range | `sysbus <0x70000000, +0x100>` | Memory-mapped peripherals |
|
|
776
|
+
| GPIO port + index | `gpioPortC 13` | Button, LED registered at a pin slot |
|
|
777
|
+
| Named object | `sysbus` | CPU, NVIC, and other sysbus-attached peripherals |
|
|
778
|
+
| `none` | `none` | Peripherals with no bus registration (e.g. `CombinedInput`) |
|
|
779
|
+
|
|
780
|
+
The optional `init:` block contains Renode monitor commands that run when the peripheral is loaded during platform initialisation (same as `init:` in a native `.repl` file).
|
|
781
|
+
|
|
782
|
+
#### connections (per-machine)
|
|
783
|
+
|
|
784
|
+
All signal wiring for a machine lives here. Peripheral names are automatically scoped to the parent machine; hub names and already-qualified `machine.x` tokens are left unchanged. Three forms are supported:
|
|
785
|
+
|
|
786
|
+
**Simple GPIO / IRQ** — connects a peripheral output to a GPIO input or NVIC line:
|
|
787
|
+
```yaml
|
|
788
|
+
connections: |
|
|
789
|
+
Btn -> gpioPortC@13 # Button IRQ → GPIO port C pin 13
|
|
790
|
+
usart2 -> nvic@38 # UART interrupt → NVIC line 38
|
|
791
|
+
usart2 <-> uartlink # cross-machine hub
|
|
792
|
+
gpioPortA@7 -> irqline # GPIO pin → cross-machine GPIO hub
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
**Signal name** — `periph.Signal` notation for named IRQ outputs:
|
|
796
|
+
```yaml
|
|
797
|
+
connections: |
|
|
798
|
+
nvic.IRQ -> cpu@0 # NVIC IRQ output → CPU interrupt input
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**Bracket range** — compact multi-pin wiring (Renode `.repl` range syntax, passed through as-is):
|
|
802
|
+
```yaml
|
|
803
|
+
connections: |
|
|
804
|
+
gpioPortC[0-15] -> exti@[0-15] # all 16 GPIO pins → EXTI lines
|
|
805
|
+
exti[0-4] -> nvic@[6-10] # EXTI lines 0-4 → NVIC inputs 6-10
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
All three forms can be mixed freely in the same block.
|
|
809
|
+
|
|
810
|
+
**Peripheral-scoped wiring** — a `connections:` block may also be placed *on a peripheral* (including a nested grouping peripheral), to keep wiring next to the part it concerns. These lines are merged into the machine's wiring at build time — peripheral names are flat in the generated `.repl`, so scoping is purely organizational:
|
|
811
|
+
|
|
812
|
+
```yaml
|
|
813
|
+
machines:
|
|
814
|
+
blackpill:
|
|
815
|
+
peripherals:
|
|
816
|
+
mcu: # a grouping peripheral
|
|
817
|
+
type: ...
|
|
818
|
+
peripherals: { cpu: {...}, nvic: {...}, ... }
|
|
819
|
+
connections: | # wiring scoped to the mcu
|
|
820
|
+
nvic.IRQ -> cpu@0
|
|
821
|
+
button:
|
|
822
|
+
type: Miscellaneous.Button
|
|
823
|
+
at: gpioPortC 13
|
|
824
|
+
connections: | # machine-level wiring
|
|
825
|
+
button -> gpioPortC@13
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
Peripheral-scoped connections are always intra-machine; cross-machine hub links must use the machine-level `connections:` block.
|
|
829
|
+
|
|
830
|
+
#### mappings
|
|
831
|
+
|
|
832
|
+
Binds an emulated peripheral to a host resource. Same syntax as the global `mappings:` section.
|
|
833
|
+
|
|
834
|
+
```yaml
|
|
835
|
+
mappings: |
|
|
836
|
+
uart4 -> file:results/uart_output.log
|
|
837
|
+
eth0 -> tap:tap0
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
#### artifacts
|
|
841
|
+
|
|
842
|
+
A **top-level** (global) list of glob patterns. After each job completes, every
|
|
843
|
+
matching file is copied into `<output>/artifacts/job_<N>/…` (mirroring its source path,
|
|
844
|
+
so same-named files in different directories don't clash). This gives CI a single
|
|
845
|
+
directory to upload and a dashboard one place to look.
|
|
846
|
+
|
|
847
|
+
Two pattern styles are supported:
|
|
848
|
+
|
|
849
|
+
- **Bare filename** (no `/`, e.g. `*.xml`, `metrics_*.bin`) — searched **recursively**
|
|
850
|
+
across the whole project, skipping hidden dirs (the bundled Renode clone in `.hector/`,
|
|
851
|
+
`.git/`, …) and the artifacts output dir. Use this to grab outputs wherever they land.
|
|
852
|
+
- **Path pattern** (`results/*.log`, `logs/**/*.vcd`) — taken literally; `**` matches any
|
|
853
|
+
depth, a single `*` matches one level.
|
|
854
|
+
|
|
855
|
+
```yaml
|
|
856
|
+
artifacts:
|
|
857
|
+
- "*.xml" # every .xml anywhere (junit.xml, robot_output.xml, …)
|
|
858
|
+
- results/*.log # .log files directly under results/
|
|
859
|
+
- logs/**/*.vcd # .vcd files at any depth under logs/
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
Override the YAML list on the command line with `--artifacts` (repeatable); when given,
|
|
863
|
+
it replaces the config value — same precedence as `--renode-version`:
|
|
864
|
+
|
|
865
|
+
```bash
|
|
866
|
+
hector test --artifacts 'results/*.bin' --artifacts 'logs/**/*.log'
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
### connections (global)
|
|
872
|
+
|
|
873
|
+
The top-level `connections:` block is the natural home for cross-board wiring.
|
|
874
|
+
|
|
875
|
+
```yaml
|
|
876
|
+
connections: |
|
|
877
|
+
boardA.usart2 <-> uartlink <-> boardB.usart2
|
|
878
|
+
boardA.gpioPortA@7 -> irqline -> boardB.gpioPortB@4
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
See [Connection syntax](#connection-syntax) for the full grammar.
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
### mappings (global)
|
|
886
|
+
|
|
887
|
+
Binds emulated peripherals to host resources. Each line is `[machine.]peripheral -> backend:param`.
|
|
888
|
+
|
|
889
|
+
```yaml
|
|
890
|
+
mappings: |
|
|
891
|
+
boardA.uart4 -> file:results/uart.log
|
|
892
|
+
boardA.usart2 -> tcp:4567
|
|
893
|
+
boardA.uart1 -> pty:/workspace/ptys/uart1
|
|
894
|
+
boardB.eth0 -> tap:tap0
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
#### backend: file
|
|
898
|
+
|
|
899
|
+
Redirect UART output to a file using Renode's `CreateFileBackend` ([Renode docs: UART integration](https://renode.readthedocs.io/en/latest/host-integration/uart.html)). Subsequent runs append with a numeric suffix rather than overwriting.
|
|
900
|
+
|
|
901
|
+
```yaml
|
|
902
|
+
boardA.uart4 -> file:results/uart_output.log
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
The file path is relative to the project root. The directory must exist before the simulation starts.
|
|
906
|
+
|
|
907
|
+
#### backend: tcp
|
|
908
|
+
|
|
909
|
+
Expose a UART as a TCP socket terminal. With `--net=host` (the default Docker mode) the port is directly accessible from the host.
|
|
910
|
+
|
|
911
|
+
```yaml
|
|
912
|
+
boardA.uart4 -> tcp:4567
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
Connect with: `nc localhost 4567` or `telnet localhost 4567`.
|
|
916
|
+
|
|
917
|
+
Append `:raw` to suppress IAC telnet negotiation bytes (useful when connecting a tool like `picocom` via `nc`):
|
|
918
|
+
|
|
919
|
+
```yaml
|
|
920
|
+
boardA.uart4 -> tcp:4567:raw
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
#### backend: pty
|
|
924
|
+
|
|
925
|
+
Expose a UART as a PTY device (Linux/macOS only). Useful for connecting tools that expect a serial device path.
|
|
926
|
+
|
|
927
|
+
```yaml
|
|
928
|
+
boardA.uart4 -> pty:/workspace/ptys/uart4
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
**Note:** PTY devices are created inside the Docker container and are not visible on the host filesystem by default. For host-accessible serial ports, prefer `tcp`. To use PTY from the host, run the container with `--privileged` and bind-mount `/dev/pts`.
|
|
932
|
+
|
|
933
|
+
#### backend: tap
|
|
934
|
+
|
|
935
|
+
Connect an Ethernet peripheral to a Linux TAP network interface, bridging the emulated network to the host.
|
|
936
|
+
|
|
937
|
+
```yaml
|
|
938
|
+
boardA.eth0 -> tap:tap0
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
If the interface name is omitted, `tap0` is used. Requires `NET_ADMIN` capability in the container; with `--net=host` this is typically available automatically.
|
|
942
|
+
|
|
943
|
+
---
|
|
944
|
+
|
|
945
|
+
### build
|
|
946
|
+
|
|
947
|
+
Pre-simulation steps that produce the inputs a run/test needs — compile firmware, fetch a binary, generate a file. Each step runs **in a container** with the project bind-mounted (the same engine as the `shell` test type), once per job, *before* the machines are built. Steps run in order and the job aborts on the first failure (the sim depends on their output).
|
|
948
|
+
|
|
949
|
+
```yaml
|
|
950
|
+
build:
|
|
951
|
+
- name: Compile firmware
|
|
952
|
+
image: arm-gcc:13 # a toolchain image — no Renode involved
|
|
953
|
+
steps:
|
|
954
|
+
- script: make -C firmware
|
|
955
|
+
|
|
956
|
+
- name: Fetch bootloader # no image → the run's renode image (logged)
|
|
957
|
+
script: |
|
|
958
|
+
wget -O boot.bin https://example.com/boot.bin
|
|
959
|
+
|
|
960
|
+
machines:
|
|
961
|
+
mcu:
|
|
962
|
+
firmware: firmware/build/app.elf # the build output, read straight back
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
Same shape as a `shell` test (`name`, optional `image`, and `steps:` or a single-step `script:`). Build steps and the sim/tests all share the one bind-mounted project directory at the same path, so **a file a build step writes is simply there for the sim and tests** — reference it by its normal path, no copying or declaration. Under `--no-docker`, build steps run on the host (image ignored).
|
|
966
|
+
|
|
967
|
+
Build steps **report like tests**: each block becomes one entry (`type: "build"`) in the JUnit/JSON reports with its own `report.html`, alongside the test results. A failing build step marks the job failed and skips the simulation/tests.
|
|
968
|
+
|
|
969
|
+
> **Migration:** `build:` replaces the old host-side `prepare:` script. A one-line
|
|
970
|
+
> `prepare:` becomes a one-step `build:` (no `image:` → runs in the renode image, which
|
|
971
|
+
> has the usual shell tools). Old `prepare:`/`setup:` keys still parse, with a rename
|
|
972
|
+
> warning.
|
|
973
|
+
|
|
974
|
+
> **Tip:** containers may write build outputs as `root`. If later host steps (git, rm)
|
|
975
|
+
> trip on ownership, that's the known cause; gitignore generated outputs like `.hector/`.
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
### tests
|
|
980
|
+
|
|
981
|
+
A list of steps executed sequentially when running `hector test`. All steps run even if one fails. The process exits with code 1 if any step failed.
|
|
982
|
+
|
|
983
|
+
```yaml
|
|
984
|
+
tests:
|
|
985
|
+
- name: Firmware boots
|
|
986
|
+
type: robot
|
|
987
|
+
script: |
|
|
988
|
+
Create Terminal Tester sysbus.usart2
|
|
989
|
+
Wait For Line On Uart Board initialized timeout=30
|
|
990
|
+
|
|
991
|
+
- name: Check output
|
|
992
|
+
type: shell
|
|
993
|
+
script: |
|
|
994
|
+
grep -q "Board initialized" results/uart_output.log \
|
|
995
|
+
&& echo "PASS" || { echo "FAIL"; exit 1; }
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
There are two test types: **`robot`** (boots the emulation and drives it with Robot
|
|
999
|
+
Framework) and **`shell`** (runs a shell script in a container). By default every
|
|
1000
|
+
test runs *after* the simulation; a `shell` test that doesn't need Renode can opt out
|
|
1001
|
+
with `requires_sim: false` (see below).
|
|
1002
|
+
|
|
1003
|
+
#### type: robot
|
|
1004
|
+
|
|
1005
|
+
Each `robot` step can provide its keywords either inline via `script:` or as an external file via `file:`. Both are run with `renode-test` inside the Docker container. ([Renode docs: Robot Framework testing](https://renode.readthedocs.io/en/latest/introduction/testing.html))
|
|
1006
|
+
|
|
1007
|
+
##### Inline script
|
|
1008
|
+
|
|
1009
|
+
The `script` value is the body of a Robot Framework test case. Hector wraps it in a minimal `.robot` file that auto-loads the generated resc (which starts the emulation) and resets the emulation on teardown:
|
|
1010
|
+
|
|
1011
|
+
```yaml
|
|
1012
|
+
- name: UART echo
|
|
1013
|
+
type: robot
|
|
1014
|
+
script: |
|
|
1015
|
+
Create Terminal Tester sysbus.usart2
|
|
1016
|
+
Write Line To Uart ping
|
|
1017
|
+
Wait For Line On Uart pong timeout=10
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
##### External file
|
|
1021
|
+
|
|
1022
|
+
Point `file:` at an existing `.robot` file on the host. The file is passed to `renode-test` as-is; Hector injects the generated resc path as the Robot variable `${RESC}` so the file can load the emulation:
|
|
1023
|
+
|
|
1024
|
+
```yaml
|
|
1025
|
+
- name: Boot sequence
|
|
1026
|
+
type: robot
|
|
1027
|
+
file: tests/boot.robot
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
`tests/boot.robot`:
|
|
1031
|
+
```robot
|
|
1032
|
+
*** Settings ***
|
|
1033
|
+
Library Collections
|
|
1034
|
+
Suite Teardown Reset Emulation
|
|
1035
|
+
|
|
1036
|
+
*** Test Cases ***
|
|
1037
|
+
Boot sequence
|
|
1038
|
+
Execute Command include @${RESC}
|
|
1039
|
+
Create Terminal Tester sysbus.usart2
|
|
1040
|
+
Wait For Line On Uart Board initialized timeout=30
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
`${RESC}` resolves to the container-absolute path of the generated `.resc` for this job. If `file:` and `script:` are both present, `file:` takes precedence.
|
|
1044
|
+
|
|
1045
|
+
##### Loading a snapshot for one test (`snapshot`)
|
|
1046
|
+
|
|
1047
|
+
Provide a `snapshot:` path to load a saved Renode state instead of booting from the resc for that specific step. The CLI `--snapshot` flag overrides this field when present.
|
|
1048
|
+
|
|
1049
|
+
```yaml
|
|
1050
|
+
- name: Verify UART after boot
|
|
1051
|
+
type: robot
|
|
1052
|
+
snapshot: .hector/snapshots/post_boot.save
|
|
1053
|
+
script: |
|
|
1054
|
+
Execute Command mach set "boardA"
|
|
1055
|
+
Create Terminal Tester sysbus.usart2 machine=boardA
|
|
1056
|
+
Write Line To Uart ping
|
|
1057
|
+
Wait For Line On Uart pong timeout=5
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
This is useful when only one test in a suite needs a checkpoint — other tests still boot normally. `snapshot:` is only supported for inline `script:` tests; file-based (`file:`) tests ignore it.
|
|
1061
|
+
|
|
1062
|
+
##### Extra renode-test flags (`args`)
|
|
1063
|
+
|
|
1064
|
+
Use the optional `args` field to pass additional flags directly to the `renode-test` invocation. Accepts a list or a shell-style string:
|
|
1065
|
+
|
|
1066
|
+
```yaml
|
|
1067
|
+
- name: Verbose UART test
|
|
1068
|
+
type: robot
|
|
1069
|
+
args: "--loglevel DEBUG"
|
|
1070
|
+
script: |
|
|
1071
|
+
Create Terminal Tester sysbus.usart2
|
|
1072
|
+
Wait For Line On Uart Ready timeout=30
|
|
1073
|
+
|
|
1074
|
+
- name: Tagged subset
|
|
1075
|
+
type: robot
|
|
1076
|
+
args:
|
|
1077
|
+
- --include
|
|
1078
|
+
- smoke
|
|
1079
|
+
file: tests/full_suite.robot
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
Any flag that `renode-test` (Robot Framework) accepts can be passed here — `--include`, `--exclude`, `--loglevel`, `--variable`, `--listener`, etc.
|
|
1083
|
+
|
|
1084
|
+
##### Available Robot keywords
|
|
1085
|
+
|
|
1086
|
+
| Keyword | Description |
|
|
1087
|
+
|---|---|
|
|
1088
|
+
| `Create Terminal Tester sysbus.usart2 machine=boardA` | Set up a UART listener; `machine=` required in multi-board setups |
|
|
1089
|
+
| `Wait For Line On Uart <text> timeout=<n>` | Assert UART output within N seconds |
|
|
1090
|
+
| `Write Line To Uart <text>` | Send a line to a UART |
|
|
1091
|
+
| `Execute Command mach set "boardA"` | Change the active machine (multi-board) |
|
|
1092
|
+
| `Execute Command <monitor command>` | Send any Renode monitor command |
|
|
1093
|
+
| `Read From Uart <n>` | Read N bytes from UART |
|
|
1094
|
+
|
|
1095
|
+
Test results (HTML report, JUnit XML) are written to `results/` by default (override with `--output`).
|
|
1096
|
+
|
|
1097
|
+
**Note:** each `robot` step is an independent simulation run (a fresh Renode process). Put all assertions that must share the same simulation state inside one step.
|
|
1098
|
+
|
|
1099
|
+
#### type: shell
|
|
1100
|
+
|
|
1101
|
+
Each step's `script` runs **inside a one-off Docker container** with the project directory bind-mounted at the workspace path, so the script can read/write your files and artifacts. Scripts execute with `set -ex` (exit on error; echo each command), so `+ command` trace lines appear in the output; a non-zero exit fails the step.
|
|
1102
|
+
|
|
1103
|
+
```yaml
|
|
1104
|
+
- name: Validate log in a clean env
|
|
1105
|
+
type: shell
|
|
1106
|
+
image: python:3.12-slim # the container to run in
|
|
1107
|
+
script: |
|
|
1108
|
+
pip install --quiet pyyaml
|
|
1109
|
+
python3 tools/check_trace.py results/uart_output.log
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
- **`image:` is optional.** Omit it to use the run's renode image (which already has `python`/`wget`/shell tools) — hector logs which image it picked. Set it to pin a specific toolchain.
|
|
1113
|
+
- **`--no-docker`** runs the script on the host instead, ignoring `image:` (with a warning). See [Running without Docker](#running-without-docker---no-docker).
|
|
1114
|
+
|
|
1115
|
+
##### `requires_sim: false`
|
|
1116
|
+
|
|
1117
|
+
By default a `shell` test runs after the simulation and counts as needing it. Set `requires_sim: false` for a check that's independent of Renode (lint a file, validate a generated artifact, run a host tool). Such a test runs even when the config defines **no machines at all** — handy for build-and-check pipelines:
|
|
1118
|
+
|
|
1119
|
+
```yaml
|
|
1120
|
+
- name: Lint the generated config
|
|
1121
|
+
type: shell
|
|
1122
|
+
requires_sim: false
|
|
1123
|
+
image: python:3.12-slim
|
|
1124
|
+
script: |
|
|
1125
|
+
python3 -m yamllint .hector.yaml
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
A test that needs the simulation but finds no machine defined is skipped and marked failed, with a note telling you to add a machine or set `requires_sim: false`.
|
|
1129
|
+
|
|
1130
|
+
> **Migration:** the old `bash` and `docker` test types are now both `shell` (a `bash`
|
|
1131
|
+
> step → a `shell` step running on the host under `--no-docker`; a `docker` step → a
|
|
1132
|
+
> `shell` step with an `image:`). Old configs still run, with a rename warning.
|
|
1133
|
+
|
|
1134
|
+
---
|
|
1135
|
+
|
|
1136
|
+
### quantum
|
|
1137
|
+
|
|
1138
|
+
Override the emulation-wide time quantum (in seconds). The quantum controls how often Renode synchronises virtual time across machines and is only relevant in multi-machine jobs with hubs. ([Renode docs: time framework](https://renode.readthedocs.io/en/latest/advanced/time_framework.html))
|
|
1139
|
+
|
|
1140
|
+
```yaml
|
|
1141
|
+
quantum: 0.0001 # 100 µs
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
If absent and any hub is declared, the framework defaults to `0.00001` (10 µs). If no hubs are present, the quantum is not set and Renode uses its internal default.
|
|
1145
|
+
|
|
1146
|
+
---
|
|
1147
|
+
|
|
1148
|
+
### ci
|
|
1149
|
+
|
|
1150
|
+
An **optional** block describing CI pipelines (when and how to run this config in
|
|
1151
|
+
continuous integration). The CLI itself never acts on `ci:` — no `hector` command
|
|
1152
|
+
reads it — it only validates the block's shape so a misconfiguration is caught
|
|
1153
|
+
early. The field is consumed by [Hector CI](https://hector-ci.com), the companion
|
|
1154
|
+
server that runs this same `.hector.yaml` (there is no separate `hector-ci.yml`);
|
|
1155
|
+
its full reference lives with that product. A minimal shape:
|
|
1156
|
+
|
|
1157
|
+
```yaml
|
|
1158
|
+
ci:
|
|
1159
|
+
embedded-tests:
|
|
1160
|
+
when:
|
|
1161
|
+
branch: [main, "release/*"]
|
|
1162
|
+
event: [push, pull_request, manual]
|
|
1163
|
+
reporters: [json, junit]
|
|
1164
|
+
timeout: 30m
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
If you only ever drive `hector` yourself (locally or from your own CI runner),
|
|
1168
|
+
you can omit `ci:` entirely.
|
|
1169
|
+
|
|
1170
|
+
---
|
|
1171
|
+
|
|
1172
|
+
## Connection syntax
|
|
1173
|
+
|
|
1174
|
+
Pin and line numbers use Renode's `@` notation throughout.
|
|
1175
|
+
|
|
1176
|
+
### Intra-machine signal wiring
|
|
1177
|
+
|
|
1178
|
+
Connects the output of one peripheral to the input of another on the same machine. Generates a Renode [platform description](https://renode.readthedocs.io/en/latest/advanced/platform_description_format.html) **updating entry** keyed on the source peripheral.
|
|
1179
|
+
|
|
1180
|
+
```
|
|
1181
|
+
<machine>.<source> -> <machine>.<dest>@<pin>
|
|
1182
|
+
<machine>.<source>@<srcPin> -> <machine>.<dest>@<pin>
|
|
1183
|
+
<machine>.<source>.<Signal> -> <machine>.<dest>@<pin>
|
|
1184
|
+
<machine>.<source>[<range>] -> <machine>.<dest>@[<range>]
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
Inside a per-machine `connections:` block the machine prefix is optional — bare names are scoped to the parent machine automatically:
|
|
1188
|
+
|
|
1189
|
+
```yaml
|
|
1190
|
+
connections: |
|
|
1191
|
+
button -> gpioPortC@13 # peripheral → GPIO pin
|
|
1192
|
+
gpioPortA@5 -> led@0 # GPIO pin → LED input
|
|
1193
|
+
nvic.IRQ -> cpu@0 # named signal output
|
|
1194
|
+
gpioPortC[0-15] -> exti@[0-15] # bracket range: 16 pins in one line
|
|
1195
|
+
exti[0-4] -> nvic@[6-10] # bracket range with offset destination
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
### Cross-machine symmetric hub (uart / can / ethernet / wireless / ble / wisun)
|
|
1199
|
+
|
|
1200
|
+
Chained two-endpoint shorthand, or one endpoint↔hub line each — equivalent:
|
|
1201
|
+
```
|
|
1202
|
+
<nodeA>.<periph> <-> <hub> <-> <nodeB>.<periph>
|
|
1203
|
+
|
|
1204
|
+
# …or as separate lines (and the only form for 3+ endpoints):
|
|
1205
|
+
boardA.usart2 <-> uartlink
|
|
1206
|
+
boardB.usart2 <-> uartlink
|
|
1207
|
+
boardC.usart2 <-> uartlink
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### Cross-machine GPIO (directional)
|
|
1211
|
+
|
|
1212
|
+
Left side is the SOURCE, right side is the DESTINATION. Chained one-liner, or the two
|
|
1213
|
+
endpoint→hub lines it expands to — equivalent:
|
|
1214
|
+
```
|
|
1215
|
+
<nodeA>.<port>@<pin> -> <gpiohub> -> <nodeB>.<port>@<pin>
|
|
1216
|
+
|
|
1217
|
+
# …or split:
|
|
1218
|
+
boardA.gpioPortA@7 -> irqline # source
|
|
1219
|
+
irqline -> boardB.gpioPortB@4 # destination
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
### USB (asymmetric)
|
|
1223
|
+
|
|
1224
|
+
Chained one-liner, or the two equivalent endpoint→hub lines:
|
|
1225
|
+
```
|
|
1226
|
+
<device_node>.<periph> -> <usbhub> -> <host_node>.<periph>
|
|
1227
|
+
|
|
1228
|
+
# …or split:
|
|
1229
|
+
<device_node>.<periph> -> <usbhub>
|
|
1230
|
+
<usbhub> -> <host_node>.<periph>
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
---
|
|
1234
|
+
|
|
1235
|
+
## Interpolation
|
|
1236
|
+
|
|
1237
|
+
`${NAME}` is expanded anywhere in the YAML (keys, values, strings, block scalars). Precedence (highest wins):
|
|
1238
|
+
|
|
1239
|
+
1. Matrix variables for the current job
|
|
1240
|
+
2. `arguments:` values (or env var override of the same name)
|
|
1241
|
+
3. Framework variables: `${RENODE_DIR}`, `${INTEGRATION_DIR}`
|
|
1242
|
+
|
|
1243
|
+
`${RENODE_DIR}` and `${INTEGRATION_DIR}` are container-absolute paths (`/workspace/.hector/renode` and `/workspace/.hector/renode-verilator-integration`). They are primarily useful in module `source:` fields.
|
|
1244
|
+
|
|
1245
|
+
**YAML quoting rule:** `${...}` inside a YAML flow sequence (`[...]`) or flow mapping (`{...}`) will cause a parse error because `{` is a reserved character in flow context. Use block style instead:
|
|
1246
|
+
|
|
1247
|
+
```yaml
|
|
1248
|
+
# Wrong — will fail to parse
|
|
1249
|
+
platform: [ platforms/boards/${BOARD}.repl ]
|
|
1250
|
+
|
|
1251
|
+
# Correct
|
|
1252
|
+
platform:
|
|
1253
|
+
- platforms/boards/${BOARD}.repl
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
---
|
|
1257
|
+
|
|
1258
|
+
## Generated artifacts
|
|
1259
|
+
|
|
1260
|
+
Everything the framework generates lives under `.hector/` and is safe to delete (it will be rebuilt on the next run). Add this directory to `.gitignore`.
|
|
1261
|
+
|
|
1262
|
+
```
|
|
1263
|
+
.hector/
|
|
1264
|
+
├── renode/ # managed Renode clone
|
|
1265
|
+
├── renode-verilator-integration/ # managed integration clone
|
|
1266
|
+
├── build/
|
|
1267
|
+
│ ├── modules/<name>/ # compiled .so / .dll per module
|
|
1268
|
+
│ └── instances/<machine>__<inst>.so # per-instance .so copies (verilator only)
|
|
1269
|
+
├── repl/
|
|
1270
|
+
│ └── job_<N>_<machine>.gen.repl # generated platform descriptions
|
|
1271
|
+
├── resc/
|
|
1272
|
+
│ └── job_<N>.resc # generated emulation scripts
|
|
1273
|
+
└── tests/
|
|
1274
|
+
└── job_<N>_test_<M>.robot # generated Robot Framework wrappers
|
|
1275
|
+
|
|
1276
|
+
results/ # test + simulation output (override with --output)
|
|
1277
|
+
├── build_0_<name>/ # one directory per build: step (reported like a test)
|
|
1278
|
+
│ └── report.html
|
|
1279
|
+
├── test_0_<name>/ # one directory per test (robot / shell alike)
|
|
1280
|
+
│ ├── report.html # human-readable report (all test types)
|
|
1281
|
+
│ ├── log.html # robot only: full interactive log
|
|
1282
|
+
│ └── robot_output.xml # robot only: native Robot Framework XML
|
|
1283
|
+
├── test_1_<name>/
|
|
1284
|
+
│ └── report.html
|
|
1285
|
+
├── artifacts/ # files collected by the top-level 'artifacts:' globs
|
|
1286
|
+
│ └── job_<N>/… # mirrors each source path (avoids name clashes)
|
|
1287
|
+
├── junit.xml # aggregated JUnit XML (--reporters junit)
|
|
1288
|
+
└── manifest.json # machine-readable result index (--reporters json)
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
`manifest.json` is the structured surface for dashboards / CI: one entry per test with
|
|
1292
|
+
its `status`, `duration`, `commands`, and relative paths to its detail artifacts
|
|
1293
|
+
(`report_html`, and for robot also `log_html` / `robot_xml`). Enable it with
|
|
1294
|
+
`--reporters junit --reporters json`.
|
|
1295
|
+
|
|
1296
|
+
---
|
|
1297
|
+
|
|
1298
|
+
## Complete example
|
|
1299
|
+
|
|
1300
|
+
```yaml
|
|
1301
|
+
version: "0.1"
|
|
1302
|
+
renode_version: "1.16.1"
|
|
1303
|
+
|
|
1304
|
+
matrix:
|
|
1305
|
+
variables:
|
|
1306
|
+
VARIANT: ["default"]
|
|
1307
|
+
|
|
1308
|
+
arguments:
|
|
1309
|
+
BOARD: stm32f4_discovery
|
|
1310
|
+
FW: stm32f4_test.elf
|
|
1311
|
+
|
|
1312
|
+
# Pre-sim build steps, run once per job (in containers) before the machines are built.
|
|
1313
|
+
build:
|
|
1314
|
+
- name: Fetch firmware
|
|
1315
|
+
script: | # no image → the run's renode image (has wget)
|
|
1316
|
+
wget -q https://example.com/${FW} -O ${FW}
|
|
1317
|
+
|
|
1318
|
+
artifacts:
|
|
1319
|
+
- results/*.log
|
|
1320
|
+
|
|
1321
|
+
modules:
|
|
1322
|
+
uartlite:
|
|
1323
|
+
kind: renode-verilator
|
|
1324
|
+
type: CoSimulated.CoSimulatedUART
|
|
1325
|
+
source: "${INTEGRATION_DIR}/samples/uartlite"
|
|
1326
|
+
|
|
1327
|
+
hubs:
|
|
1328
|
+
uartlink: { type: uart }
|
|
1329
|
+
irqline: { type: gpio }
|
|
1330
|
+
|
|
1331
|
+
machines:
|
|
1332
|
+
boardA:
|
|
1333
|
+
platform:
|
|
1334
|
+
- platforms/boards/${BOARD}.repl
|
|
1335
|
+
firmware: ${FW}
|
|
1336
|
+
peripherals:
|
|
1337
|
+
uart0:
|
|
1338
|
+
type: uartlite
|
|
1339
|
+
at: "sysbus <0x70000000, +0x100>"
|
|
1340
|
+
frequency: 100000000
|
|
1341
|
+
Btn:
|
|
1342
|
+
type: Miscellaneous.Button
|
|
1343
|
+
at: gpioPortC 13
|
|
1344
|
+
connections: |
|
|
1345
|
+
Btn -> gpioPortC@13
|
|
1346
|
+
mappings: |
|
|
1347
|
+
uart4 -> file:results/${VARIANT}_uart.log
|
|
1348
|
+
|
|
1349
|
+
boardB:
|
|
1350
|
+
platform:
|
|
1351
|
+
- platforms/boards/${BOARD}.repl
|
|
1352
|
+
firmware: ${FW}
|
|
1353
|
+
|
|
1354
|
+
connections: |
|
|
1355
|
+
boardA.usart2 <-> uartlink <-> boardB.usart2
|
|
1356
|
+
boardA.gpioPortA@7 -> irqline -> boardB.gpioPortB@4
|
|
1357
|
+
|
|
1358
|
+
tests:
|
|
1359
|
+
- name: Firmware boots
|
|
1360
|
+
type: robot
|
|
1361
|
+
script: |
|
|
1362
|
+
Create Terminal Tester sysbus.usart2
|
|
1363
|
+
Wait For Line On Uart Board initialized timeout=30
|
|
1364
|
+
|
|
1365
|
+
- name: UART echo
|
|
1366
|
+
type: robot
|
|
1367
|
+
script: |
|
|
1368
|
+
Create Terminal Tester sysbus.usart2
|
|
1369
|
+
Write Line To Uart ping
|
|
1370
|
+
Wait For Line On Uart pong timeout=10
|
|
1371
|
+
|
|
1372
|
+
- name: Check log
|
|
1373
|
+
type: shell
|
|
1374
|
+
script: |
|
|
1375
|
+
grep -q "Board initialized" results/${VARIANT}_uart.log \
|
|
1376
|
+
&& echo "PASS" || { echo "FAIL"; exit 1; }
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
Run it:
|
|
1380
|
+
|
|
1381
|
+
```bash
|
|
1382
|
+
hector run # simulate
|
|
1383
|
+
hector run --debug boardA:3333 # debug boardA
|
|
1384
|
+
hector test # run tests
|
|
1385
|
+
hector test --fail-fast # stop on first failure
|
|
1386
|
+
hector run --job VARIANT=default # run only one matrix combination
|
|
1387
|
+
BOARD=stm32f7_discovery hector run # override argument via env
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
---
|
|
1391
|
+
|
|
1392
|
+
## License
|
|
1393
|
+
|
|
1394
|
+
Hector (the CLI) is licensed under the **GNU Affero General Public License v3.0
|
|
1395
|
+
or later** (AGPL-3.0-or-later). The full text is in [LICENSE](LICENSE); see
|
|
1396
|
+
[LICENSING.md](LICENSING.md) for what that means in practice — notably that your
|
|
1397
|
+
own firmware, `.hector.yaml` configs, and test files are **not** derivative
|
|
1398
|
+
works of Hector and carry no license obligation from it.
|
|
1399
|
+
|
|
1400
|
+
A separate commercial license is available for the **Hector CI** server; contact
|
|
1401
|
+
`info@hector-ci.com`.
|