ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1
68
69#include <stddef.h>
70#include <stdint.h>
71
72#include "ra8_board_ek_ra8d2.h"
73#include "ra8_boot_entry.h"
74#include "ra8_cgc.h"
75#include "ra8_err.h"
76#include "ra8_eth_gptp.h"
77#include "ra8_etha.h"
78#include "ra8_isr.h"
79#include "ra8_mstp.h"
80#include "ra8_time.h"
81
83typedef enum : uint32_t {
84 k_tsn_baud = 115200U,
86 k_tsn_win_units = 125000U,
91
93typedef enum : uint8_t {
98} tsn_fmt_t;
99
101typedef enum : uint32_t {
103 k_tsn_ns_per_ms = 1000000U,
104 k_tsn_ns_per_sec = 1000000000U
106
113typedef enum : uint64_t {
114 k_tsn_sec_flatten_max = 18446744073ULL,
116
118typedef enum : uint8_t {
121} tsn_band_t;
122
126typedef enum : uint8_t {
128} tsn_gate_t;
129
130/* Console line fragments (kept short so each write is one shift-register
131 * fill; the periodic log is the only output path). */
132static const uint8_t k_tsn_tas_prefix[] = "tsn: tas_entries=";
133static const uint8_t k_tsn_cbs_prefix[] = "tsn: cbs_en=";
134static const uint8_t k_tsn_cbs_gate_sep[] = " gate=";
135static const uint8_t k_tsn_cyc_prefix[] = "tsn: tas_cycle=";
136static const uint8_t k_tsn_base_prefix[] = "tsn: gptp_adv_ns=";
137static const uint8_t k_tsn_base_sep[] = " sys_ms=";
138static const uint8_t k_tsn_crlf[] = "\r\n";
139static const uint8_t k_tsn_verdict_pass[] = "tsn: schedule PASS\r\n";
140static const uint8_t k_tsn_verdict_fail[] = "tsn: schedule FAIL\r\n";
141
149static void tsn_panic_halt(void)
150{
151 while (1) {
152 __asm__ volatile("wfi");
153 }
154}
155
167static void tsn_write(const uint8_t* data, uint32_t len)
168{
169 (void)ra8_board_uart_console_write(data, (size_t)len);
170}
171
186static uint32_t tsn_u32_to_dec(uint8_t* buf, uint32_t val)
187{
188 if (val == 0U) {
189 buf[0] = (uint8_t)'0';
190 return 1U;
191 }
192 uint8_t tmp[k_tsn_dec_u32_max];
193 uint32_t n = 0U;
194 uint32_t v = val;
195 while (v != 0U) {
196 tmp[n] = (uint8_t)('0' + (uint8_t)(v % (uint32_t)k_tsn_radix));
197 v = v / (uint32_t)k_tsn_radix;
198 n++;
199 }
200 for (uint32_t i = 0U; i < n; i++) {
201 buf[i] = tmp[n - 1U - i];
202 }
203 return n;
204}
205
216static void tsn_write_u32(uint32_t val)
217{
218 uint8_t buf[k_tsn_dec_u32_max];
219 const uint32_t n = tsn_u32_to_dec(buf, val);
220 tsn_write(buf, n);
221}
222
241static void tsn_write_u64(uint64_t val)
242{
243 uint8_t buf[k_tsn_dec_u64_max];
244 uint8_t tmp[k_tsn_dec_u64_max];
245 uint32_t n = 0U;
246 uint64_t v = val;
247 if (v == 0U) {
248 buf[0] = (uint8_t)'0';
249 tsn_write(buf, 1U);
250 return;
251 }
252 while (v != 0U) {
253 tmp[n] = (uint8_t)('0' + (uint8_t)(v % (uint64_t)k_tsn_radix));
254 v = v / (uint64_t)k_tsn_radix;
255 n++;
256 }
257 for (uint32_t i = 0U; i < n; i++) {
258 buf[i] = tmp[n - 1U - i];
259 }
260 tsn_write(buf, n);
261}
262
286static uint64_t tsn_flatten_ns(uint64_t sec, uint32_t nsec)
287{
288 if (sec > (uint64_t)k_tsn_sec_flatten_max) {
289 return UINT64_MAX;
290 }
291 return (sec * (uint64_t)k_tsn_ns_per_sec) + (uint64_t)nsec;
292}
293
315static bool tsn_check_time_base(void)
316{
317 uint64_t sec0 = 0U;
318 uint32_t nsec0 = 0U;
319 uint64_t sec1 = 0U;
320 uint32_t nsec1 = 0U;
321 bool ok = true;
322
323 const uint32_t ms0 = ra8_time_ms();
324 if (ra8_eth_gptp_get_time(k_ra8_gptp_timer_0, &sec0, &nsec0) != k_ra8_ok) {
325 ok = false;
326 }
328 if (ra8_eth_gptp_get_time(k_ra8_gptp_timer_0, &sec1, &nsec1) != k_ra8_ok) {
329 ok = false;
330 }
331 const uint32_t elapsed_ms = ra8_time_ms() - ms0;
332 const uint64_t ns0 = tsn_flatten_ns(sec0, nsec0);
333 const uint64_t ns1 = tsn_flatten_ns(sec1, nsec1);
334 const uint64_t advance_ns = (ns1 > ns0) ? (ns1 - ns0) : 0ULL;
335 const uint64_t expected_ns = (uint64_t)elapsed_ms * (uint64_t)k_tsn_ns_per_ms;
336 const uint64_t band_ns = (expected_ns * (uint64_t)k_tsn_tolerance_pct) / (uint64_t)k_tsn_pct_full;
337
338 tsn_write(k_tsn_base_prefix, (uint32_t)(sizeof(k_tsn_base_prefix) - 1U));
339 tsn_write_u64(advance_ns);
340 tsn_write(k_tsn_base_sep, (uint32_t)(sizeof(k_tsn_base_sep) - 1U));
341 tsn_write_u32(elapsed_ms);
342 tsn_write(k_tsn_crlf, (uint32_t)(sizeof(k_tsn_crlf) - 1U));
343
344 if (elapsed_ms == 0U) {
345 return false;
346 }
347 if (advance_ns > (expected_ns + band_ns)) {
348 ok = false;
349 }
350 if (advance_ns < (expected_ns - band_ns)) {
351 ok = false;
352 }
353 return ok;
354}
355
375static bool tsn_tas_entry_matches(uint8_t index, const ra8_etha_tas_entry_t* want)
376{
377 ra8_etha_tas_entry_t got = {};
379 return false;
380 }
381 if (got.gate_time_ns != want->gate_time_ns) {
382 return false;
383 }
384 return got.gate_open == want->gate_open;
385}
386
412static bool tsn_program_tas(void)
413{
414 bool ok = true;
415 static const ra8_etha_tas_entry_t entries[k_tsn_gate_entries] = {
416 {.gate_time_ns = (uint32_t)k_tsn_win_units, .gate_open = true},
417 {.gate_time_ns = (uint32_t)k_tsn_win_units, .gate_open = false},
418 };
420 queues[k_ra8_etha_tc_7].entries = entries;
421 queues[k_ra8_etha_tc_7].count = (uint16_t)k_tsn_gate_entries;
422
424 ok = false;
425 }
427 queues,
428 (uint8_t)k_tsn_gate_ptp_only,
429 (uint32_t)k_tsn_cycle_units,
430 0U);
431 if (sched_err != k_ra8_ok) {
432 ok = false;
433 }
434 /* Read every entry back: this is what makes the verdict mean something. */
435 for (uint8_t index = 0U; index < (uint8_t)k_tsn_gate_entries; ++index) {
436 if (!tsn_tas_entry_matches(index, &entries[index])) {
437 ok = false;
438 }
439 }
441 ok = false;
442 }
443 tsn_write(k_tsn_tas_prefix, (uint32_t)(sizeof(k_tsn_tas_prefix) - 1U));
445 tsn_write(k_tsn_crlf, (uint32_t)(sizeof(k_tsn_crlf) - 1U));
446 return ok;
447}
448
465static bool tsn_program_cbs(void)
466{
467 bool ok = true;
468 const ra8_etha_cbs_param_t param = {
469 .increment = (uint32_t)k_tsn_cbs_increment,
470 .upper_lim = (uint32_t)k_tsn_cbs_upper_lim,
471 };
473 ok = false;
474 }
475 uint8_t enabled = 0U;
476 uint8_t gate_open = 0U;
477 ra8_etha_cbs_param_t oper = {};
478 const ra8_err_t st_err =
479 ra8_etha_get_cbs_state(k_ra8_etha_port_0, k_ra8_etha_tc_2, &enabled, &gate_open, &oper);
480 if (st_err != k_ra8_ok) {
481 ok = false;
482 }
483 tsn_write(k_tsn_cbs_prefix, (uint32_t)(sizeof(k_tsn_cbs_prefix) - 1U));
484 tsn_write_u32((uint32_t)enabled);
485 tsn_write(k_tsn_cbs_gate_sep, (uint32_t)(sizeof(k_tsn_cbs_gate_sep) - 1U));
486 tsn_write_u32((uint32_t)gate_open);
487 tsn_write(k_tsn_crlf, (uint32_t)(sizeof(k_tsn_crlf) - 1U));
488 return ok;
489}
490
502static bool tsn_log_status(void)
503{
504 bool ok = true;
505 ra8_etha_status_t sts = {};
507 if (err != k_ra8_ok) {
508 ok = false;
509 }
510 tsn_write(k_tsn_cyc_prefix, (uint32_t)(sizeof(k_tsn_cyc_prefix) - 1U));
512 tsn_write(k_tsn_crlf, (uint32_t)(sizeof(k_tsn_crlf) - 1U));
513 return ok;
514}
515
528static bool tsn_run_cycle(void)
529{
530 bool ok = true;
531 if (!tsn_check_time_base()) {
532 ok = false;
533 }
534 if (!tsn_program_tas()) {
535 ok = false;
536 }
537 if (!tsn_program_cbs()) {
538 ok = false;
539 }
540 if (!tsn_log_status()) {
541 ok = false;
542 }
543 return ok;
544}
545
555static void tsn_setup_or_halt(void)
556{
557 uint32_t cpuclk0_hz = 0U;
558 if (ra8_cgc_init() != k_ra8_ok) {
560 }
563 }
564 if (ra8_mstp_init() != k_ra8_ok) {
566 }
567 if (ra8_time_init(cpuclk0_hz) != k_ra8_ok) {
569 }
572 }
575 }
576 if (ra8_cgc_eswclk_init() != k_ra8_ok) {
578 }
579}
580
601[[nodiscard]] static ra8_err_t tsn_arm_time_base(void)
602{
603 uint32_t eswclk_hz = 0U;
604 const ra8_err_t hz_err = ra8_cgc_eswclk_hz(&eswclk_hz);
605 if (hz_err != k_ra8_ok) {
606 return hz_err;
607 }
608 const ra8_eth_gptp_cfg_t cfg = {.clk_hz = eswclk_hz};
609 const ra8_err_t init_err = ra8_eth_gptp_init(&cfg);
610 if (init_err != k_ra8_ok) {
611 return init_err;
612 }
614}
615
635[[nodiscard]] static ra8_err_t tsn_arm(void)
636{
637 const ra8_err_t gptp_err = tsn_arm_time_base();
638 if (gptp_err != k_ra8_ok) {
639 return gptp_err;
640 }
641 const ra8_etha_config_t cfg = {
642 .initial_mode = k_ra8_etha_opc_config,
643 .eaeie0_mask = 0U,
644 .eaeie1_mask = 0U,
645 .eaeie2_mask = 0U,
646 };
647 return ra8_etha_init(k_ra8_etha_port_0, &cfg);
648}
649
650void main(void)
651{
654
655 if (tsn_arm() != k_ra8_ok) {
657 }
658
659 while (1) {
660 const bool healthy = tsn_run_cycle();
661 if (healthy) {
662 tsn_write(k_tsn_verdict_pass, (uint32_t)(sizeof(k_tsn_verdict_pass) - 1U));
663 } else {
664 tsn_write(k_tsn_verdict_fail, (uint32_t)(sizeof(k_tsn_verdict_fail) - 1U));
665 }
667 break;
668 }
669 ra8_delay_ms((uint32_t)k_tsn_period_ms);
670 }
672}
void main(void)
Secure fallback main entry point.
Definition main.c:37
static uint64_t tsn_flatten_ns(uint64_t sec, uint32_t nsec)
Flatten a GPTP sample to nanoseconds.
Definition main.c:286
static void tsn_write_u64(uint64_t val)
Log one unsigned 64-bit value as decimal ASCII.
Definition main.c:241
static bool tsn_log_status(void)
Read + log the ETHA port-0 status (TAS cycle-time monitor).
Definition main.c:502
static void tsn_panic_halt(void)
Park forever after a fatal init error.
Definition main.c:149
static void tsn_write(const uint8_t *data, uint32_t len)
Write a byte span to the SCI8 console, discarding the status.
Definition main.c:167
tsn_gate_t
EATASIGSC initial gate-state bitmap: bit q is queue q's gate.
Definition main.c:126
@ k_tsn_gate_ptp_only
Only queue 7 (PTP / control) starts open.
Definition main.c:127
static void tsn_setup_or_halt(void)
Core bring-up: CGC -> MSTP -> TIME -> console + LED.
Definition main.c:555
static const uint8_t k_tsn_verdict_fail[]
Definition main.c:140
static bool tsn_program_tas(void)
Program + arm the 802.1Qbv time-aware shaper on port 0.
Definition main.c:412
tsn_band_t
Accuracy band the gPTP advance must fall inside.
Definition main.c:118
@ k_tsn_pct_full
Denominator for the percentage above.
Definition main.c:120
@ k_tsn_tolerance_pct
Allowed drift vs SysTick, in percent.
Definition main.c:119
static const uint8_t k_tsn_crlf[]
Definition main.c:138
static const uint8_t k_tsn_verdict_pass[]
Definition main.c:139
static bool tsn_program_cbs(void)
Configure the 802.1Qav credit-based shaper on traffic class 2.
Definition main.c:465
tsn_fmt_t
Formatting + iteration constants.
Definition main.c:93
@ k_tsn_gate_entries
Gate-control-list entry count.
Definition main.c:97
@ k_tsn_dec_u32_max
Max decimal digits for a uint32_t.
Definition main.c:95
@ k_tsn_radix
Decimal serialiser radix.
Definition main.c:94
@ k_tsn_dec_u64_max
Max decimal digits for a uint64_t.
Definition main.c:96
static const uint8_t k_tsn_base_sep[]
Definition main.c:137
static ra8_err_t tsn_arm(void)
Bring up the gPTP time base and ETHA port 0 in CONFIG mode.
Definition main.c:635
static void tsn_write_u32(uint32_t val)
Log one unsigned 32-bit value as decimal ASCII.
Definition main.c:216
static ra8_err_t tsn_arm_time_base(void)
Start the gPTP time base the TAS scheduler references.
Definition main.c:601
static bool tsn_run_cycle(void)
Run one cycle: check the time base, program TAS + CBS, read status.
Definition main.c:528
static const uint8_t k_tsn_cbs_gate_sep[]
Definition main.c:134
static uint32_t tsn_u32_to_dec(uint8_t *buf, uint32_t val)
Serialise an unsigned 32-bit value into decimal ASCII.
Definition main.c:186
static const uint8_t k_tsn_base_prefix[]
Definition main.c:136
static bool tsn_tas_entry_matches(uint8_t index, const ra8_etha_tas_entry_t *want)
Read one TAS RAM entry back and compare it with what was programmed.
Definition main.c:375
static const uint8_t k_tsn_cyc_prefix[]
Definition main.c:135
tsn_const_t
Demo tunables (illustrative shaper values).
Definition main.c:83
@ k_tsn_baud
SCI8 console baud.
Definition main.c:84
@ k_tsn_win_units
Per-window gate time, nanoseconds.
Definition main.c:86
@ k_tsn_period_ms
Delay between cycles.
Definition main.c:85
@ k_tsn_cbs_increment
CBS credit increment (20-bit field).
Definition main.c:88
@ k_tsn_cbs_upper_lim
CBS upper credit limit (31-bit field).
Definition main.c:89
@ k_tsn_cycle_units
Full GCL cycle = 2 windows, ns.
Definition main.c:87
static bool tsn_check_time_base(void)
Verify the gPTP time base the TAS scheduler references is running.
Definition main.c:315
static const uint8_t k_tsn_cbs_prefix[]
Definition main.c:133
tsn_flatten_limit_t
Largest seconds value that can be flattened to nanoseconds.
Definition main.c:113
@ k_tsn_sec_flatten_max
floor(2^64 - 1 / 1e9).
Definition main.c:114
tsn_gptp_const_t
gPTP time-base check parameters.
Definition main.c:101
@ k_tsn_ns_per_ms
Nanoseconds in one millisecond.
Definition main.c:103
@ k_tsn_gptp_window_ms
SysTick-timed measurement window.
Definition main.c:102
@ k_tsn_ns_per_sec
Nanoseconds in one second.
Definition main.c:104
static const uint8_t k_tsn_tas_prefix[]
Definition main.c:132
Board-support layer for the Renesas EK-RA8D2 v1 evaluation kit.
ra8_err_t ra8_board_led_toggle(ra8_board_led_id_t led)
Toggle led's output state.
ra8_err_t ra8_board_led_init(ra8_board_led_id_t led)
Configure led as a digital output, initial level low (off).
@ k_ra8_board_led1
LED1, BLUE, P600 (jumper E27).
ra8_err_t ra8_board_uart_console_write(const uint8_t *data, size_t len)
Polled blocking write to the J-Link OB VCOM console.
ra8_err_t ra8_board_uart_console_init(uint32_t baud)
Configure SCI8 + PD02/PD03 as the debug-console UART.
Boot entry points shared between a vector table and its startup code.
High-level Clock Generation Circuit driver.
ra8_err_t ra8_cgc_eswclk_hz(uint32_t *out_hz)
Query the current ESWCLK (Ethernet Switch clock) frequency.
ra8_err_t ra8_cgc_get_clock_hz(ra8_clock_id_t id, uint32_t *out_hz)
Query the current frequency of a clock-tree domain.
Definition ra8_cgc.c:132
ra8_err_t ra8_cgc_eswclk_init(void)
Bring up the Ethernet Switch Module (ESWM / RMAC) clock.
@ k_ra8_clock_id_cpuclk0
Cortex-M85 CPUCLK0.
Definition ra8_cgc.h:70
ra8_err_t ra8_cgc_init(void)
Configure the clock tree to a safe default.
Definition ra8_cgc.c:727
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Ethernet Generic PTP Timer (GPTP) driver – HUM Ch 35.
ra8_err_t ra8_eth_gptp_timer_enable(ra8_gptp_timer_idx_t timer)
Start one timer unit (PTPTMEC.TEq).
ra8_err_t ra8_eth_gptp_get_time(ra8_gptp_timer_idx_t timer, uint64_t *out_sec, uint32_t *out_nsec)
Read one timer unit's 78-bit GPTP time.
ra8_err_t ra8_eth_gptp_init(const ra8_eth_gptp_cfg_t *cfg)
Power up the GPTP block and programme both timers' increment.
Per-port Ethernet Agent (ETHA) driver – HUM Ch 32 (p 1627-1702).
ra8_err_t ra8_etha_get_status(ra8_etha_port_t port, ra8_etha_status_t *out)
Read a port's status snapshot (mode + 3x error IRQ status + TAS).
Definition ra8_etha.c:228
ra8_err_t ra8_etha_init(ra8_etha_port_t port, const ra8_etha_config_t *cfg)
Initialise an ETHA port (MSTP gate + reset registers + initial mode).
Definition ra8_etha.c:167
@ k_ra8_etha_port_0
ETHA port 0 (base 0x403C_A000).
@ k_ra8_etha_tc_7
Highest priority / control / PTP.
@ k_ra8_etha_tc_count
Number of traffic classes per port.
@ k_ra8_etha_tc_2
AVB class A.
@ k_ra8_etha_opc_config
Enter CONFIG mode.
ra8_err_t ra8_etha_enable_tas(ra8_etha_port_t port, uint8_t enable)
Enable or disable the TAS scheduler (main switch).
ra8_err_t ra8_etha_tas_ram_reset(ra8_etha_port_t port)
Reset the TAS RAM and wait for it to report ready.
ra8_err_t ra8_etha_configure_cbs(ra8_etha_port_t port, ra8_etha_tc_t tc, uint8_t enable, const ra8_etha_cbs_param_t *param)
Configure the credit-based shaper (CBS) for one traffic class.
Definition ra8_etha.c:680
ra8_err_t ra8_etha_get_cbs_state(ra8_etha_port_t port, ra8_etha_tc_t tc, uint8_t *enabled, uint8_t *gate_open, ra8_etha_cbs_param_t *oper_param)
Read the operational CBS gate-state vector + per-class oper params.
Definition ra8_etha.c:718
ra8_err_t ra8_etha_set_tas_schedule(ra8_etha_port_t port, const ra8_etha_tas_queue_t *queues, uint8_t initial_gate_states, uint32_t cycle_time_ns, uint64_t start_time)
Programme the time-aware shaper (TAS / 802.1Qbv) gate lists.
ra8_err_t ra8_etha_read_tas_entry(ra8_etha_port_t port, uint8_t address, ra8_etha_tas_entry_t *out)
Read one TAS RAM entry back out of hardware.
@ k_ra8_gptp_timer_0
Timer unit 0 (registers at +0x0020).
NVIC + ICU IELSR allocator.
void ra8_isr_globals_enable(void)
Globally enable maskable interrupts (PRIMASK = 0).
Definition ra8_isr.c:439
Ref-counted Module Stop Control wrapper for the RA8D2.
ra8_err_t ra8_mstp_init(void)
Re-establish the ra8_mstp ref-count table from current hardware state.
Definition ra8_mstp.c:291
SysTick-based tick counter, delay and timestamp helpers.
ra8_err_t ra8_time_init(uint32_t cpu_hz)
Initialise SysTick for a 1 kHz tick interrupt.
Definition ra8_time.c:59
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
uint32_t ra8_time_ms(void)
Get the current 1 kHz tick count.
Definition ra8_time.c:108
Static configuration for ra8_eth_gptp_init.
Credit-based shaper parameters per traffic class.
Per-port ETHA configuration knobs.
Snapshot of an ETHA port's runtime state.
uint32_t tas_cycle
EATASCTM TAS cycle-time monitor.
One TAS (802.1Qbv) RAM entry, exactly as HUM Table 32.6 defines it.
bool gate_open
TAS.GS – true opens this queue's gate.
uint32_t gate_time_ns
TAS.GT – entry duration in ns, 28 bits.
The gate-control list of one descriptor queue.
const ra8_etha_tas_entry_t * entries
Gate list, or nullptr when count is 0.
uint16_t count
Entries for this queue (EATASENCi).