# 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
* `0xC0`: Read
* `0xC1`: Read (alternate)
* `0xC2`: Short Write
* `0xC3`: Long Write
### Response Results
* `0xA0`: OK
* `0xA1`: CRC Failure
* `0xA2`: Bad Request Type
* `0xA3`: Bad Register#
* `0xA4`: Bad Length
### Read Request / Response Sequence
A *Read Request* consists of four 8-bit values:
* A *Type* byte of either `0xC0` or `0xC1` marking this as a *Read Request*.
* A *Register#*, indicating which register within the *NBMC* the *Host* wishes
to read.
* A Length, indicating how many bytes are to be read from the given *Register#*.
* A *CRC*, which is the CRC-8 of the proceeding three bytes.
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:
* A *Response Result* code indicating whether the read operation was successful
or not.
* A *Payload* consisting of the number of bytes requested in the *Read
Operation* (only present if the *Result* byte indicates Success)
* A *CRC*, which is the CRC-8 of all the proceeding bytes.
#### 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 *Type* byte of `0xC2` or `0xC3` marking this as a *Short Write Request*.
* A *Register#*, indicating which register within the *NBMC* the *Host* wishes to write to.
* A *Data Byte*, which is to be written to the given *Register#*.
* A *CRC*, which is the CRC-8 of the proceeding three bytes.
A *Short Response* is sent in returning, containing two bytes:
* A [*Response Result*](#response-result-codes) code indicating whether the read
operation was successful or not.
* A *CRC*, which is the CRC-8 of all the sole proceeding byte.
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 *Type* byte of `0xC4` or `0xC5` marking this as a *Long Write Request*.
* A *Register#*, indicating which register within the *NBMC* the *Host* wishes to write to.
* A *Length*, which is the number of payload bytes to follow in the subsequent *Long Write Payload*.
* A *CRC*, which is the CRC-8 of the proceeding three bytes.
A *Short Response* is sent, as per [Short Write
Request](#short-write-request--response-sequence)
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:
* A *Payload* consisting of the number of bytes requested in the *Long Write Request*
* A *CRC*, which is the CRC-8 of all the proceeding bytes.
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](#short-write-request--response-sequence). 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:
* [The LICENSE file](./LICENSE)
* [The Blue Oak Licence Website](https://blueoakcouncil.org/license/1.0.0)
Our intent behind picking this licence is to allow this code to be freely
reused, both in open-source and commercially licensed products.