@samitouri / QOSamiQemu / commits / b3d4fc22f6

tests: Add qtests for FlexCAN

The tests do not test all of the FlexCAN emulator functionality. Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com> Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz> Message-id: 3d47746ef30f29c58fec10267bdce7dac2c756a7.1782140438.git.matyas.bobek@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Matyáš Bobek committed Jun 22, 2026 at 18:08 UTC b3d4fc22f62490a74dbd920f3e56377c144ae5ac
3 files changed +423
MAINTAINERS
+1
@@ -2114,6 +2114,7 @@ S: Maintained
2114 F: hw/net/can/flexcan.c
2115 F: hw/net/can/flexcan_regs.h
2116 F: include/hw/net/flexcan.h
2117 +F: tests/qtest/flexcan-test.c
2118
2119 EDU
2120 M: Jiri Slaby <jslaby@suse.cz>
tests/qtest/flexcan-test.c new
+421
@@ -0,0 +1,421 @@
1 +/*
2 + * QTests for FlexCAN CAN controller device model
3 + *
4 + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#include "qemu/osdep.h"
10 +#include "libqtest-single.h"
11 +
12 +#include "hw/net/flexcan.h"
13 +#include "hw/net/can/flexcan_regs.h"
14 +
15 +#define FSL_IMX6_CAN2_ADDR 0x02094000
16 +#define FSL_IMX6_CAN2_SIZE 0x4000
17 +#define FSL_IMX6_CAN1_ADDR 0x02090000
18 +#define FSL_IMX6_CAN1_SIZE 0x4000
19 +
20 +#define FC_QEMU_ARGS "-nographic -M sabrelite " \
21 + "-object can-bus,id=qcan0 " \
22 + "-machine canbus0=qcan0 -machine canbus1=qcan0"
23 +
24 +/* used for masking out unused/reserved bits */
25 +#define FC_MB_CNT_USED_MASK (~0xF080FFFFu)
26 +
27 +#define FCREG(BASE_ADDR, REG) ((BASE_ADDR) + offsetof(FlexcanRegs, REG))
28 +#define FCMB(BASE_ADDR, MB_IDX, WORD_IDX) (FCREG(BASE_ADDR, mbs) + \
29 + 0x10 * (MB_IDX) + 4 * (WORD_IDX))
30 +
31 +typedef struct FcTestFrame {
32 + uint32_t id;
33 + uint32_t data[2];
34 + uint8_t len;
35 + bool ide;
36 + bool rtr;
37 +
38 + /* rx only */
39 + bool expect_overrun;
40 +} FcTestFrame;
41 +
42 +static const FcTestFrame fc_test_frame_1 = {
43 + .id = 0x5AF,
44 + .len = 8,
45 + .data = {
46 + 0x01020304,
47 + 0x0A0B0C0D
48 + },
49 + .ide = false
50 +};
51 +
52 +static const FcTestFrame fc_test_frame_1_ide = {
53 + .id = 0x105AF5AF,
54 + .len = 8,
55 + .data = {
56 + 0x01020304,
57 + 0x0A0B0C0D
58 + },
59 + .ide = true
60 +};
61 +
62 +static void fc_reset(hwaddr ba, uint32_t mcr_flags)
63 +{
64 + /* disable */
65 + writel(FCREG(ba, mcr), 0xD890000F);
66 +
67 + /* enable in freeze mode */
68 + writel(FCREG(ba, mcr), 0x5980000F);
69 +
70 + /* soft reset */
71 + writel(FCREG(ba, mcr), 0x5980000F | FLEXCAN_MCR_SOFTRST);
72 +
73 + g_assert_cmpuint(readl(FCREG(ba, mcr)), ==, 0x5980000F);
74 + g_assert_cmpuint(readl(FCREG(ba, ctrl)), ==, 0);
75 + g_assert_cmpuint(readl(FCREG(ba, ctrl2)), ==, 0);
76 +
77 + writel(FCREG(ba, mcr), (0x5980000F & ~FLEXCAN_MCR_HALT) | mcr_flags);
78 + writel(FCREG(ba, ctrl2), FLEXCAN_CTRL2_RRS);
79 +
80 + /* initialize all mailboxes as rx inactive */
81 + for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
82 + writel(FCMB(ba, i, 0), FLEXCAN_MB_CODE_RX_INACTIVE);
83 + writel(FCMB(ba, i, 1), 0);
84 + writel(FCMB(ba, i, 2), 0);
85 + writel(FCMB(ba, i, 3), 0);
86 + }
87 +}
88 +
89 +static uint64_t fc_get_irqs(hwaddr ba)
90 +{
91 + return (uint64_t)readl(FCREG(ba, iflag1)) |
92 + ((uint64_t)readl(FCREG(ba, iflag2)) << 32);
93 +}
94 +
95 +static void fc_clear_irq(hwaddr ba, int idx)
96 +{
97 + if (idx >= 32) {
98 + writel(FCREG(ba, iflag2), (uint32_t)1 << (idx - 32));
99 + } else {
100 + writel(FCREG(ba, iflag1), (uint32_t)1 << idx);
101 + }
102 +
103 + g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << idx), ==, 0);
104 +}
105 +
106 +static void fc_setup_rx_mb(hwaddr ba, int mbidx)
107 +{
108 + writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_RX_EMPTY);
109 + writel(FCMB(ba, mbidx, 1), 0);
110 + /* the data value should be ignored for RX mb */
111 + writel(FCMB(ba, mbidx, 2), 0);
112 + writel(FCMB(ba, mbidx, 3), 0);
113 +
114 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==, FLEXCAN_MB_CODE_RX_EMPTY);
115 +}
116 +
117 +static void fc_tx(hwaddr ba, int mbidx, const FcTestFrame *frame)
118 +{
119 + g_assert_cmpuint(frame->len, <=, 8);
120 +
121 + writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_INACTIVE);
122 + uint32_t id = frame->ide ? frame->id : frame->id << 18;
123 + writel(FCMB(ba, mbidx, 1), id);
124 + writel(FCMB(ba, mbidx, 2), frame->data[0]);
125 + writel(FCMB(ba, mbidx, 3), frame->data[1]);
126 +
127 + uint32_t ctrl = FLEXCAN_MB_CODE_TX_DATA | FLEXCAN_MB_CNT_LENGTH(frame->len);
128 + if (frame->ide) {
129 + ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR;
130 + }
131 + if (frame->rtr) {
132 + ctrl |= FLEXCAN_MB_CNT_RTR;
133 + }
134 + writel(FCMB(ba, mbidx, 0), ctrl);
135 +
136 + /* check frame was transmitted */
137 + g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << mbidx),
138 + !=, 0);
139 +
140 + uint32_t xpectd_ctrl = (ctrl & ~FLEXCAN_MB_CODE_MASK) |
141 + FLEXCAN_MB_CODE_TX_INACTIVE;
142 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK, ==,
143 + xpectd_ctrl);
144 + /* other fields should stay unchanged */
145 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, id);
146 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
147 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
148 +}
149 +
150 +static void fc_rx_check(hwaddr ba, int mbidx, const FcTestFrame *frame)
151 +{
152 + uint32_t xpectd_ctrl = frame->expect_overrun ? FLEXCAN_MB_CODE_RX_OVERRUN
153 + : FLEXCAN_MB_CODE_RX_FULL;
154 + xpectd_ctrl |= FLEXCAN_MB_CNT_LENGTH(frame->len) | FLEXCAN_MB_CNT_SRR;
155 + if (frame->ide) {
156 + xpectd_ctrl |= FLEXCAN_MB_CNT_IDE;
157 + }
158 + if (frame->rtr) {
159 + xpectd_ctrl |= FLEXCAN_MB_CNT_RTR;
160 + }
161 +
162 + uint32_t xpectd_id = frame->ide ? frame->id : frame->id << 18;
163 +
164 + uint32_t ctrl = readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK;
165 + if ((ctrl & FLEXCAN_MB_CODE_MASK) == FLEXCAN_MB_CODE_RX_EMPTY) {
166 + fprintf(stderr, "expected frame (id=0x%X) not received\n", frame->id);
167 + }
168 +
169 + g_assert_cmpuint(ctrl, ==, xpectd_ctrl);
170 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, xpectd_id);
171 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
172 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
173 +}
174 +
175 +static void fc_check_empty_multi(hwaddr ba, int idx_count, int mbidxs[])
176 +{
177 + for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
178 + bool check_empty = true;
179 + int ctrl = readl(FCMB(ba, i, 0));
180 +
181 + for (int j = 0; j < idx_count; j++) {
182 + if (i == mbidxs[j]) {
183 + check_empty = false;
184 + }
185 + }
186 +
187 + if (check_empty) {
188 + if (!(ctrl == FLEXCAN_MB_CODE_RX_EMPTY ||
189 + ctrl == FLEXCAN_MB_CODE_RX_INACTIVE)) {
190 + g_assert_cmpuint(ctrl, ==, FLEXCAN_MB_CODE_RX_INACTIVE);
191 + }
192 + g_assert_cmpuint(readl(FCMB(ba, i, 1)), ==, 0);
193 + g_assert_cmpuint(readl(FCMB(ba, i, 2)), ==, 0);
194 + g_assert_cmpuint(readl(FCMB(ba, i, 3)), ==, 0);
195 + } else {
196 + g_assert_cmpuint(
197 + ctrl & FLEXCAN_MB_CODE_MASK, !=,
198 + FLEXCAN_MB_CODE_RX_INACTIVE
199 + );
200 + }
201 + }
202 +}
203 +
204 +static void fc_check_empty(hwaddr ba, int mbidx)
205 +{
206 + fc_check_empty_multi(ba, 1, &mbidx);
207 +}
208 +
209 +static void flexcan_test_linux_probe_impl(hwaddr fba)
210 +{
211 + /* -- test a Linux driver-like probe sequence -- */
212 + /* disable */
213 + writel(FCREG(fba, mcr), 0xD890000F);
214 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
215 + g_assert_cmpuint(readl(FCREG(fba, ctrl)), ==, 0);
216 +
217 + /* set bit in reserved field we do not implement (CTRL_CLK_SRC) */
218 + writel(FCREG(fba, ctrl), 0x00002000);
219 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
220 +
221 + /* enable in freeze mode */
222 + writel(FCREG(fba, mcr), 0x5980000F);
223 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x5980000F);
224 +
225 + /* enable Rx-FIFO */
226 + writel(FCREG(fba, mcr), 0x7980000F);
227 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
228 + g_assert_cmpuint(readl(FCREG(fba, ecr)), ==, 0);
229 +
230 + /* disable */
231 + writel(FCREG(fba, mcr), 0xF890000F);
232 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
233 +}
234 +
235 +static void flexcan_test_freeze_disable_interaction_impl(hwaddr fba)
236 +{
237 + /* -- test normal <=> freeze <=> disable transitions -- */
238 +
239 + /* leave freeze in disabled, FRZ_ACK should stay cleared */
240 + writel(FCREG(fba, mcr), 0xF890000F); /* disable */
241 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
242 + writel(FCREG(fba, mcr), 0xB890000F); /* by clearing FRZ */
243 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xB890000F);
244 +
245 + writel(FCREG(fba, mcr), 0xF890000F); /* disable */
246 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
247 + writel(FCREG(fba, mcr), 0xE890000F); /* by clearing HALT */
248 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xE890000F);
249 +
250 + writel(FCREG(fba, mcr), 0xF890000F); /* disable */
251 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
252 + writel(FCREG(fba, mcr), 0xA890000F); /* by clearing both */
253 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xA890000F);
254 +
255 + /* enter and leave freeze */
256 + writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
257 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
258 + writel(FCREG(fba, mcr), 0x3980000F); /* leave by clearing FRZ */
259 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x3080000F);
260 +
261 + writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
262 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
263 + writel(FCREG(fba, mcr), 0x6980000F); /* leave by clearing HALT */
264 + g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x6080000F);
265 +}
266 +
267 +static void flexcan_test_mailbox_io_impl(hwaddr ba_tx, hwaddr ba_rx)
268 +{
269 + /* -- test correct handling of mailbox IO -- */
270 + const int test_1_mbidx = 0;
271 + fc_reset(ba_tx,
272 + FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
273 + fc_reset(ba_rx,
274 + FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
275 +
276 + fc_setup_rx_mb(ba_rx, test_1_mbidx);
277 + fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1_ide);
278 + g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 1 << test_1_mbidx);
279 + fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1_ide);
280 + readl(FCREG(ba_rx, timer)); /* reset lock */
281 +
282 + writel(FCMB(ba_rx, test_1_mbidx, 0), 0);
283 + g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 0)), ==, 0);
284 + writel(FCMB(ba_rx, test_1_mbidx, 1), 0x99AABBCC);
285 + g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 1)), ==, 0x99AABBCC);
286 +}
287 +
288 +static void flexcan_test_dual_transmit_receive_impl(hwaddr ba_tx, hwaddr ba_rx)
289 +{
290 + /* -- test TX and RX between the two FlexCAN instances -- */
291 + const int test_1_mbidx = 50;
292 + const int test_rounds = 50;
293 +
294 + /* self-receive enabled on tx FC */
295 + fc_reset(ba_tx,
296 + FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
297 + fc_reset(ba_rx,
298 + FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
299 +
300 + /* tests self-receive on tx and reception on rx */
301 + fc_setup_rx_mb(ba_rx, test_1_mbidx);
302 + fc_check_empty(ba_rx, test_1_mbidx);
303 + fc_setup_rx_mb(ba_tx, test_1_mbidx + 1);
304 + fc_check_empty(ba_tx, test_1_mbidx + 1);
305 + g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
306 + g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
307 +
308 + fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1);
309 + fc_clear_irq(ba_tx, test_1_mbidx);
310 +
311 + fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1);
312 + fc_check_empty(ba_rx, test_1_mbidx);
313 + fc_rx_check(ba_tx, test_1_mbidx + 1, &fc_test_frame_1);
314 + int tx_non_empty_mbidxs[] = {test_1_mbidx, test_1_mbidx + 1};
315 +
316 + fc_check_empty_multi(ba_tx, 2, tx_non_empty_mbidxs);
317 + fc_clear_irq(ba_rx, test_1_mbidx);
318 + fc_clear_irq(ba_tx, test_1_mbidx + 1);
319 + readl(FCREG(ba_rx, timer)); /* reset lock */
320 +
321 + for (int ridx = 0; ridx < test_rounds; ridx++) {
322 + /* test extended IDs sent to all mailboxes */
323 + for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
324 + fc_setup_rx_mb(ba_rx, i);
325 + }
326 + fc_check_empty_multi(ba_rx, 0, NULL);
327 + g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
328 + g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
329 +
330 + for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
331 + fc_tx(ba_tx, i, &fc_test_frame_1_ide);
332 + }
333 + g_assert_cmpuint(fc_get_irqs(ba_rx), ==, UINT64_MAX);
334 + g_assert_cmpuint(fc_get_irqs(ba_tx), ==, UINT64_MAX);
335 + for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
336 + fc_rx_check(ba_rx, i, &fc_test_frame_1_ide);
337 + }
338 +
339 + /* reset interrupts */
340 + writel(FCREG(ba_rx, iflag1), UINT32_MAX);
341 + writel(FCREG(ba_rx, iflag2), UINT32_MAX);
342 + writel(FCREG(ba_tx, iflag1), UINT32_MAX);
343 + writel(FCREG(ba_tx, iflag2), UINT32_MAX);
344 + g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
345 + g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
346 + }
347 +}
348 +
349 +static void flexcan_test_tx_abort_impl(hwaddr ba)
350 +{
351 + /* -- test the TX abort feature -- */
352 + fc_reset(ba,
353 + FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
354 +
355 +
356 + for (int mbidx = 0; mbidx < FLEXCAN_MAILBOX_COUNT; mbidx++) {
357 + fc_tx(ba, mbidx, &fc_test_frame_1);
358 +
359 + writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_ABORT);
360 + g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==,
361 + FLEXCAN_MB_CODE_TX_INACTIVE);
362 + }
363 +}
364 +
365 +static void flexcan_test_freeze_disable_interaction(void)
366 +{
367 + qtest_start(FC_QEMU_ARGS);
368 + flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN1_ADDR);
369 + flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN2_ADDR);
370 + qtest_end();
371 +}
372 +
373 +static void flexcan_test_linux_probe(void)
374 +{
375 + qtest_start(FC_QEMU_ARGS);
376 + flexcan_test_linux_probe_impl(FSL_IMX6_CAN1_ADDR);
377 + flexcan_test_linux_probe_impl(FSL_IMX6_CAN2_ADDR);
378 + qtest_end();
379 +}
380 +
381 +static void flexcan_test_dual_transmit_receive(void)
382 +{
383 + qtest_start(FC_QEMU_ARGS);
384 + flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN1_ADDR,
385 + FSL_IMX6_CAN2_ADDR);
386 + flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN2_ADDR,
387 + FSL_IMX6_CAN1_ADDR);
388 + qtest_end();
389 +}
390 +
391 +static void flexcan_test_tx_abort(void)
392 +{
393 + qtest_start(FC_QEMU_ARGS);
394 + flexcan_test_tx_abort_impl(FSL_IMX6_CAN1_ADDR);
395 + flexcan_test_tx_abort_impl(FSL_IMX6_CAN2_ADDR);
396 + qtest_end();
397 +}
398 +
399 +static void flexcan_test_mailbox_io(void)
400 +{
401 + qtest_start(FC_QEMU_ARGS);
402 + flexcan_test_mailbox_io_impl(FSL_IMX6_CAN1_ADDR, FSL_IMX6_CAN2_ADDR);
403 + flexcan_test_mailbox_io_impl(FSL_IMX6_CAN2_ADDR, FSL_IMX6_CAN1_ADDR);
404 + qtest_end();
405 +}
406 +
407 +int main(int argc, char **argv)
408 +{
409 + g_test_init(&argc, &argv, NULL);
410 +
411 + qtest_add_func("/net/flexcan/linux_probe", flexcan_test_linux_probe);
412 + qtest_add_func("/net/flexcan/freeze_disable_interaction",
413 + flexcan_test_freeze_disable_interaction);
414 + qtest_add_func("/net/flexcan/dual_transmit_receive",
415 + flexcan_test_dual_transmit_receive);
416 + qtest_add_func("/net/flexcan/tx_abort",
417 + flexcan_test_tx_abort);
418 + qtest_add_func("/net/flexcan/mailbox_io", flexcan_test_mailbox_io);
419 +
420 + return g_test_run();
421 +}
tests/qtest/meson.build
+1
@@ -251,6 +251,7 @@ qtests_arm = \
251 (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
252 (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
253 (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \
254 + (config_all_devices.has_key('CONFIG_CAN_FLEXCAN') ? ['flexcan-test'] : []) + \
255 (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and
256 config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \
257 ['arm-cpu-features',