# Bare-metal C driver for the AXI4-Lite SPI master

This driver controls the included Big Sur Silicon AXI4-Lite SPI peripheral. It configures the SPI clock divider and sends and receives one eight-bit, MSB-first, mode-0 frame. Each transfer asserts and releases chip select independently.

| File | Purpose |
| --- | --- |
| `axil_spi_regs.h` | Register offsets and bit masks |
| `axil_spi.h`, `axil_spi.c` | Driver API and implementation |
| `spi_loopback.c` | Example MMIO accessors and a board loopback function |
| `test_axil_spi.cpp`, `Makefile` | Driver tests against the RTL in the sibling `rtl/` directory |

## Register map

All offsets are byte offsets from the peripheral base. Use aligned 32-bit accesses.

| Offset | Register | Driver contract |
| --- | --- | --- |
| `0x00` | STATUS | Bit 0 reports busy. Bit 1 reports completion and is write-one-to-clear. |
| `0x04` | DIVIDER | Bits 15:0 select the number of AXI clock cycles per SPI half period. Valid values are 1 through 65,535. |
| `0x08` | TXDATA | A write starts one frame with the low eight bits and clears previous completion. |
| `0x0C` | RXDATA | The low eight bits hold the most recently received byte. |

## Use it on a target

1. Enable the peripheral clock and release reset. Pass the aligned base address assigned by your system interconnect. Connect MOSI to MISO to run the loopback example.
2. Compile the C driver and supply the two platform MMIO accessors declared in `axil_spi.h`. The example provides a volatile implementation for a board with ordered, 32-bit device memory. Use your board support package where barriers, byte swapping, or other platform-specific handling are required. The driver expects the write to take effect before it polls status. [Arm guide to Device memory](https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Learn%20the%20Architecture/Armv8-A%20memory%20model%20guide.pdf)
3. Call `bss_spi_loopback_example(spi_base)` from your firmware, or use the driver directly:

```c
uint8_t received;
bss_spi_result result = bss_spi_set_divider(spi_base, 4);
if (result == BSS_SPI_OK) {
    result = bss_spi_transfer_byte(spi_base, UINT8_C(0xA6), &received, 1024);
}
/* In loopback, received is 0xA6 on success. */
```

Pass a null receive pointer to discard incoming data. Serialize access across threads, interrupts, and other bus masters. The poll limit counts status reads, and its real duration depends on the CPU and AXI bridge. Pick a limit for your divider and platform. A frame uses 17 SPI half periods from start to completion. A timeout leaves the receive buffer untouched and does not cancel the frame. The driver reports busy if you try another operation while the frame is still active. The MMIO accessors must handle any bus faults according to the platform’s requirements.

## Run the simulation

From the extracted project folder, run:

```sh
make -C firmware test
```

You’ll need a C compiler, a C++ compiler, GNU Make, and [Verilator](https://verilator.org/guide/latest/install.html). The test connects the C driver’s accessors to AXI4-Lite transactions on the actual RTL model and loops MOSI back to MISO. It checks divider programming, byte transfer, completion acknowledgement, invalid arguments, busy handling, and recovery after a timeout. The board example is compiled as freestanding C. Physical hardware validation is pending.
