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

FAT16 synthesis, OSPI pattern, MSC media callbacks + console helpers. More...

#include <stdint.h>
#include <string.h>
#include "ra8_board_ek_ra8d2.h"
#include "ra8_err.h"
#include "ra8_xspi.h"
#include "usb_selftest_ospi_steps.h"
#include "tx_api.h"
#include "ux_api.h"
#include "ux_device_class_storage.h"
Include dependency graph for usb_selftest_ospi_format.c:

Go to the source code of this file.

Functions

static void selftest_put16 (UCHAR *dst, uint16_t value)
 Write a 16-bit value little-endian into a byte buffer.
static void selftest_put32 (UCHAR *dst, uint32_t value)
 Write a 32-bit value little-endian into a byte buffer.
static void selftest_fat_fill_boot (UCHAR *out)
 Synthesize the FAT16 boot sector (MS FAT spec 1.03 sec 3.1).
static void selftest_fat_fill_fat (uint32_t fat_sector, UCHAR *out)
 Synthesize one FAT16 sector of the cluster chain.
static void selftest_fat_fill_root (uint32_t root_sector, UCHAR *out)
 Synthesize one root-directory sector.
void selftest_pattern_fill (uint32_t win_sector, UCHAR *out)
 Compute the deterministic pattern for one window data sector.
static void selftest_fat_fill_sector (uint32_t lba, UCHAR *out)
 Synthesize one 512-byte sector of the read-only volume.
UINT selftest_msc_read (VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status)
 Storage media-read callback: synthesize sectors over MRAM.
UINT selftest_msc_write (VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status)
 Storage media-write callback: always rejects (write-protected).
UINT selftest_msc_status (VOID *storage, ULONG lun, ULONG media_id, ULONG *media_status)
 Storage media-status callback.
static uint8_t selftest_nibble_to_hex (uint32_t nibble)
 Format one nibble (0..15) into an uppercase hex character.
static uint32_t selftest_str_len (const char *text)
 Bounded ASCII string length (cap k_selftest_print_cap).
static ra8_err_t selftest_sci_write (const uint8_t *data, uint32_t len)
 Push a literal block over SCI8 polled.
ra8_err_t selftest_print (const char *text)
 Print a NUL-terminated ASCII string over the console.
ra8_err_t selftest_print_dec (uint32_t value)
 Print a uint32_t as ASCII decimal.
ra8_err_t selftest_print_hex (uint32_t value, uint8_t digits)
 Print a value as fixed-width uppercase hex.
ra8_err_t selftest_print_fail (const char *what, ra8_err_t err)
 Print "FAIL <what> err=0xNNNNNNNN" on its own line.

Variables

static volatile uint32_t s_dbg_read_calls
 Device-side media_read invocations.
static const UCHAR s_fat_oem_name [8] = {'R', 'A', '8', 'D', '2', 'F', 'W', ' '}
 Boot-sector OEM name (8 bytes, space padded).
static const UCHAR s_fat_volume_label [11] = {'R', 'A', '8', 'D', '2', ' ', 'O', 'S', 'P', 'I', ' '}
 Volume label, 11 bytes space padded (also the root entry).
static const UCHAR s_fat_fs_type [8] = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '}
 Filesystem-type tag, 8 bytes space padded.
static const UCHAR s_fat_file_name [11] = {'O', 'S', 'P', 'I', ' ', ' ', ' ', ' ', 'B', 'I', 'N'}
 8.3 directory name of the exposed file: "OSPI.BIN".

Detailed Description

FAT16 synthesis, OSPI pattern, MSC media callbacks + console helpers.

Tag
[Ring 6 / APP] {World: S}

Device-facing half of the OSPI USB self-loop app, split out of main.c so every translation unit stays under the file-size cap. Holds the synthesized read-only FAT16 volume (boot sector / FAT chain / root directory / data sectors pulled off the OSPI flash), the deterministic per-sector pattern the boot programmer and the host verifier both compute, the USBX Mass-Storage media read/write/status callbacks, and the polled SCI8 -> J-Link OB CDC console formatters. main.c registers the media callbacks; usb_selftest_ospi_host.c reuses the pattern + console formatters. The shared constants and the cross-TU prototypes live in usb_selftest_ospi_steps.h.

Author
Brighton Sikarskie
Date
2026-06-13
Since
0.1.0

Definition in file usb_selftest_ospi_format.c.

Function Documentation

◆ selftest_fat_fill_boot()

void selftest_fat_fill_boot ( UCHAR * out)
static

Synthesize the FAT16 boot sector (MS FAT spec 1.03 sec 3.1).

BPB for the padded 4146-sector volume plus the 0x55AA signature; geometry constants in selftest_fat_geom_t.

Parameters
[out]outZeroed 512-byte sector buffer.
Precondition
out is zeroed.
Geometry constants describe a valid FAT16 volume.
Postcondition
out holds the BPB + 0x55AA signature.
No other state changes.
Note
Pure function.
Since
0.1.0

Definition at line 128 of file usb_selftest_ospi_format.c.

References k_boot_drive_num, k_boot_ext_sig, k_boot_jmp0, k_boot_jmp1, k_boot_jmp2, k_boot_media, k_boot_num_heads, k_boot_sec_per_trk, k_boot_sig_hi, k_boot_sig_hi_off, k_boot_sig_lo, k_boot_sig_lo_off, k_boot_volume_id, k_bpb_off_bootsig, k_bpb_off_bps, k_bpb_off_drvnum, k_bpb_off_fatsz16, k_bpb_off_fstype, k_bpb_off_heads, k_bpb_off_jmp, k_bpb_off_label, k_bpb_off_media, k_bpb_off_nfats, k_bpb_off_oem, k_bpb_off_rootent, k_bpb_off_rsvd, k_bpb_off_spc, k_bpb_off_spt, k_bpb_off_totsec16, k_bpb_off_volid, k_fat_fat_sectors, k_fat_num_fats, k_fat_reserved_sectors, k_fat_root_entries, k_fat_total_sectors, k_selftest_block_size, memcpy(), s_fat_fs_type, s_fat_oem_name, s_fat_volume_label, selftest_put16(), and selftest_put32().

Referenced by selftest_fat_fill_sector().

◆ selftest_fat_fill_fat()

void selftest_fat_fill_fat ( uint32_t fat_sector,
UCHAR * out )
static

Synthesize one FAT16 sector of the cluster chain.

MRAM.BIN occupies clusters 2..2049 as one sequential chain (entry c -> c + 1, last entry -> end-of-chain). Entries 0/1 carry the media descriptor per the FAT spec; everything past the chain reads as free (0x0000).

Parameters
[in]fat_sectorIndex of the FAT sector (0-based).
[out]outZeroed 512-byte sector buffer.
Precondition
out is zeroed.
fat_sector is below k_fat_fat_sectors.
Postcondition
out holds 256 little-endian FAT16 entries.
No other state changes.
Note
Pure function.
Since
0.1.0

Definition at line 172 of file usb_selftest_ospi_format.c.

References k_fat_entries_per_sec, k_fat_entry0, k_fat_eoc, k_fat_last_data_clus, and selftest_put16().

Referenced by selftest_fat_fill_sector().

◆ selftest_fat_fill_root()

void selftest_fat_fill_root ( uint32_t root_sector,
UCHAR * out )
static

Synthesize one root-directory sector.

Sector 0 of the root carries two entries: the volume label and the read-only MRAM.BIN file (start cluster 2, size 1 MiB). Every other root sector is empty.

Parameters
[in]root_sectorIndex of the root sector (0-based).
[out]outZeroed 512-byte sector buffer.
Precondition
out is zeroed.
root_sector is below k_fat_root_sectors.
Postcondition
out holds the directory entries for that sector.
No other state changes.
Note
Pure function.
Since
0.1.0

Definition at line 211 of file usb_selftest_ospi_format.c.

References k_dir_attr_read_only, k_dir_attr_volume, k_dir_entry_bytes, k_dir_name_bytes, k_dir_off_attr, k_dir_off_cluster_lo, k_dir_off_size, k_fat_first_cluster, k_ospi_bytes, memcpy(), s_fat_file_name, s_fat_volume_label, selftest_put16(), and selftest_put32().

Referenced by selftest_fat_fill_sector().

◆ selftest_fat_fill_sector()

void selftest_fat_fill_sector ( uint32_t lba,
UCHAR * out )
static

Synthesize one 512-byte sector of the read-only volume.

Dispatches on the LBA: boot sector, FAT, root directory, or data region. Data sectors are pulled straight off the OSPI flash with ra8_xspi_flash_read (the bytes the boot programmer wrote); padding clusters past the chain read as zeros. A flash read error leaves the zero-fill in place so the host sees a mismatch rather than stale data.

Parameters
[in]lbaLogical block address inside the volume.
[out]out512-byte destination buffer.
Precondition
lba is below k_fat_total_sectors (caller-checked).
out has 512 writable bytes.
The OSPI window was erased + programmed at boot.
Postcondition
out holds the synthesized sector content.
No other state changes (OSPI is read, never written here).
Note
Reads OSPI via ra8_xspi (command-based); runs on the class thread.
Since
0.1.0

Definition at line 257 of file usb_selftest_ospi_format.c.

References k_fat_data_lba, k_fat_fat_lba, k_fat_first_cluster, k_fat_last_data_clus, k_fat_root_lba, k_ospi_instance, k_ospi_test_offset, k_selftest_block_size, memset(), ra8_xspi_flash_read(), selftest_fat_fill_boot(), selftest_fat_fill_fat(), and selftest_fat_fill_root().

Referenced by selftest_msc_read().

◆ selftest_msc_read()

UINT selftest_msc_read ( VOID * storage,
ULONG lun,
UCHAR * data_pointer,
ULONG number_blocks,
ULONG lba,
ULONG * media_status )

Storage media-read callback: synthesize sectors over MRAM.

Bound checks the request against the volume, then fills each block via the FAT16 synthesizer. LED1 toggles per call so the self-loop traffic is visible on the board.

Parameters
[in,out]storageUSBX storage class instance (unused).
[in]lunLogical unit number (must be 0).
[out]data_pointerUSBX-owned destination buffer.
[in]number_blocksNumber of 512-byte blocks to produce.
[in]lbaStarting LBA.
[out]media_statusFilled with sense status word.
Returns
UX_SUCCESS if the request fits the volume; otherwise UX_ERROR with media_status set to ILLEGAL REQUEST.
Return values
UX_SUCCESSRead completed.
UX_ERROROut-of-range LBA / count.
Precondition
data_pointer and media_status are non-NULL (USBX guarantee).
lun is 0 (single-LUN device).
Postcondition
Either number_blocks * 512 bytes were synthesized or media_status is non-zero.
The device-side read-call probe advanced.
Note
Called from the USBX storage class thread.
Since
0.1.0

Bound checks the request against the volume, then fills each block via selftest_fat_fill_sector. LED1 toggles per call so the self-loop traffic is visible on the board.

Parameters
[in,out]storageUSBX storage class instance (unused).
[in]lunLogical unit number (must be 0).
[out]data_pointerUSBX-owned destination buffer.
[in]number_blocksNumber of 512-byte blocks to produce.
[in]lbaStarting LBA.
[out]media_statusFilled with sense status word.
Returns
UX_SUCCESS if the request fits the volume; otherwise UX_ERROR with media_status set to ILLEGAL REQUEST.
Return values
UX_SUCCESSRead completed.
UX_ERROROut-of-range LBA / count.
Precondition
data_pointer and media_status are non-NULL (USBX guarantee).
lun is 0 (single-LUN device).
Postcondition
Either number_blocks * 512 bytes were synthesized or media_status is non-zero.
s_dbg_read_calls advanced.
Note
Called from the USBX storage class thread.
Since
0.1.0

Definition at line 288 of file usb_selftest_ospi_format.c.

References k_fat_total_sectors, k_ra8_board_led1, k_scsi_asc_lba_out_of_range, k_scsi_ascq_none, k_scsi_sense_illegal_request, k_selftest_block_size, ra8_board_led_toggle(), s_dbg_read_calls, selftest_fat_fill_sector(), and selftest_fat_fill_sector().

Referenced by selftest_msc_class_register(), selftest_msc_class_register(), and selftest_msc_class_register().

◆ selftest_msc_status()

UINT selftest_msc_status ( VOID * storage,
ULONG lun,
ULONG media_id,
ULONG * media_status )

Storage media-status callback.

Always reports media-present.

The synthesized volume cannot go away; status is constant 0.

Parameters
[in,out]storageUSBX storage class instance (unused).
[in]lunLogical unit number (unused).
[in]media_idMedia id (unused).
[out]media_statusFilled with 0 (no fault).
Returns
Always UX_SUCCESS.
Return values
UX_SUCCESSMedia is present and ready.
Precondition
media_status is non-NULL (USBX guarantee).
The class instance is live.
Postcondition
*media_status is 0.
No other state changes.
Note
Synthesized volume; never reports media-not-present.
Since
0.1.0

Definition at line 330 of file usb_selftest_ospi_format.c.

Referenced by selftest_msc_class_register(), selftest_msc_class_register(), and selftest_msc_class_register().

◆ selftest_msc_write()

UINT selftest_msc_write ( VOID * storage,
ULONG lun,
UCHAR * data_pointer,
ULONG number_blocks,
ULONG lba,
ULONG * media_status )

Storage media-write callback: always rejects (write-protected).

The host side of this very app probes exactly this rejection (WRITE(10) must fail with DATA PROTECT and the transport must keep working afterwards).

Parameters
[in,out]storageUSBX storage class instance (unused).
[in]lunLogical unit number (unused).
[in]data_pointerUSBX-owned source buffer (unused).
[in]number_blocksNumber of blocks the host tried (unused).
[in]lbaStarting LBA (unused).
[out]media_statusFilled with DATA PROTECT sense.
Returns
Always UX_ERROR.
Return values
UX_ERRORThe medium is write-protected.
Precondition
media_status is non-NULL (USBX guarantee).
The LUN also reports write-protected via MODE SENSE.
Postcondition
*media_status carries the DATA PROTECT sense triple.
The MRAM window is untouched.
Note
Hosts honouring the MODE SENSE WP bit never call this.
Since
0.1.0

Definition at line 312 of file usb_selftest_ospi_format.c.

References k_scsi_asc_write_protected, k_scsi_ascq_none, and k_scsi_sense_data_protect.

Referenced by selftest_msc_class_register(), selftest_msc_class_register(), and selftest_msc_class_register().

◆ selftest_nibble_to_hex()

uint8_t selftest_nibble_to_hex ( uint32_t nibble)
static

Format one nibble (0..15) into an uppercase hex character.

Standard '0'-'9' then 'A'-'F' mapping.

Parameters
[in]nibble4-bit value.
Returns
ASCII '0'..'9' or 'A'..'F'.
Return values
'0'For a zero nibble.
Precondition
Caller has already masked the value to 4 bits.
None beyond the mask contract.
Postcondition
Returned byte is in the printable hex range.
No state changes.
Note
Pure function.
Since
0.1.0

Definition at line 361 of file usb_selftest_ospi_format.c.

References k_selftest_hex_digit_split.

Referenced by selftest_print_hex().

◆ selftest_pattern_fill()

void selftest_pattern_fill ( uint32_t win_sector,
UCHAR * out )

Compute the deterministic pattern for one window data sector.

Fills out with (win_sector * smul + i * imul + bias) per byte (see selftest_pattern_t). This is the single source of truth: the boot programmer writes it into OSPI and the host verifier recomputes it – so the host never reads the flash and there is no single-controller contention.

Parameters
[in]win_sector0-based sector index within the OSPI window.
[out]out512-byte destination buffer.
Precondition
out has 512 writable bytes.
Postcondition
out holds the sector's pattern bytes.
No global state changes.
Note
Pure function.
Since
0.1.0

Definition at line 227 of file usb_selftest_ospi_format.c.

References k_ospi_pat_bias, k_ospi_pat_imul, k_ospi_pat_mask, k_ospi_pat_smul, and k_selftest_block_size.

Referenced by selftest_ospi_write_pattern(), and selftest_verify_ospi_raw().

◆ selftest_print()

ra8_err_t selftest_print ( const char * text)
nodiscard

Print a NUL-terminated ASCII string over the console.

Length-bounded by the bounded string-length scan.

Parameters
[in]textString to print (CR/LF included by the caller).
Returns
ra8_err_t propagated from the SCI helper.
Return values
k_ra8_okAll bytes queued.
Precondition
SCI8 init already ran; text is non-NULL.
text is NUL-terminated within k_selftest_print_cap bytes.
Postcondition
The string bytes are in the SCI8 TX FIFO.
No other state changes.
Note
Blocking polled TX.
Since
0.1.0

Definition at line 423 of file usb_selftest_ospi_format.c.

Referenced by selftest_print_fail().

◆ selftest_print_dec()

ra8_err_t selftest_print_dec ( uint32_t value)
nodiscard

Print a uint32_t as ASCII decimal.

Print an unsigned 32-bit value in decimal.

Digit-reversal into a bounded scratch buffer.

Parameters
[in]valueValue to print.
Returns
ra8_err_t propagated from the SCI helper.
Return values
k_ra8_okAll bytes queued.
Precondition
SCI8 init already ran.
None beyond console readiness.
Postcondition
One ASCII decimal token is in the SCI8 TX FIFO.
No other state changes.
Note
Blocking polled TX.
Since
0.1.0

Definition at line 428 of file usb_selftest_ospi_format.c.

◆ selftest_print_fail()

ra8_err_t selftest_print_fail ( const char * what,
ra8_err_t err )
nodiscard

Print "FAIL <what> err=0xNNNNNNNN" on its own line.

Print a "FAIL <what> err=0x..." diagnostic line.

One-line diagnostic; print errors inside are not recoverable anyway, so the first failing chunk's code is returned.

Parameters
[in]whatShort description of the failed step.
[in]errError code returned by the step.
Returns
ra8_err_t propagated from the SCI helpers.
Return values
k_ra8_okThe diagnostic line is queued.
Precondition
SCI8 init already ran.
what is NUL-terminated within the print cap.
Postcondition
One diagnostic line is in the SCI8 TX FIFO.
No other state changes.
Note
Blocking polled TX.
Since
0.1.0

Definition at line 466 of file usb_selftest_ospi_format.c.

◆ selftest_print_hex()

ra8_err_t selftest_print_hex ( uint32_t value,
uint8_t digits )
nodiscard

Print a value as fixed-width uppercase hex.

Print the low digits hex nibbles of value (upper-case).

Width is clamped to 8 hex digits.

Parameters
[in]valueValue to print.
[in]digitsHex digit count (4 for u16, 8 for u32).
Returns
ra8_err_t propagated from the SCI helper.
Return values
k_ra8_okAll bytes queued.
Precondition
SCI8 init already ran.
digits is at most k_selftest_hex_chars_u32.
Postcondition
One fixed-width hex token is in the SCI8 TX FIFO.
No other state changes.
Note
Blocking polled TX.
Since
0.1.0

Definition at line 452 of file usb_selftest_ospi_format.c.

Referenced by selftest_print_fail().

◆ selftest_put16()

void selftest_put16 ( UCHAR * dst,
uint16_t value )
static

Write a 16-bit value little-endian into a byte buffer.

Low byte first, high byte second, per the FAT on-disk layout.

Parameters
[out]dstDestination (2 bytes).
[in]valueValue to store.
Precondition
dst has 2 writable bytes.
None beyond the buffer contract.
Postcondition
dst[0] holds the low byte, dst[1] the high byte.
No other state changes.
Note
Pure function.
Since
0.1.0

Definition at line 84 of file usb_selftest_ospi_format.c.

References k_byte_mask, and k_byte_shift.

Referenced by selftest_fat_fill_boot(), selftest_fat_fill_fat(), selftest_fat_fill_root(), and selftest_put32().

◆ selftest_put32()

void selftest_put32 ( UCHAR * dst,
uint32_t value )
static

Write a 32-bit value little-endian into a byte buffer.

Two selftest_put16 halves, low half-word first.

Parameters
[out]dstDestination (4 bytes).
[in]valueValue to store.
Precondition
dst has 4 writable bytes.
None beyond the buffer contract.
Postcondition
dst holds the four little-endian bytes of value.
No other state changes.
Note
Pure function.
Since
0.1.0

Definition at line 106 of file usb_selftest_ospi_format.c.

References k_word_mask, k_word_shift, and selftest_put16().

Referenced by selftest_fat_fill_boot(), and selftest_fat_fill_root().

◆ selftest_sci_write()

ra8_err_t selftest_sci_write ( const uint8_t * data,
uint32_t len )
staticnodiscard

Push a literal block over SCI8 polled.

Thin wrapper fixing the console channel.

Parameters
[in]dataBuffer to send.
[in]lenByte count.
Returns
ra8_err_t passthrough from ra8_board_uart_console_write.
Return values
k_ra8_okAll bytes queued.
Precondition
data is non-NULL; SCI8 init already ran.
len excludes any NUL terminator.
Postcondition
Bytes have been pushed out the SCI8 TX FIFO.
No other state changes.
Note
Blocking polled TX.
Since
0.1.0

Definition at line 418 of file usb_selftest_ospi_format.c.

References ra8_board_uart_console_write().

Referenced by selftest_print(), selftest_print_dec(), and selftest_print_hex().

◆ selftest_str_len()

uint32_t selftest_str_len ( const char * text)
static

Bounded ASCII string length (cap k_selftest_print_cap).

Linear scan with a hard upper bound.

Parameters
[in]textNUL-terminated string.
Returns
Number of bytes before the NUL, capped.
Return values
0For an empty string.
Precondition
text is non-NULL.
text points to readable storage of at least the returned length.
Postcondition
No state changes.
Return value never exceeds k_selftest_print_cap.
Note
Bounded scan – never walks past the cap on a missing NUL.
Since
0.1.0

Definition at line 387 of file usb_selftest_ospi_format.c.

References k_selftest_print_cap.

Referenced by selftest_print().

Variable Documentation

◆ s_dbg_read_calls

volatile uint32_t s_dbg_read_calls
static

Device-side media_read invocations.

Definition at line 46 of file usb_selftest_ospi_format.c.

◆ s_fat_file_name

const UCHAR s_fat_file_name[11] = {'O', 'S', 'P', 'I', ' ', ' ', ' ', ' ', 'B', 'I', 'N'}
static

8.3 directory name of the exposed file: "OSPI.BIN".

Definition at line 62 of file usb_selftest_ospi_format.c.

◆ s_fat_fs_type

const UCHAR s_fat_fs_type[8] = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '}
static

Filesystem-type tag, 8 bytes space padded.

Definition at line 59 of file usb_selftest_ospi_format.c.

◆ s_fat_oem_name

const UCHAR s_fat_oem_name[8] = {'R', 'A', '8', 'D', '2', 'F', 'W', ' '}
static

Boot-sector OEM name (8 bytes, space padded).

Definition at line 53 of file usb_selftest_ospi_format.c.

◆ s_fat_volume_label

const UCHAR s_fat_volume_label[11] = {'R', 'A', '8', 'D', '2', ' ', 'O', 'S', 'P', 'I', ' '}
static

Volume label, 11 bytes space padded (also the root entry).

Definition at line 56 of file usb_selftest_ospi_format.c.