1 unstable release
0.1.0 | Jul 15, 2023 |
---|
#1464 in Hardware support
31KB
359 lines
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.
>
Request-
Turn-Around<
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
: Read0xC1
: Read (alternate)0xC2
: Short Write0xC3
: Long Write
Response Results
0xA0
: OK0xA1
: CRC Failure0xA2
: Bad Request Type0xA3
: Bad Register#0xA4
: Bad Length
Read Request / Response Sequence
A Read Request consists of four 8-bit values:
- A Type byte of either
0xC0
or0xC1
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
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
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
or0xC3
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 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
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
or0xC5
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
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. The nCS
signal must be
raised at this point to restart the write sequence, regardless of the specific
Response Result sent.
Example of Success
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
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<br/>must have been corrupted as their CRC didn't<br/>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.
Dependencies
~1.5MB
~39K SLoC