ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_c6link_frame.c File Reference

The twelve-byte esp-hosted payload header: build it, believe it or not. More...

#include <stdint.h>
#include <string.h>
#include "ra8_attributes.h"
#include "ra8_c6link.h"
#include "ra8_c6link_internal.h"
Include dependency graph for ra8_c6link_frame.c:

Go to the source code of this file.

Enumerations

enum  ra8_c6link_hdr_t : uint8_t {
  k_ra8_c6link_hdr_nibble = 0x0FU ,
  k_ra8_c6link_hdr_seq = 0U ,
  k_ra8_c6link_hdr_csum_at = 6U ,
  k_ra8_c6link_hdr_csum_sz = 2U
}
 Field-level constants the header writer and reader need. More...

Functions

static void internal_c6link_frame_clear (uint8_t *frame, uint16_t from)
 Zero part of a transaction buffer.
static uint16_t internal_c6link_frame_sum (uint8_t *frame, uint16_t span)
 Recompute a frame's checksum as if its checksum field were zero.
void priv_c6link_frame_filler (uint8_t *tx)
 Stamp the transmit transaction as the host's idle filler.
void priv_c6link_frame_seal (uint8_t *tx, uint8_t if_type, uint8_t if_num, uint16_t len)
 Wrap an already-staged payload in a payload header.
static uint8_t internal_c6link_caps_tlv (uint8_t *out, uint8_t at, uint8_t tag, uint8_t value)
 Write one one-octet TLV of the capabilities frame at a cursor.
uint8_t priv_c6link_caps (uint8_t *out, uint8_t cap)
 Build the host-capabilities announcement.
ra8_c6link_frame_class_t priv_c6link_frame_classify (uint8_t *rx, ra8_c6link_rx_view_t *view)
 Decide what a received transaction is, and where its payload lies.

Detailed Description

The twelve-byte esp-hosted payload header: build it, believe it or not.

Tag
[Ring 4 / PAL] {World: NS}

Every transaction on this link carries the same header in front of whatever it is transporting: an interface nibble pair, a length, an offset, a checksum and a sequence number. This file is the only place that reads or writes it.

The header is assembled in a local struct esp_payload_header and copied into the transaction, rather than reached by casting the byte buffer to a header pointer. That keeps the vendored declaration as the single source of truth for the layout while staying inside what C actually defines: the cast would be an aliasing violation, and the (T*)(void*) spelling that silences the compiler is exactly what clang-tidy's bugprone-casting-through-void exists to catch.

Verifying a checksum without touching the buffer
The frame checksum is a plain 16-bit sum of every byte with the checksum field taken as zero. Upstream verifies it by zeroing the field in place, summing, and putting it back. Subtracting the two checksum bytes from the sum of the whole span gives the identical value and leaves the received buffer untouched, which is what lets a frame be classified without a write.
Sequence numbers
The transmitted seq_num stays zero. Upstream increments it, but the bench run that proved this link answered a request whose sequence number was zero, and nothing in the co-processor's reply depended on it. Changing a proven variable for cosmetic parity is how a working link acquires an unexplained failure, so it stays as it was measured.
Since
0.1.0

Definition in file ra8_c6link_frame.c.

Enumeration Type Documentation

◆ ra8_c6link_hdr_t

enum ra8_c6link_hdr_t : uint8_t

Field-level constants the header writer and reader need.

The nibble mask exists because if_type and if_num share one byte: an unmasked assignment to a four-bit bitfield is a narrowing conversion this project's -Wconversion rejects.

Invariant
k_ra8_c6link_hdr_nibble is the widest value either interface field can hold.
k_ra8_c6link_hdr_csum_at is where the checksum's two octets sit in the header, which the verifier subtracts rather than zeroes.
Example:
hdr.if_type = (uint8_t)(if_type & (uint8_t)k_ra8_c6link_hdr_nibble);
See also
priv_c6link_frame_seal
Since
0.1.0
Enumerator
k_ra8_c6link_hdr_nibble 

Mask for a four-bit header field.

k_ra8_c6link_hdr_seq 

Sequence number transmitted; see above.

k_ra8_c6link_hdr_csum_at 

Offset of the checksum's low octet.

k_ra8_c6link_hdr_csum_sz 

Octets the checksum field occupies.

Definition at line 70 of file ra8_c6link_frame.c.

Function Documentation

◆ internal_c6link_caps_tlv()

uint8_t internal_c6link_caps_tlv ( uint8_t * out,
uint8_t at,
uint8_t tag,
uint8_t value )
static

Write one one-octet TLV of the capabilities frame at a cursor.

Every tag the announcement carries has a one-octet value, so the three-octet shape is written once here rather than five times.

Parameters
[out]outBuffer being filled; must be non-null.
[in]atOffset to write at.
[in]tagTag identifier.
[in]valueThe tag's single-octet value.
Returns
The offset just past this TLV.
Return values
non-zeroAlways three past at.
Precondition
Three octets are writable at out + at.
The caller has already checked the buffer capacity.
Postcondition
Exactly three octets were written.
The returned offset addresses the next TLV.
Note
Matches the layout upstream's send_slave_config() composes. LEGACY-OK: send_slave_config() is the upstream esp-hosted symbol name
Since
0.1.0

Definition at line 186 of file ra8_c6link_frame.c.

References k_ra8_c6link_caps_stride, and k_ra8_c6link_caps_value_len.

Referenced by priv_c6link_caps().

◆ internal_c6link_frame_clear()

void internal_c6link_frame_clear ( uint8_t * frame,
uint16_t from )
static

Zero part of a transaction buffer.

Takes a starting offset so a payload already staged in the transaction survives, which is what lets the encoder write in place.

Parameters
[out]frameBuffer to clear; must be non-null.
[in]fromFirst byte to clear, so a staged payload can be preserved.
Returns
Nothing.
Precondition
frame is k_ra8_c6link_frame_bytes long.
from is at most k_ra8_c6link_frame_bytes.
Postcondition
Every byte from from onwards is zero.
Bytes before from are untouched.
Note
The loop is bounded by k_ra8_c6link_frame_bytes (NASA Rule 2).
Since
0.1.0

Definition at line 102 of file ra8_c6link_frame.c.

References k_ra8_c6link_frame_bytes, and RA8_INTERNAL.

Referenced by priv_c6link_frame_filler(), and priv_c6link_frame_seal().

◆ internal_c6link_frame_sum()

uint16_t internal_c6link_frame_sum ( uint8_t * frame,
uint16_t span )
static

Recompute a frame's checksum as if its checksum field were zero.

Subtracts the transmitted checksum octets from the sum of the whole span, which is arithmetically identical to upstream's zero-the-field method and leaves the received buffer untouched.

Parameters
[in]frameTransaction buffer; must be non-null and not modified.
[in]spanBytes the checksum is defined over: header plus payload.
Returns
The checksum the sender should have transmitted.
Return values
0Every octet in the span, and the checksum field, were zero.
Precondition
span is at least k_ra8_c6link_header_bytes, so the checksum field is inside the summed range.
span is at most k_ra8_c6link_frame_bytes.
Postcondition
frame is unmodified.
The result equals what upstream computes by zeroing the field.
Note
The arithmetic is deliberately 16-bit and wrapping, matching the accumulator in the vendored compute_checksum().
Since
0.1.0

Definition at line 127 of file ra8_c6link_frame.c.

References k_ra8_c6link_hdr_csum_at, k_ra8_c6link_hdr_csum_sz, and RA8_INTERNAL.

Referenced by priv_c6link_frame_classify().

◆ priv_c6link_caps()

uint8_t priv_c6link_caps ( uint8_t * out,
uint8_t cap )
nodiscard

Build the host-capabilities announcement.

Pure formatting: no hardware, no link state. Split out so the exact octets can be compared against the protocol in a host test rather than only against the code that wrote them.

Parameters
[out]outBuffer to fill; must be non-null.
[in]capOctets available at out.
Returns
The frame length in octets, or zero when it would not fit.
Return values
0out was null or cap is below k_ra8_c6link_caps_bytes.
Precondition
cap octets are writable at out.
The caller transmits the result on ESP_PRIV_IF, interface 0.
Postcondition
On success exactly k_ra8_c6link_caps_bytes octets were written.
On failure out is not modified.
Note
Safe from any context; it only writes constants.
Example:
const uint8_t n = priv_c6link_caps(&link->tx[k_ra8_c6link_header_bytes],
See also
ra8_c6link_await_ready
Since
0.1.0

Definition at line 194 of file ra8_c6link_frame.c.

References internal_c6link_caps_tlv(), k_ra8_c6link_caps_bytes, k_ra8_c6link_caps_chip, k_ra8_c6link_caps_hdr, k_ra8_c6link_caps_host, k_ra8_c6link_caps_len, k_ra8_c6link_caps_raw_tp, k_ra8_c6link_caps_stride, k_ra8_c6link_caps_tags, k_ra8_c6link_caps_throttle_high, k_ra8_c6link_caps_throttle_low, k_ra8_c6link_caps_type, and RA8_PRIV.

Referenced by ra8_c6link_await_ready().

◆ priv_c6link_frame_classify()

ra8_c6link_frame_class_t priv_c6link_frame_classify ( uint8_t * rx,
ra8_c6link_rx_view_t * view )
nodiscard

Decide what a received transaction is, and where its payload lies.

Applies upstream's ordering, and verifies the checksum by zeroing the field in place and restoring it, so the value a caller later reports is the one the co-processor actually sent.

Parameters
[in,out]rxReceived transaction; must be non-null. Momentarily modified and restored.
[out]viewPayload location and interface, filled only for a data frame; must be non-null.
Returns
ra8_c6link_frame_class_t The verdict.
Return values
k_ra8_c6link_frame_dataThe payload is real and view describes it.
k_ra8_c6link_frame_idleThe co-processor had nothing to send.
k_ra8_c6link_frame_malformedThe header could not be believed.
k_ra8_c6link_frame_bad_checksumThe integrity check failed.
Precondition
The transfer has completed and rx is stable.
rx covers a whole transaction.
Postcondition
rx holds exactly the bytes it held on entry.
view is written only on k_ra8_c6link_frame_data.
Note
Not thread-safe against a concurrent read of rx.
Example:
See also
priv_c6link_frame_seal
Since
0.1.0
MC/DC:
The malformed test is a two-condition decision and the tests drive N+1 vectors against it; see tests/wireless/src/test_ra8_c6link.c.

Definition at line 227 of file ra8_c6link_frame.c.

References ra8_c6link_rx_view::if_num, ra8_c6link_rx_view::if_type, internal_c6link_frame_sum(), k_ra8_c6link_frame_bad_checksum, k_ra8_c6link_frame_data, k_ra8_c6link_frame_idle, k_ra8_c6link_frame_malformed, k_ra8_c6link_header_bytes, k_ra8_c6link_max_payload, ra8_c6link_rx_view::len, memcpy(), ra8_c6link_rx_view::offset, and RA8_PRIV.

Referenced by internal_c6link_pump_receive().

◆ priv_c6link_frame_filler()

void priv_c6link_frame_filler ( uint8_t * tx)

Stamp the transmit transaction as the host's idle filler.

Field for field what the reference host sends when it has nothing queued: if_type = ESP_MAX_IF and every other byte zero, checksum included. The co-processor needs a transaction to answer in, so an idle host still clocks one.

Parameters
[out]txTransmit transaction; must be non-null and k_ra8_c6link_frame_bytes long.
Returns
Nothing.
Precondition
No transfer is in flight on tx.
tx covers a whole transaction.
Postcondition
Byte zero carries ESP_MAX_IF; every other byte is zero.
The frame is byte-identical to upstream's dummy buffer.
Note
The clearing loop is bounded by k_ra8_c6link_frame_bytes (Rule 2).
Example:
See also
priv_c6link_frame_seal
Since
0.1.0

Definition at line 136 of file ra8_c6link_frame.c.

References internal_c6link_frame_clear(), k_ra8_c6link_hdr_nibble, memcpy(), and RA8_PRIV.

Referenced by priv_c6link_pump().

◆ priv_c6link_frame_seal()

void priv_c6link_frame_seal ( uint8_t * tx,
uint8_t if_type,
uint8_t if_num,
uint16_t len )

Wrap an already-staged payload in a payload header.

The payload is expected to be sitting at tx + k_ra8_c6link_header_bytes already – both the RPC encoder and the Ethernet transmit path write it there directly, so nothing is copied twice. This fills the header in front of it, zeroes the tail, and computes the checksum over header plus payload with the checksum field taken as zero, exactly as get_next_tx_buffer() does upstream.

Parameters
[in,out]txTransmit transaction; must be non-null.
[in]if_typeInterface type for the frame, 0..15.
[in]if_numInterface number for the frame, 0..15.
[in]lenPayload length already staged; at most k_ra8_c6link_max_payload.
Returns
Nothing.
Precondition
len bytes are staged at tx + k_ra8_c6link_header_bytes.
len is within k_ra8_c6link_max_payload.
Postcondition
The header describes the payload and the checksum covers both.
Every byte past the payload is zero.
Note
The tail-clearing loop is bounded by k_ra8_c6link_frame_bytes.
Example:
priv_c6link_frame_seal(link->tx, (uint8_t)ESP_SERIAL_IF, 0U, n);
See also
priv_c6link_frame_classify
Since
0.1.0

Definition at line 148 of file ra8_c6link_frame.c.

References internal_c6link_frame_clear(), k_ra8_c6link_hdr_nibble, k_ra8_c6link_hdr_seq, k_ra8_c6link_header_bytes, k_ra8_c6link_max_payload, memcpy(), and RA8_PRIV.

Referenced by priv_c6link_pump().