ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_eth_gwca_default.c
Go to the documentation of this file.
1
20
21#include <stdint.h>
22#include <string.h>
23
24#include "ra8_attributes.h"
25#include "ra8_check.h"
26#include "ra8_err.h"
27#include "ra8_eth_gwca.h"
29#include "ra8_ether_regs.h"
30#include "ra8_hw_err.h"
31#include "ra8_hw_intrinsics.h"
32#include "ra8_log.h"
33
34static const char* s_tag = "ETHGWC";
35
55{
56 enum : uint32_t {
57 k_ra8_ds_low_mask = 0xFFU,
58 k_ra8_ds_high_shift = 8U,
59 k_ra8_ds_high_mask = 0xFU,
60 };
61 return ((uint32_t)desc->ds_l & k_ra8_ds_low_mask) |
62 (((uint32_t)desc->ds_h & k_ra8_ds_high_mask) << k_ra8_ds_high_shift);
63}
64
100 uint8_t* out_frame,
101 uint32_t out_capacity,
102 uint32_t slot_bytes,
103 uint32_t* out_len)
104{
105 const uint8_t* const buf = priv_ra8_eth_gwca_decode_ptr(desc);
106 const uint32_t frame_ds = internal_decode_ds(desc);
107 if (buf == nullptr || frame_ds > out_capacity) {
109 }
110 (void)memcpy(out_frame, buf, (size_t)frame_ds);
111 *out_len = frame_ds;
112 enum : uint32_t {
113 k_ra8_ds_byte_mask = 0xFFU,
114 k_ra8_ds_high_shift = 8U,
115 k_ra8_ds_high_mask = 0xFU,
116 };
117 desc->ds_l = (uint8_t)(slot_bytes & k_ra8_ds_byte_mask);
118 desc->ds_h = (uint8_t)((slot_bytes >> k_ra8_ds_high_shift) & k_ra8_ds_high_mask);
119 desc->dt = (uint8_t)k_ra8_gwdcc_dt_fempty;
120 return k_ra8_ok;
121}
122
156 uint32_t depth,
157 uint32_t slot_bytes,
158 const uint8_t* pool)
159{
160 RA8_CHECK_NULL_PTR(chain, s_tag, "tx_ext_init: chain null");
161 RA8_CHECK_NULL_PTR(pool, s_tag, "tx_ext_init: pool null");
162 enum : uint32_t {
163 k_tx_ext_min_depth = 2U,
164 k_tx_ext_max_bytes = 2048U,
165 k_ds_low_mask = 0xFFU,
166 k_ds_high_shift = 8U,
167 k_ds_high_mask = 0xFU,
168 };
169 if (depth < k_tx_ext_min_depth) {
171 }
172 if (slot_bytes > k_tx_ext_max_bytes) {
174 }
175 enum : uintptr_t {
176 k_ptr_hi_shift = 32U,
177 k_ptr_hi_mask = 0xFFULL,
178 };
179 for (uint32_t i = 0U; i < (depth - 1U); ++i) {
180 (void)memset(&chain[i], 0, sizeof(ra8_gwca_ext_descriptor_t));
181 chain[i].dt = (uint8_t)k_ra8_gwdcc_dt_fempty;
182 chain[i].ds_l = (uint8_t)(slot_bytes & k_ds_low_mask);
183 chain[i].ds_h = (uint8_t)((slot_bytes >> k_ds_high_shift) & k_ds_high_mask);
184 const uintptr_t buf = (uintptr_t)pool + ((uintptr_t)i * (uintptr_t)slot_bytes);
185 chain[i].ptr_h = (uint8_t)(((uint64_t)buf >> k_ptr_hi_shift) & (uint64_t)k_ptr_hi_mask);
186 chain[i].ptr_l = (uint32_t)buf;
187 }
188 ra8_gwca_ext_descriptor_t* const term = &chain[depth - 1U];
189 (void)memset(term, 0, sizeof(ra8_gwca_ext_descriptor_t));
190 const uintptr_t head = (uintptr_t)&chain[0];
191 term->ptr_h = (uint8_t)(((uint64_t)head >> k_ptr_hi_shift) & (uint64_t)k_ptr_hi_mask);
192 term->ptr_l = (uint32_t)head;
193 term->dt = (uint8_t)k_ra8_gwdcc_dt_link;
194 return k_ra8_ok;
195}
196
221static void
222internal_tx_ext_rearm(ra8_gwca_ext_descriptor_t* chain, uint32_t depth, uint32_t queue_index)
223{
224 ra8_gwca_ext_descriptor_t* const term = &chain[depth - 1U];
225 if (term->dt != (uint8_t)k_ra8_gwdcc_dt_lempty) {
226 return;
227 }
228 enum : uintptr_t {
229 k_ptr_hi_shift = 32U,
230 k_ptr_hi_mask = 0xFFULL,
231 };
232 const uintptr_t head = (uintptr_t)&chain[0];
233 term->ptr_h = (uint8_t)(((uint64_t)head >> k_ptr_hi_shift) & (uint64_t)k_ptr_hi_mask);
234 term->ptr_l = (uint32_t)head;
235 term->dt = (uint8_t)k_ra8_gwdcc_dt_link;
236 (void)ra8_eth_gwca_reload_queue(queue_index);
237}
238
269{
270 ra8_err_t err = ra8_eth_gwca_init_ring(state->rx_chain, state->rx_depth, state->rx_slot_bytes);
271 RA8_RETURN_ON_ERROR(err, s_tag, "default_open: rx init_ring");
273 state->rx_depth,
274 state->rx_slot_bytes,
275 state->rx_pool);
276 RA8_RETURN_ON_ERROR(err, s_tag, "default_open: rx attach");
277 return internal_tx_ext_init(state->tx_chain,
278 state->tx_depth,
279 state->tx_slot_bytes,
280 state->tx_pool);
281}
282
307{
308 const ra8_eth_gwca_queue_cfg_t rx_cfg = {.priority = 0U,
309 .is_tx = false,
310 .stop_on_last = false,
311 .chain_head = state->rx_chain};
312 ra8_err_t err =
314 RA8_RETURN_ON_ERROR(err, s_tag, "default_open: rx config");
315 const ra8_eth_gwca_queue_cfg_t tx_cfg = {.priority = 0U,
316 .is_tx = true,
317 .stop_on_last = false,
318 .extended = true,
319 .chain_head = state->tx_chain};
320 return ra8_eth_gwca_configure_queue(state->linkfix_table, state->tx_queue_index, &tx_cfg);
321}
322
347{
350 if (err != k_ra8_ok) {
352 return err;
353 }
355 err = internal_default_open_rings(state);
356 if (err != k_ra8_ok) {
358 return err;
359 }
362 if (err != k_ra8_ok) {
364 return err;
365 }
368 if (cfg_err != k_ra8_ok) {
370 return cfg_err;
371 }
373 return k_ra8_ok;
374}
375
398{
399 RA8_CHECK_NULL_PTR(state, s_tag, "default_open: state null");
402 if (err != k_ra8_ok) {
404 return err;
405 }
407 err = internal_default_open_queues(state);
408 if (err != k_ra8_ok) {
410 return err;
411 }
413 state->rx_head = 0U;
414 state->tx_tail = 0U;
416 if (op_err != k_ra8_ok) {
418 return op_err;
419 }
421
422 /* Arm both queues now the GWCA is in OPERATION: BALR loads the
423 * chain base into the AXI address RAM so the GWCA starts scanning
424 * the descriptor chains (HUM Ch 34.3 "GWDCCi"). */
425 const ra8_err_t rx_reload = ra8_eth_gwca_reload_queue(state->rx_queue_index);
426 if (rx_reload != k_ra8_ok) {
428 return rx_reload;
429 }
430 const ra8_err_t tx_reload = ra8_eth_gwca_reload_queue(state->tx_queue_index);
431 if (tx_reload != k_ra8_ok) {
433 return tx_reload;
434 }
435 return k_ra8_ok;
436}
437
477 uint32_t ring_depth,
478 uint32_t queue_index,
479 uint32_t* cursor)
480{
481 ra8_gwca_basic_descriptor_t* const term = &chain[ring_depth - 1U];
482 if (term->dt != (uint8_t)k_ra8_gwdcc_dt_lempty) {
483 return;
484 }
485 /* Restore the LINK terminator (PTR -> chain[0], dt = LINK), reload
486 * the queue so the GWCA resumes from the chain base, and snap the
487 * software cursor to 0 so it tracks the GWCA's reset scan position. */
489 term->dt = (uint8_t)k_ra8_gwdcc_dt_link;
490 *cursor = 0U;
491 (void)ra8_eth_gwca_reload_queue(queue_index);
492}
493
512static uint32_t internal_tx_info1_hi(uint8_t mac_port)
513{
514 const uint32_t dv = (uint32_t)1U << (uint32_t)mac_port;
515 return ((dv << (uint32_t)k_ra8_gwca_info1_tx_dv_shift) & (uint32_t)k_ra8_gwca_info1_tx_dv_mask);
516}
517
546{
547 for (uint32_t i = 0U; i < k_ra8_eth_gwca_tx_done_spin; ++i) {
548#if defined(RA8_OFF_TARGET) && defined(UNIT_TEST)
549 if (ra8_fake_mmio_wait_eval(&state->tx_chain[0],
550 i,
551 (state->tx_chain[0].dt != (uint8_t)k_ra8_gwdcc_dt_fsingle))) {
552 return k_ra8_ok;
553 }
554#else
555 if (state->tx_chain[0].dt != (uint8_t)k_ra8_gwdcc_dt_fsingle) {
556 return k_ra8_ok;
557 }
558#endif
559 }
560 ra8_log_error(s_tag, "default_send: TX completion timeout");
562}
563
601ra8_eth_gwca_default_send(ra8_eth_gwca_default_state_t* state, const uint8_t* frame, uint32_t len)
602{
603 RA8_CHECK_NULL_PTR(state, s_tag, "default_send: state null");
604 RA8_CHECK_NULL_PTR(frame, s_tag, "default_send: frame null");
605 if (len == 0U) {
607 }
608 if (len > state->tx_slot_bytes) {
610 }
611 /* Re-arm if the GWCA idle-disabled the queue, then fill slot 0. */
613 enum : uint32_t {
614 k_ds_low_mask = 0xFFU,
615 k_ds_high_shift = 8U,
616 k_ds_high_mask = 0xFU,
617 };
618 ra8_gwca_ext_descriptor_t* const d = &state->tx_chain[0];
619 (void)memcpy(state->tx_pool, frame, (size_t)len);
620 d->ds_l = (uint8_t)(len & k_ds_low_mask);
621 d->ds_h = (uint8_t)((len >> k_ds_high_shift) & k_ds_high_mask);
624 d->dt = (uint8_t)k_ra8_gwdcc_dt_fsingle;
625 state->tx_tail = 0U;
626 /* DSB: the descriptor + frame buffer are Normal (SRAM) writes; the
627 * GWCA kick below is a Device write. Armv8-M does not order them
628 * without an explicit barrier (host no-op via the ra8_hw_intrinsics seam). */
629 ra8_hw_dsb();
630 const ra8_err_t reload_err = ra8_eth_gwca_reload_queue(state->tx_queue_index);
631 if (reload_err != k_ra8_ok) {
632 return reload_err;
633 }
634 const ra8_err_t kick_err = ra8_eth_gwca_kick_tx(state->tx_queue_index);
635 if (kick_err != k_ra8_ok) {
636 return kick_err;
637 }
638 /* Block until the GWCA writes slot 0 back (FSINGLE -> FEMPTY) or the spin
639 * budget is exhausted; extracted so this send stays under the complexity cap. */
640 return internal_wait_tx0_done(state);
641}
642
670 uint8_t* out_frame,
671 uint32_t out_capacity,
672 uint32_t* out_len)
673{
674 RA8_CHECK_NULL_PTR(state, s_tag, "default_recv: state null");
675 const ra8_err_t err = ra8_eth_gwca_rx_frame(state->rx_chain,
676 state->rx_depth,
677 &state->rx_head,
678 out_frame,
679 out_capacity,
680 state->rx_slot_bytes,
681 out_len);
682 if (err == k_ra8_err_no_data) {
684 state->rx_depth,
685 state->rx_queue_index,
686 &state->rx_head);
687 }
688 return err;
689}
690
721 uint32_t ring_depth,
722 uint32_t* head_idx,
723 uint8_t* out_frame,
724 uint32_t out_capacity,
725 uint32_t slot_bytes,
726 uint32_t* out_len)
727{
728 RA8_CHECK_NULL_PTR(chain, s_tag, "rx_frame: chain null");
729 RA8_CHECK_NULL_PTR(head_idx, s_tag, "rx_frame: head_idx null");
730 RA8_CHECK_NULL_PTR(out_frame, s_tag, "rx_frame: out_frame null");
731 RA8_CHECK_NULL_PTR(out_len, s_tag, "rx_frame: out_len null");
732 if (out_capacity == 0U) {
734 }
735 uint32_t slot = 0U;
736 const ra8_err_t err = ra8_eth_gwca_find_slot(chain,
737 ring_depth,
739 *head_idx % (ring_depth - 1U),
740 &slot);
741 if (err != k_ra8_ok) {
742 return err;
743 }
744 const ra8_err_t drain_err =
745 internal_drain_rx_slot(&chain[slot], out_frame, out_capacity, slot_bytes, out_len);
746 if (drain_err != k_ra8_ok) {
747 return drain_err;
748 }
749 *head_idx = (slot + 1U) % (ring_depth - 1U);
750 return k_ra8_ok;
751}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_hw_timeout
Hardware timed out waiting for a flag or handshake.
Definition ra8_err.h:304
@ 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
volatile uint32_t g_ra8_eth_gwca_pre_step
Bench-side debug trail bumped inside internal_default_open_pre.
volatile uint32_t g_ra8_eth_gwca_open_step
Bench-side debug trail bumped by ra8_eth_gwca_default_open.
Ethernet CPU Agent (GWCA) driver.
ra8_err_t ra8_eth_gwca_bring_up(ra8_gwca_basic_descriptor_t *linkfix_table, uint32_t entry_count)
Bring the GWCA from RESET state up to OPERATION with a fresh LINKFIX table.
ra8_err_t ra8_eth_gwca_kick_tx(uint32_t queue_index)
Kick a TX queue – request the chip to start transmitting.
ra8_err_t ra8_eth_gwca_init_ring(ra8_gwca_basic_descriptor_t *chain, uint32_t ring_depth, uint32_t slot_bytes)
Initialise a descriptor chain as a ring of FEMPTY slots.
ra8_err_t ra8_eth_gwca_find_slot(const ra8_gwca_basic_descriptor_t *chain, uint32_t ring_depth, ra8_gwdcc_dt_t match_dt, uint32_t start_idx, uint32_t *out_index)
Find the next FEMPTY slot in a descriptor ring.
ra8_err_t ra8_eth_gwca_init(void)
Initialise GWCA.
ra8_err_t ra8_eth_gwca_set_operation_mode(ra8_gwmc_opc_t mode)
Transition the GWCA / ESWM state machine to a new OPC mode.
ra8_err_t ra8_eth_gwca_reload_queue(uint32_t queue_index)
Reload (arm) a descriptor queue by pulsing GWDCC[i].BALR.
ra8_err_t ra8_eth_gwca_configure_queue(ra8_gwca_basic_descriptor_t *linkfix_table, uint32_t queue_index, const ra8_eth_gwca_queue_cfg_t *cfg)
Wire a per-queue config into GWDCC[i] and the matching LINKFIX entry.
ra8_err_t ra8_eth_gwca_attach_buffers(ra8_gwca_basic_descriptor_t *chain, uint32_t ring_depth, uint32_t slot_bytes, uint8_t *pool)
Walk a ring and attach per-slot buffers from a static pool.
static void internal_rearm_queue_if_disabled(ra8_gwca_basic_descriptor_t *chain, uint32_t ring_depth, uint32_t queue_index, uint32_t *cursor)
Re-arm a descriptor queue if the GWCA has disabled it.
static uint32_t internal_decode_ds(const ra8_gwca_basic_descriptor_t *desc)
Reconstruct the 12-bit DS (descriptor size) field from a basic descriptor.
ra8_err_t ra8_eth_gwca_default_open(ra8_eth_gwca_default_state_t *state)
One-call GWCA bring-up for the default-state API.
static ra8_err_t internal_default_open_rings(ra8_eth_gwca_default_state_t *state)
Set up the RX + TX descriptor rings for the default-state API.
static ra8_err_t internal_drain_rx_slot(ra8_gwca_basic_descriptor_t *desc, uint8_t *out_frame, uint32_t out_capacity, uint32_t slot_bytes, uint32_t *out_len)
Copy out a filled RX slot and reset it to FEMPTY.
static ra8_err_t internal_default_open_queues(ra8_eth_gwca_default_state_t *state)
Program the RX + TX per-queue cfgs for the default-state API.
ra8_err_t ra8_eth_gwca_rx_frame(ra8_gwca_basic_descriptor_t *chain, uint32_t ring_depth, uint32_t *head_idx, uint8_t *out_frame, uint32_t out_capacity, uint32_t slot_bytes, uint32_t *out_len)
Dequeue one frame from an RX queue's descriptor ring.
ra8_err_t ra8_eth_gwca_default_recv(ra8_eth_gwca_default_state_t *state, uint8_t *out_frame, uint32_t out_capacity, uint32_t *out_len)
One-call RX: dequeue next frame.
static ra8_err_t internal_default_open_pre(ra8_eth_gwca_default_state_t *state)
Walk the bring-up sub-sequence (init/rings/bring_up/-> CONFIG).
static uint32_t internal_tx_info1_hi(uint8_t mac_port)
Compose the INFO1_hi word of a TX extended descriptor.
static void internal_tx_ext_rearm(ra8_gwca_ext_descriptor_t *chain, uint32_t depth, uint32_t queue_index)
Re-arm the extended TX queue if the GWCA has disabled it.
ra8_err_t ra8_eth_gwca_default_send(ra8_eth_gwca_default_state_t *state, const uint8_t *frame, uint32_t len)
One-call TX: enqueue an extended descriptor into slot 0 + kick.
static ra8_err_t internal_wait_tx0_done(ra8_eth_gwca_default_state_t *state)
Block until the GWCA writes TX slot 0 back (FSINGLE -> FEMPTY).
static ra8_err_t internal_tx_ext_init(ra8_gwca_ext_descriptor_t *chain, uint32_t depth, uint32_t slot_bytes, const uint8_t *pool)
Initialise the extended (16-byte) TX descriptor chain.
Cross-TU shared surface for the ra8_eth_gwca driver split.
@ k_ra8_eth_gwca_tx_done_spin
TX descriptor write-back poll budget.
uint8_t * priv_ra8_eth_gwca_decode_ptr(const ra8_gwca_basic_descriptor_t *desc)
Decode a descriptor's 40-bit PTR back to a host pointer.
void priv_ra8_eth_gwca_set_linkfix_entry(ra8_gwca_basic_descriptor_t *entry, const void *chain_head)
Promote a LINKFIX entry from LEMPTY to LINKFIX with chain-head PTR.
@ k_ra8_eth_gwca_step_fail_2
RA8 Ethernet gwca step fail 2.
@ k_ra8_eth_gwca_step_ok_3
RA8 Ethernet gwca step ok 3.
@ k_ra8_eth_gwca_step_fail_4
RA8 Ethernet gwca step fail 4.
@ k_ra8_eth_gwca_step_ok_2
RA8 Ethernet gwca step ok 2.
@ k_ra8_eth_gwca_step_fail_1
RA8 Ethernet gwca step fail 1.
@ k_ra8_eth_gwca_step_ok_4
RA8 Ethernet gwca step ok 4.
@ k_ra8_eth_gwca_step_fail_3
RA8 Ethernet gwca step fail 3.
@ k_ra8_eth_gwca_step_ok_1
RA8 Ethernet gwca step ok 1.
Ethernet controller base addresses for the Renesas RA8D2.
@ k_ra8_gwdcc_dt_fsingle
FSINGLE – single-fragment frame.
@ k_ra8_gwdcc_dt_link
LINK – chain continuation.
@ k_ra8_gwdcc_dt_fempty
FEMPTY – empty, full frame slot.
@ k_ra8_gwdcc_dt_lempty
LEMPTY – queue disabled.
@ k_ra8_gwmc_opc_config
CR: Config request – LINKFIX writable here.
@ k_ra8_gwmc_opc_operation
OR: Operation request – frames flow.
@ k_ra8_gwca_info1_tx_dv_shift
info1_hi: DV[6:0] field shift.
@ k_ra8_gwca_info1_tx_dv_mask
info1_hi: DV[6:0] field mask.
@ k_ra8_gwca_info1_tx_fmt_direct
info1_lo: FMT = direct descriptor.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Bounded wait-flag primitives for RA8D2 HAL drivers.
Shared CPU-intrinsic seam for the HAL drivers (host/target split).
static void ra8_hw_dsb(void)
Data Synchronisation Barrier (full system).
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
State block for ra8_eth_gwca_default_open / _send / _recv.
uint32_t rx_slot_bytes
RX slot bytes.
ra8_gwca_basic_descriptor_t * rx_chain
RX descriptor chain (FEMPTY ring of rx_depth entries).
uint32_t linkfix_count
Number of LINKFIX entries.
uint32_t tx_queue_index
TX queue index.
uint32_t tx_slot_bytes
TX slot bytes.
ra8_gwca_ext_descriptor_t * tx_chain
TX descriptor chain – 16-byte EXTENDED descriptors (EDE = 1).
uint32_t tx_tail
Round-robin write cursor.
ra8_gwca_basic_descriptor_t * linkfix_table
LINKFIX table covering at least 2 queues (RX + TX).
uint32_t rx_queue_index
LINKFIX entry + GWDCC[i] index.
uint8_t mac_port
MAC port (0..3) that frames are sourced from / sent to.
uint32_t rx_head
Round-robin read cursor.
Per-queue configuration descriptor for ra8_eth_gwca_configure_queue.
8-byte basic descriptor (HUM Ch 34.5.1.2 "General Formats").
volatile uint8_t dt
20..23 Descriptor type (ra8_gwdcc_dt_t).
volatile uint8_t ds_h
8..11 Descriptor size (high nibble).
volatile uint8_t ds_l
0..7 Descriptor size (low byte).
16-byte extended descriptor (HUM Ch 34.5.1.2 "Extended descriptor", GWDCCi.ETS == 0 && GWDCCi....
volatile uint8_t ptr_h
24..31 Pointer high byte (PTR[39:32]).
volatile uint8_t ds_l
0..7 Descriptor size (low byte).
volatile uint32_t info1_lo
64..95 INFO1[31:0].
volatile uint32_t info1_hi
96..127 INFO1[63:32].
volatile uint8_t dt
20..23 Descriptor type (ra8_gwdcc_dt_t).
volatile uint8_t ds_h
8..11 Descriptor size (high nibble).
volatile uint32_t ptr_l
32..63 Pointer low 32 bits.