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
64
65#include <stdint.h>
66#include <string.h>
67
68#include "ra8_board_ek_ra8d2.h"
69#include "ra8_boot_entry.h"
70#include "ra8_cgc.h"
71#include "ra8_err.h"
72#include "ra8_gpio_constants.h"
73#include "ra8_isr.h"
74#include "ra8_log.h"
75#include "ra8_port_constants.h"
76#include "ra8_port_utils.h"
77#include "ra8_time.h"
78#include "ra8_usb.h"
79
80#ifndef RA8_OFF_TARGET
81#include "tx_api.h"
82#include "ux_api.h"
83#include "ux_dcd_ra8_usb.h"
84#include "ux_device_class_cdc_acm.h"
85#include "ux_device_stack.h"
86#include "ux_system.h"
87
88/* SysTick handler lives in libs/ra8_core/src/ra8_time.c -- the project's
89 * shared weak SysTick_Handler dispatches into ThreadX (via a weak
90 * extern to `_tx_timer_interrupt`) AND re-arms the USB storm-guard
91 * NVIC line (via a weak extern to `ux_dcd_ra8_usb_irq_reenable`), so
92 * no per-app override is needed. Closes Issue #8. */
93#endif
94
95/* -------------------------------------------------------------------------- */
96/* Pinout (FSP-aligned, EK-RA8D2 v1 User's Manual) */
97/* -------------------------------------------------------------------------- */
98
109
110/* -------------------------------------------------------------------------- */
111/* Tunables */
112/* -------------------------------------------------------------------------- */
113
124
125#ifndef RA8_OFF_TARGET
126
127/* -------------------------------------------------------------------------- */
128/* ThreadX worker + USBX pool storage */
129/* -------------------------------------------------------------------------- */
130
138
145
152
159static UX_SLAVE_CLASS_CDC_ACM* s_cdc_acm = UX_NULL;
160
165typedef struct {
166 volatile uint32_t loop_iter;
167 volatile uint32_t loop_cdc_null;
168 volatile uint32_t loop_pre_read;
169 volatile uint32_t loop_post_read;
170 volatile uint32_t loop_read_ok;
171 volatile uint32_t loop_read_zero;
172 volatile uint32_t loop_pre_write;
173 volatile uint32_t loop_post_write;
175
183
193static TX_SEMAPHORE s_cdc_active_sem;
194
195/* -------------------------------------------------------------------------- */
196/* USB descriptors (DEVICE + CONFIG + IAD + CDC interfaces + endpoints) */
197/* -------------------------------------------------------------------------- */
198
199/* VID/PID matches the prior bare-metal app (pid.codes test range). The
200 * configuration is one CDC ACM communications interface + one CDC data
201 * interface, with EP3 IN (interrupt) for notifications and EP2 OUT /
202 * EP1 IN (bulk, 64-byte MPS) for the data pipes. Layout per CDC 1.20
203 * sec 5 + USB 2.0 sec 9.6.
204 */
205static UCHAR s_device_framework_fs[] = {
206 /* Device descriptor (USB 2.0 sec 9.6.1) -- 18 bytes.
207 bcdUSB = 0x0200 (USB 2.0); macOS rejects IAD-based composite
208 devices that advertise USB 1.1 (IAD ECN was added in USB 2.0). */
209 0x12U,
210 0x01U,
211 0x00U,
212 0x02U,
213 0xEFU, /* class = MISC */
214 0x02U, /* subclass = common */
215 0x01U, /* protocol = IAD */
216 0x40U,
217 0x09U,
218 0x12U,
219 0x0AU,
220 0x00U,
221 0x00U,
222 0x01U,
223 0x01U,
224 0x02U,
225 0x03U,
226 0x01U,
227 /* Configuration descriptor (75 bytes total: 9 cfg + 8 IAD + 9 CCI +
228 5 hdr + 5 call-mgmt + 4 ACM + 5 union + 7 EP3 + 9 DCI + 7 EP2 +
229 7 EP1 = 75 = 0x4B). Host reads wTotalLength then requests that
230 many bytes; truncating to 0x43 silently dropped EP1 IN, which
231 caused USBX to dereference a NULL endpoint after SET_CONFIG and
232 escalate to lockup (PC=0xEFFFFFFE). */
233 0x09U,
234 0x02U,
235 0x4BU,
236 0x00U,
237 0x02U,
238 0x01U,
239 0x00U,
240 0x80U, /* bmAttributes = bus-powered (bit 7 reserved-1). Was 0xC0
241 (self-powered) which conflicted with bMaxPower=100mA. */
242 0x32U,
243 /* Interface association (CDC). */
244 0x08U,
245 0x0BU,
246 0x00U,
247 0x02U,
248 0x02U,
249 0x02U,
250 0x01U,
251 0x00U,
252 /* Communications interface (CDC ACM). */
253 0x09U,
254 0x04U,
255 0x00U,
256 0x00U,
257 0x01U,
258 0x02U,
259 0x02U,
260 0x01U,
261 0x00U,
262 /* CDC header functional descriptor. bcdCDC = 0x0120 (CDC 1.20). */
263 0x05U,
264 0x24U,
265 0x00U,
266 0x20U,
267 0x01U,
268 /* Call-management functional descriptor.
269 bmCapabilities = 0x01 (device handles call management itself);
270 bDataInterface=0x01 only makes sense if D0 is set. */
271 0x05U,
272 0x24U,
273 0x01U,
274 0x01U,
275 0x01U,
276 /* ACM functional descriptor. */
277 0x04U,
278 0x24U,
279 0x02U,
280 0x02U,
281 /* Union functional descriptor. */
282 0x05U,
283 0x24U,
284 0x06U,
285 0x00U,
286 0x01U,
287 /* Interrupt-IN endpoint (EP3 IN, 8-byte MPS, 255 ms poll). */
288 0x07U,
289 0x05U,
290 0x83U,
291 0x03U,
292 0x08U,
293 0x00U,
294 0xFFU,
295 /* Data-class interface. */
296 0x09U,
297 0x04U,
298 0x01U,
299 0x00U,
300 0x02U,
301 0x0AU,
302 0x00U,
303 0x00U,
304 0x00U,
305 /* Bulk-OUT endpoint (EP2 OUT, 64-byte MPS). */
306 0x07U,
307 0x05U,
308 0x02U,
309 0x02U,
310 0x40U,
311 0x00U,
312 0x00U,
313 /* Bulk-IN endpoint (EP1 IN, 64-byte MPS). */
314 0x07U,
315 0x05U,
316 0x81U,
317 0x02U,
318 0x40U,
319 0x00U,
320 0x00U,
321};
322
330static UCHAR s_string_framework[] = {
331 /* idx 1: "Brighton Sikarskie". */
332 0x09U,
333 0x04U,
334 0x01U,
335 0x12U,
336 'B',
337 'r',
338 'i',
339 'g',
340 'h',
341 't',
342 'o',
343 'n',
344 ' ',
345 'S',
346 'i',
347 'k',
348 'a',
349 'r',
350 's',
351 'k',
352 'i',
353 'e',
354 /* idx 2: "EK-RA8D2 CDC Echo!" (18 ASCII bytes). */
355 0x09U,
356 0x04U,
357 0x02U,
358 0x12U,
359 'E',
360 'K',
361 '-',
362 'R',
363 'A',
364 '8',
365 'D',
366 '2',
367 ' ',
368 'C',
369 'D',
370 'C',
371 ' ',
372 'E',
373 'c',
374 'h',
375 'o',
376 '!',
377 /* idx 3: serial. */
378 0x09U,
379 0x04U,
380 0x03U,
381 0x08U,
382 '0',
383 '0',
384 '0',
385 '0',
386 '0',
387 '0',
388 '0',
389 '1',
390};
391
392/* USBX LANGID descriptor 0x0409 (English-US), little-endian byte pair. */
393typedef enum : uint8_t {
397
404
405/* -------------------------------------------------------------------------- */
406/* CDC-ACM activate / deactivate callbacks */
407/* -------------------------------------------------------------------------- */
408
420static VOID demo_cdc_activate(VOID* cdc_instance)
421{
422 s_cdc_acm = (UX_SLAVE_CLASS_CDC_ACM*)cdc_instance;
423 /* USBX writes ux_slave_device_state = CONFIGURED in
424 * _ux_device_stack_configuration_set just before invoking this
425 * activate callback. Pin it here so any concurrent IRQ/poll-driven
426 * write in the dispatch worker observes the chapter-9 result and
427 * does not demote it back to ATTACHED, which would break the
428 * subsequent cdc_acm_read state gate. */
429 if (_ux_system_slave != UX_NULL) {
430 _ux_system_slave->ux_system_slave_device.ux_slave_device_state =
431 (unsigned long)UX_DEVICE_CONFIGURED;
432 }
433 /* Wake the demo loop -- it blocks on s_cdc_active_sem instead of
434 * polling s_cdc_acm with tx_thread_sleep, which never returned on
435 * this hardware. */
436 (void)tx_semaphore_put(&s_cdc_active_sem);
437 /* CDC bulk endpoints: EP2 OUT -> pipe 2, EP1 IN -> pipe 1. Enable
438 * the bridge's ISR-side auto-echo, which mirrors OUT data back on
439 * the IN pipe directly inside the ISR -- ~10x faster than the
440 * worker-thread _read/_write loop. demo_worker's _read/_write call
441 * is suppressed under the matching s_dcd_auto_echo_enable check so
442 * the two paths cannot race on the bulk-OUT pipe. */
444}
445
457static VOID demo_cdc_deactivate(VOID* cdc_instance)
458{
459 (void)cdc_instance;
460 s_cdc_acm = UX_NULL;
461}
462
463/* -------------------------------------------------------------------------- */
464/* Worker thread: bring USBX up + echo loop */
465/* -------------------------------------------------------------------------- */
466
482{
483 if (_ux_system_initialize(s_usbx_pool, k_demo_usbx_pool_bytes, UX_NULL, 0) != UX_SUCCESS) {
484 return UX_ERROR;
485 }
486 return _ux_device_stack_initialize((UCHAR*)UX_NULL,
487 0,
489 sizeof(s_device_framework_fs),
491 sizeof(s_string_framework),
494 UX_NULL);
495}
496
512{
513 UX_SLAVE_CLASS_CDC_ACM_PARAMETER cdc_params = {
514 .ux_slave_class_cdc_acm_instance_activate = demo_cdc_activate,
515 .ux_slave_class_cdc_acm_instance_deactivate = demo_cdc_deactivate,
516 .ux_slave_class_cdc_acm_parameter_change = UX_NULL,
517 };
518 static UCHAR s_class_name[] = "ux_slave_class_cdc_acm";
519
520 return _ux_device_stack_class_register(s_class_name,
521 _ux_device_class_cdc_acm_entry,
522 1,
523 0,
524 &cdc_params);
525}
526
544{
545 if (_ux_system_slave != UX_NULL) {
546 _ux_system_slave->ux_system_slave_device.ux_slave_device_state =
547 (unsigned long)UX_DEVICE_CONFIGURED;
548 }
549}
550
565static void demo_echo_iter(UCHAR* buf, ULONG cap)
566{
568 ULONG n = 0UL;
569 s_demo_diag.loop_pre_read++;
570 UINT read_status = _ux_device_class_cdc_acm_read(s_cdc_acm, buf, cap, &n);
571 s_demo_diag.loop_post_read++;
572 if (read_status != UX_SUCCESS) {
574 return;
575 }
576 s_demo_diag.loop_read_ok++;
577 if (n == 0UL) {
578 s_demo_diag.loop_read_zero++;
579 return;
580 }
581 s_demo_diag.loop_pre_write++;
582 if (_ux_device_class_cdc_acm_write(s_cdc_acm, buf, n, &n) != UX_SUCCESS) {
583 return;
584 }
585 s_demo_diag.loop_post_write++;
586 for (ULONG i = 0UL; i < n; i++) {
588 }
589}
590
602static VOID demo_worker(ULONG arg)
603{
604 (void)arg;
605
606 if (demo_usbx_stack_up() != UX_SUCCESS) {
607 return;
608 }
609 if (demo_cdc_class_register() != UX_SUCCESS) {
610 return;
611 }
613 return;
614 }
616 return;
617 }
618
619 UCHAR buf[k_demo_echo_buf_bytes];
620 (void)buf; /* Auto-echo owns the data path; buf reserved for future fallback. */
621 (void)demo_echo_iter; /* Kept for the optional non-auto-echo fallback path. */
622 while (1) {
623 s_demo_diag.loop_iter++;
624 if (s_cdc_acm == UX_NULL) {
625 s_demo_diag.loop_cdc_null++;
626 /* Block until demo_cdc_activate posts the semaphore. Avoids
627 * tx_thread_sleep, which we observed never returning on this
628 * silicon under polled-dispatch worker load. */
629 (void)tx_semaphore_get(&s_cdc_active_sem, TX_WAIT_FOREVER);
630 continue;
631 }
632 /* Auto-echo handles bulk OUT -> IN mirroring inside the ISR.
633 * Invoking demo_echo_iter here would race with auto-echo on the
634 * IN pipe and deliver out-of-order data for MPS-aligned packets. */
636 }
637}
638
639/* -------------------------------------------------------------------------- */
640/* ThreadX kernel entry: spawn the worker */
641/* -------------------------------------------------------------------------- */
642
654VOID tx_application_define(VOID* first_unused_memory)
655{
656 static CHAR s_semaphore_name[] = "cdc_active";
657 static CHAR s_thread_name[] = "usb_cdc_echo";
658
659 (void)first_unused_memory;
660 (void)tx_semaphore_create(&s_cdc_active_sem, s_semaphore_name, 0U);
664 0UL,
667 8U, /* priority */
668 8U, /* preempt threshold */
671}
672#endif /* !RA8_OFF_TARGET */
673
674/* -------------------------------------------------------------------------- */
675/* Startup helpers */
676/* -------------------------------------------------------------------------- */
677
687static void demo_panic_halt(void)
688{
689 while (1) {
690 __asm__ volatile("wfi");
691 }
692}
693
707[[nodiscard]] static ra8_err_t demo_pins_init(void)
708{
710 if (err != k_ra8_ok) {
711 return err;
712 }
713 /* P500 is the EK-RA8D2 USB-FS role-select GPIO per the v1 User's
714 * Manual section "USB Full Speed" (J11 connector): drive LOW for
715 * device mode, HIGH for host mode. Routing P500 to the USBFS
716 * peripheral function lets the USB module drive it as VBUSEN
717 * (host-mode VBUS supply enable, default HIGH) which keeps the chip
718 * in HOST mode and prevents enumeration. For this device-mode demo
719 * we instead claim P500 as a GPIO output and drive it LOW so the
720 * board's role-select circuitry presents a USB device to the host. */
722 if (err != k_ra8_ok) {
723 return err;
724 }
726 if (err != k_ra8_ok) {
727 return err;
728 }
730}
731
743void main(void)
744{
745 uint32_t cpuclk0_hz = 0U;
746
747 if (ra8_cgc_init() != k_ra8_ok) {
749 }
750
751 /* Bring up PLL2 -> USBCKCR / USBCKDIVCR so USBFS sees a spec-compliant
752 * 48 MHz reference (PLL2P 240 MHz / 5). Must run BEFORE any caller
753 * releases MSTPB11 (USBFS) -- the SREQ -> SRDY handshake silently
754 * hangs otherwise (HUM Ch 9 "Clock selection switching procedure"
755 * step 1). Without this the SIE never sees a 48 MHz clock and the
756 * host never enumerates the device. The init-order audit
757 * (scripts/checks/audit_init_order.py) requires CGC bring-up to
758 * land BEFORE peripheral inits like ra8_log_init, so the RTT
759 * heart-beat moves down to right after the time/board bring-up. */
762 }
765 }
766 if (ra8_time_init(cpuclk0_hz) != k_ra8_ok) {
768 }
771 }
772 if (demo_pins_init() != k_ra8_ok) {
774 }
775 ra8_log_init();
776 ra8_log_info("USBCDC", "usb_cdc_echo boot, CGC PLL2 enable OK");
777
779
780#ifndef RA8_OFF_TARGET
781 /* tx_kernel_enter is __noreturn -- it never comes back. */
782 tx_kernel_enter();
783#endif
784
786}
void main(void)
Secure fallback main entry point.
Definition main.c:37
static TX_THREAD s_demo_thread
Definition main.c:237
demo_config_t
Compile-time settings for the HTTPS client demo.
Definition main.c:87
@ k_demo_thread_stack
Demo thread stack.
Definition main.c:89
static UCHAR s_demo_stack[k_demo_thread_stack]
Definition main.c:238
void tx_application_define(void *first_unused_memory)
ThreadX system-define hook: build worker thread + byte pool.
Definition main.c:942
static UCHAR s_string_framework[]
USBX string descriptor table (vendor / product / serial).
Definition main.c:299
static UCHAR s_usbx_pool[k_wlun_usbx_pool_bytes]
USBX memory pool (USBX uses tx_byte_pool internally).
Definition main.c:193
usb_langid_byte_t
Definition main.c:358
@ k_usb_langid_en_us_lo
LANGID 0x0409 low byte.
Definition main.c:359
@ k_usb_langid_en_us_hi
LANGID 0x0409 high byte.
Definition main.c:360
static UCHAR s_device_framework_fs[]
Definition main.c:234
static UCHAR s_language_id_framework[]
USBX language-id table – US English.
Definition main.c:368
static void demo_panic_halt(void)
Halt forever in WFI – used as a panic stop on init failure.
Definition main.c:93
static const ra8_port_pin_t k_demo_pin_vbus
USB_FS VBUS pin, packed ra8_port_pin_t.
Definition main.c:153
static ra8_err_t demo_pins_init(void)
Route the four USB-FS pins to the USBFS controller.
Definition main.c:491
static const ra8_port_pin_t k_demo_pin_dp
USB_FS D+ pin, packed ra8_port_pin_t.
Definition main.c:157
static const ra8_port_pin_t k_demo_pin_vbusen
USB_FS VBUSEN pin, packed ra8_port_pin_t.
Definition main.c:155
static const ra8_port_pin_t k_demo_pin_dm
USB_FS D- pin, packed ra8_port_pin_t.
Definition main.c:159
static CHAR s_thread_name[]
Definition main.c:212
static UX_SLAVE_CLASS_CDC_ACM * s_cdc_acm
Active CDC-ACM class instance, captured by the activate callback.
Definition main.c:195
static TX_SEMAPHORE s_cdc_active_sem
Posted by the activate callback so the echo worker blocks on it instead of polling s_cdc_acm with tx_...
Definition main.c:205
static UINT demo_cdc_class_register(void)
Registers the CDC-ACM class against the device-stack configuration.
Definition main.c:511
static UINT demo_usbx_stack_up(void)
Brings USBX system + device stack up with the FS framework.
Definition main.c:481
static void demo_echo_iter(UCHAR *buf, ULONG cap)
One iteration of the CDC echo loop.
Definition main.c:565
volatile demo_diag_t s_demo_diag
Externally-readable counters for demo loop progress.
Definition main.c:182
static VOID demo_cdc_activate(VOID *cdc_instance)
CDC-ACM activate callback.
Definition main.c:420
@ k_demo_echo_buf_bytes
One bulk-FS packet per recv/send.
Definition main.c:121
@ k_demo_idle_ticks
Idle back-off when no class active.
Definition main.c:122
@ k_demo_usbx_pool_bytes
USBX pool: 32 KiB; CDC-ACM enum exhausts 16 KiB.
Definition main.c:120
static VOID demo_worker(ULONG arg)
Worker thread entry.
Definition main.c:602
static VOID demo_cdc_deactivate(VOID *cdc_instance)
CDC-ACM deactivate callback.
Definition main.c:457
static void demo_pin_configured_state(void)
Pin USBX peripheral state at CONFIGURED.
Definition main.c:543
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).
@ k_ra8_board_usbfs_pin_dm
P8_15 D-.
@ k_ra8_board_usbfs_pin_vbusen
P5_00 VBUSEN GPIO.
@ k_ra8_board_usbfs_pin_vbus
P4_07 VBUS sense.
@ k_ra8_board_usbfs_pin_dp
P8_14 D+.
Boot entry points shared between a vector table and its startup code.
High-level Clock Generation Circuit driver.
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
@ 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
ra8_err_t ra8_cgc_usbfs_clock_enable(void)
Bring up the USB-FS module clock (USBCKCR / USBCKDIVCR).
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
PSEL codes for peripheral pin routing on the RA8D2.
@ k_ra8_psel_usb_fs
10011b: USB Full-Speed.
NVIC + ICU IELSR allocator.
void ra8_isr_globals_enable(void)
Globally enable maskable interrupts (PRIMASK = 0).
Definition ra8_isr.c:439
Lightweight Logging Interface for ra8-firmware.
void ra8_log_init(void)
Initialise the logging backend.
Definition ra8_log.c:379
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
Typed Port / Pin Constants for the RA8D2 IOPORT Module.
ra8_port_pin_t
Packed (port << 8) | pin pin identifier.
@ k_ra8_level_low
Drive or read 0.
High-level GPIO helpers on top of the PORT + PFS register layer.
ra8_err_t ra8_gpio_output_init(ra8_port_pin_t pin, ra8_level_t init_level)
Configure a pin as a digital output and drive it to an initial level.
Definition gpio.c:88
ra8_err_t ra8_pfs_route_peripheral(ra8_port_pin_t pin, ra8_psel_t psel, const char *owner)
Route a pin to a non-IRQ peripheral function via PFS.PSEL.
Definition gpio.c:238
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
Native USB controller driver public API (device + host modes).
ra8_err_t ra8_usb_device_attach(ra8_usb_speed_t speed, bool attached)
Raise / drop the D+ pull-up to advertise the device to the host.
@ k_ra8_usb_speed_fs
Full-Speed controller (USBFS @ 0x40250000).
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
#define tx_thread_create
#define tx_thread_sleep
#define TX_NO_TIME_SLICE
char CHAR
ThreadX-compatible CHAR (host stub).
#define TX_AUTO_START
#define TX_WAIT_FOREVER
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
Opaque thread stand-in for the host build.
Demo-loop counters; read via JLink to localise stalls.
Definition main.c:165
USBX device-controller-driver (DCD) bridge to ra8_usb.
void ux_dcd_ra8_usb_auto_echo_enable(uint8_t out_pipe, uint8_t in_pipe)
Enable ISR-side auto-echo from a bulk OUT pipe to a bulk IN pipe.
ra8_err_t ux_dcd_ra8_usb_initialize(ra8_usb_speed_t speed)
Register the ra8_usb bridge as the active USBX DCD.