Neotron-BMC-Protocol

The SPI protocol for communication with the Neotron Board Management Controller (NBMC).

Introduction

The NBMC is an always-on microcontroller used on Neotron systems. It has very low idle power consumption, allowing it to remain powered up at all times. This lets it listen to button events from the Power and Reset buttons, and control the system LEDs, main ~RESET signal and turn the main 5V Power Rail on and off. This lets your Neotron system have smart 'ATX PC' style features like a soft power button, and it also ensures that all power rails come up before the system is taken out of reset.

The NBMC sits on the SPI bus, receives Requests and sends Responses in reply. It can also activate an IRQ line.

This crate describes the protocol run over the SPI bus and provides some basic helper code for implementing the protocol in Rust.

SPI Communications Protocol

To communicate with the NBMC, the Host Processor must first take the Chip Select line (nCS) low, then send a Header. SPI is a full-duplex system, but in this system only one side is actually transferring useful data at any time, so whilst the Header is being sent the Host will receive Padding Bytes of 0xFF in return (which can be discarded).

A transfer is comprised of three stages.

  1. > Request
  2. - Turn-Around
  3. < Reponse

A Request is a sequence of bytes sent from the Host to the NBMC. Turn-Around is a period of time of interdeterminate length during which the Host clocks out dummy bytes and the NBMC responds with 0xFF bytes, which indicate that it has not yet formulated the Response. This period ends, with the transmission of the Response from the NBMC to the Host.

There are different kinds of Request that can be made. Each has a corresponding Response.

| Request Type | Contains | Length | Response Type | | ------------------ | ------------------------------- | ------------ | ------------- | | Read | Type, Register#, Length, CRC | 4 | Read | | Short Write | Type, Register#, Data Byte, CRC | 4 | Short | | Long Write Start | Type, Register#, Length, CRC | 4 | Short | | Long Write Payload | Length Bytes, CRC | Length + 1 | Short |

| Response Type | Contains | Length | | ------------- | --------------------------- | ------------ | | Short | Result, CRC | 2 | | Read | Result, Length Bytes, CRC | Length + 2 |

To allow the NBMC to be efficient at receiving data over SPI, it is important that the first Request received after the nCS pin goes active-low is of a fixed length. Here, all of the initial Requests have a fixed size of four bytes. A Long Write Start Request tells the NBMC to expect a Request of a different length to follow immediately after, which allows the NBMC to re-configure the DMA to expect the longer length Request. The Long Write Payload MUST only be sent following a Long Write Start, and without resetting the nCS signal in-between.

Request Types

Response Results

Read Request / Response Sequence

A Read Request consists of four 8-bit values:

The Type byte should alternate between 0xC0 and 0xC1 so that the NBMC can tell if the Read Request is a repeat of the previous request. This may occur, for example, if the Read Response fails its CRC check on arrival at the Host. Because a Read Request can have side-effects (like removing bytes from a FIFO), a repeated Read Request should return preceisely the same values as before, rather than, say, fetching more new bytes from the FIFO. This allows the FIFO to be read in a lossless fashion, even when there is occasional corruption on the SPI bus.

A Read Response consists of a variable number of 8-bit values:

Example of Success

```mermaid sequenceDiagram

Host->>NBMC: ReadRequest(25, 5) Note over Host, NBMC: Read 5 bytes from Register 25

NBMC->>NBMC: Reads from register

NBMC->>Host: Response(OK, [0, 1, 2, 3, 4])

Note over Host, NBMC: Host get the five bytes ```

Example of Failure

```mermaid sequenceDiagram

Host->>NBMC: ReadRequest(25, 200) Note over Host, NBMC: Read 200 bytes from Register 25

NBMC->>Host: Response(BadLength)

Note over Host, NBMC: NBMC says no. ```

Short Write Request / Response Sequence

A Short Write Request consists of four 8-bit values:

A Short Response is sent in returning, containing two bytes:

You could equally consider a Short Response as a single 16-bit big-endian value, being one of 0xA069, 0xA16E, 0xA267, 0xA360 or 0xA475.

Example of Success

```mermaid sequenceDiagram

Host->>NBMC: ShortWriteRequest(10, 0xAA) Note over Host, NBMC: Write 0xAA to Register 10

NBMC->>NBMC: Writes to register

NBMC->>Host: Response(OK)

Note over Host, NBMC: NBMC is happy. ```

Long Write Request / Response Sequence

A Long Write Request consists of four 8-bit values:

A Short Response is sent, as per Short Write Request

If a Short Response is received containing a Response Result of OK (0xA0), the NBMC is ready to receive a Long Write Payload. If any other Response Result is received, the Long Write Payload must not be sent and nCS must be raised to indicate the end of the transaction.

A Long Write Payload consists of a variable number of 8-bit values:

This message must always contain exactly the number of bytes stated in the Length field of the Long Write Request, plus one additional CRC byte.

A second Short Response is then sent, as per Short Write Request. The nCS signal must be raised at this point to restart the write sequence, regardless of the specific Response Result sent.

Example of Success

```mermaid sequenceDiagram

Host->>NBMC: LongWriteRequest(16, 5) Note over Host, NBMC: Prepare to write 5 bytes to Register 16

NBMC->>NBMC: Thinks for while

NBMC->>Host: Response(OK)

Note over Host, NBMC: NBMC is ready to take 5 bytes.

Host->>NBMC: LongWritePayload([0, 1, 2, 3, 4]) Note over Host, NBMC: Five bytes are sent

NBMC->>NBMC: Checks CRC

NBMC->>Host: Response(OK)

Note over Host, NBMC: NBMC is happy. ```

Example of Failure

```mermaid sequenceDiagram

Host->>NBMC: LongWriteRequest(16, 5) Note over Host, NBMC: Prepare to write 5 bytes to Register 16

NBMC->>NBMC: Thinks for while

NBMC->>Host: Response(OK)

Note over Host, NBMC: NBMC is ready to take 5 bytes.

Host->>NBMC: LongWritePayload([0, 1, 2, 3, 4]) Note over Host, NBMC: Five bytes are sent

NBMC->>NBMC: Checks CRC

NBMC->>Host: Response(CrcFailure)

Note over Host, NBMC: NBMC is sad. The five bytes
must have been corrupted as their CRC didn't
match. Host must raise nCS and try again. ```

Cancelling

Any Request can be cancelled by the Host lifting nCS high before the Request has finished sending. This allows the NBMC to accomodate unexpected Host reboots (as during a reboot it is expected that the nCS line will be raised).

Licence

This code is licenced under the Blue Oak Model License 1.0.0. See:

Our intent behind picking this licence is to allow this code to be freely reused, both in open-source and commercially licensed products.