STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_self_test_devices.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "self_test.h"
6#include "self_test_devices.h"
7
8#define CHECK(condition) \
9 do { \
10 if (!(condition)) { \
11 fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #condition); \
12 exit(1); \
13 } \
14 } while (0)
15
16typedef struct {
17 unsigned calls;
18 uint16_t expected_id;
19 uint8_t identity;
20 uint16_t prom[8];
21 uint8_t status[2];
22 uint16_t errors;
24} fake_t;
25
26static self_test_reply_t exchange(void* context, uint16_t id, uint8_t mode,
27 uint32_t max_hz,
28 self_test_spi_frame_t* frames, uint8_t count,
29 uint32_t budget_ms) {
30 fake_t* fake = context;
31 ++fake->calls;
32 CHECK(id == fake->expected_id);
33 CHECK(budget_ms == 7);
34 for (uint8_t i = 0; i < count; ++i) {
35 for (uint8_t j = 0; j < 4; ++j)
36 CHECK(frames[i].rx[j] == 0);
37 }
39 CHECK(mode == 3 && max_hz == 20000000U && count == 8);
40 for (uint8_t i = 0; i < count; ++i) {
41 CHECK(frames[i].length == 3);
42 CHECK(frames[i].tx[0] == 0xA0 + 2 * i);
43 CHECK(frames[i].tx[1] == 0 && frames[i].tx[2] == 0);
44 frames[i].rx[1] = (uint8_t)(fake->prom[i] >> 8);
45 frames[i].rx[2] = (uint8_t)fake->prom[i];
46 }
47 } else if (id == SELF_TEST_DEVICE_RADIO) {
48 CHECK(mode == 0 && max_hz == 16000000U && count == 2);
49 const uint8_t status_cmd[4] = {0xC0, 0, 0, 0};
50 const uint8_t errors_cmd[4] = {0x17, 0, 0, 0};
51 CHECK(frames[0].length == 2 && frames[1].length == 4);
52 CHECK(!memcmp(frames[0].tx, status_cmd, 4));
53 CHECK(!memcmp(frames[1].tx, errors_cmd, 4));
54 frames[0].rx[1] = fake->status[0];
55 frames[1].rx[1] = fake->status[1];
56 frames[1].rx[2] = (uint8_t)(fake->errors >> 8);
57 frames[1].rx[3] = (uint8_t)fake->errors;
58 } else if (id == SELF_TEST_DEVICE_ETHERNET) {
59 const uint8_t command[4] = {0, 0x39, 0, 0};
60 CHECK(mode == 0 && max_hz == 80000000U && count == 1);
61 CHECK(frames[0].length == 4 && !memcmp(frames[0].tx, command, 4));
62 frames[0].rx[3] = fake->identity;
63 } else {
64 const uint8_t command[4] = {0x8F, 0, 0, 0};
65 CHECK(mode == 3 && max_hz == 10000000U && count == 1);
66 CHECK(frames[0].length == 2 && !memcmp(frames[0].tx, command, 4));
67 frames[0].rx[1] = fake->identity;
68 }
69 return fake->result;
70}
71
73 uint16_t id) {
74 fake->expected_id = id;
75 return self_test_devices_probe(devices, id, 1, 123, 7);
76}
77
78int main(void) {
79 fake_t fake = {.result = {SELF_TEST_PASS, 0}};
80 self_test_devices_t devices = {.exchange = exchange, .io_context = &fake};
85 devices.configured_mask = 0xFF;
92 CHECK(probe(&devices, &fake, UINT16_MAX).result ==
96 devices.exchange = NULL;
98 CHECK(fake.calls == 0);
99 devices.exchange = exchange;
100
101 const uint16_t ids[] = {SELF_TEST_DEVICE_IMU, SELF_TEST_DEVICE_HIGH_G,
103 const uint8_t identities[] = {0x6B, 0x32, 0x04};
104 for (unsigned i = 0; i < 3; ++i) {
105 fake.identity = identities[i];
106 CHECK(probe(&devices, &fake, ids[i]).result == SELF_TEST_PASS);
107 fake.identity ^= 1;
108 self_test_reply_t bad = probe(&devices, &fake, ids[i]);
110 CHECK(bad.diagnostic == (SELF_TEST_DIAG_IDENTITY | fake.identity));
111 fake.identity = 0;
112 CHECK(probe(&devices, &fake, ids[i]).result == SELF_TEST_FAIL);
113 fake.identity = 0xFF;
114 CHECK(probe(&devices, &fake, ids[i]).result == SELF_TEST_FAIL);
115 }
116
117 /* Independent manufacturer AN520 example: expected CRC is 0xB. */
118 const uint16_t reference[8] = {0x3132, 0x3334, 0x3536, 0x3738,
119 0x3940, 0x4142, 0x4344, 0x450B};
120 memcpy(fake.prom, reference, sizeof(reference));
123 fake.prom[7] = 0x45FB; /* Entire low byte excluded, not just CRC nibble. */
126 for (unsigned word = 0; word < 7; ++word) {
127 for (unsigned bit = 0; bit < 16; ++bit) {
128 memcpy(fake.prom, reference, sizeof(reference));
129 fake.prom[word] ^= (uint16_t)(1U << bit);
132 }
133 }
134 memcpy(fake.prom, reference, sizeof(reference));
135 fake.prom[7] ^= 1;
138 memset(fake.prom, 0, sizeof(fake.prom));
139 CHECK(probe(&devices, &fake, SELF_TEST_DEVICE_BAROMETER).diagnostic ==
141 memset(fake.prom, 0xFF, sizeof(fake.prom));
142 CHECK(probe(&devices, &fake, SELF_TEST_DEVICE_BAROMETER).diagnostic ==
144
145 for (unsigned mode = 2; mode <= 6; ++mode) {
146 fake.status[0] = fake.status[1] = (uint8_t)((mode << 4) | 4);
149 }
150 for (unsigned frame = 0; frame < 2; ++frame) {
151 const uint8_t bad_status[] = {0, 0xFF, 0x10, 0x70, 0x26, 0x28, 0x2A};
152 for (unsigned i = 0; i < sizeof(bad_status); ++i) {
153 fake.status[0] = fake.status[1] = 0x24;
154 fake.status[frame] = bad_status[i];
155 CHECK(probe(&devices, &fake, SELF_TEST_DEVICE_RADIO).diagnostic ==
156 (SELF_TEST_DIAG_RADIO_STATUS | bad_status[i]));
157 }
158 }
159 fake.status[0] = fake.status[1] = 0x24;
160 for (unsigned bit = 0; bit < 16; ++bit) {
161 fake.errors = (uint16_t)(1U << bit);
162 CHECK(probe(&devices, &fake, SELF_TEST_DEVICE_RADIO).diagnostic ==
163 (SELF_TEST_DIAG_RADIO_ERRORS | fake.errors));
164 }
165 fake.errors = 0;
166 /* A valid payload must never hide a failed/partial transport. */
167 const self_test_result_t failures[] = {
170 for (unsigned i = 0; i < sizeof(failures) / sizeof(failures[0]); ++i) {
171 fake.result = (self_test_reply_t){failures[i], 1234};
172 for (uint16_t id = 0; id <= SELF_TEST_DEVICE_RADIO; ++id) {
173 self_test_reply_t result = probe(&devices, &fake, id);
174 CHECK(result.result == failures[i] && result.diagnostic == 1234);
175 }
176 }
177 fake.result = (self_test_reply_t){SELF_TEST_PASS, 0};
178 fake.identity = 0x6B;
179 fake.expected_id = SELF_TEST_DEVICE_IMU;
180 self_test_t coordinator = {0};
181 self_test_config_t config = {.device_count = 1,
182 .devices = {{SELF_TEST_DEVICE_IMU, true}},
183 .probe_timeout_ms = 100,
184 .retry_interval_ms = 10,
185 .heartbeat_interval_ms = 1000,
186 .battery_interval_ms = 1000,
187 .battery_max_age_ms = 2000,
188 .callback_budget_ms = 7,
189 .tick_budget_ms = 21,
190 .probe_max_attempts = 2,
191 .publication_max_attempts = 2};
193 CHECK(self_test_init(&coordinator, &config, &port, &devices, 0));
194 self_test_tick(&coordinator, 0);
195 CHECK(self_test_status(&coordinator)->probes_outcome == SELF_TEST_PASS);
196 CHECK(self_test_reset(&coordinator, 1));
197 fake.identity = 0;
198 self_test_tick(&coordinator, 1);
199 CHECK(self_test_status(&coordinator)->devices[0].reply.result ==
201 puts("Device protocol checks passed");
202 return 0;
203}
static board_spi_device_t devices[BOARD_SPI_COUNT]
Definition board_devbus.c:7
static const emmc_port_t port
Definition emmc_stm32.c:210
#define NULL
Definition usbd_def.h:44
static const idle_config_t config
Definition idle_task.c:17
size_type count(const key_type &key) const
Definition json.hpp:19264
json_pointer result
Definition json.hpp:14064
bool self_test_init(self_test_t *t, const self_test_config_t *config, const self_test_port_t *port, void *context, uint32_t now_ms)
Definition self_test.c:98
void self_test_tick(self_test_t *t, uint32_t now_ms)
Definition self_test.c:244
bool self_test_reset(self_test_t *t, uint32_t now_ms)
Definition self_test.c:74
const self_test_status_t * self_test_status(const self_test_t *t)
Definition self_test.c:270
static self_test_reply_t reply(self_test_result_t result, int32_t diagnostic)
self_test_reply_t self_test_devices_probe(void *context, uint16_t device_id, uint8_t attempt, uint32_t now_ms, uint32_t budget_ms)
#define SELF_TEST_DIAG_IDENTITY
@ SELF_TEST_DEVICE_ETHERNET
@ SELF_TEST_DEVICE_IMU
@ SELF_TEST_DEVICE_GNSS
@ SELF_TEST_DEVICE_HIGH_G
@ SELF_TEST_DEVICE_NAND
@ SELF_TEST_DEVICE_RADIO
@ SELF_TEST_DEVICE_BAROMETER
@ SELF_TEST_DEVICE_EMMC
#define SELF_TEST_DIAG_RADIO_ERRORS
#define SELF_TEST_DIAG_RADIO_STATUS
#define SELF_TEST_DIAG_BLANK_PROM
self_test_result_t
@ SELF_TEST_FAIL
@ SELF_TEST_NOT_CONFIGURED
@ SELF_TEST_NOT_IMPLEMENTED
@ SELF_TEST_PENDING
@ SELF_TEST_PASS
@ SELF_TEST_TIMEOUT
self_test_reply_t result
uint8_t status[2]
uint16_t prom[8]
uint16_t expected_id
self_test_result_t result
int main()
static int failures
static std::vector< uint8_t > frame(uint8_t hi, uint8_t lo, size_t payload_len, uint8_t fill)
One wire packet: len(4, LE) then len bytes of body, where the first 4 body bytes are ty/packet_id(2)/...
#define CHECK(condition)
static self_test_reply_t exchange(void *context, uint16_t id, uint8_t mode, uint32_t max_hz, self_test_spi_frame_t *frames, uint8_t count, uint32_t budget_ms)
static self_test_reply_t probe(self_test_devices_t *devices, fake_t *fake, uint16_t id)