Intro
All data in the Awesomebus protocol is in network order (big-endian). An Awesomebus network consists of a number of nodes on a daisy-chained Ethernet bus. To communicate with the nodes, the Awesomebus host sends out a frame across the bus. The frame is made up of a ‘bucket’ for each node on the bus. Each bucket contains instructions for that specific node to perform.
Frames
An Awesomebus frame is a standard Ethernet frame with ethertype 0x8888
. Both the source and destination MAC addresses are set to the broadcast address. The payload consists of a 2 byte wordcount followed by a number of buckets, followed by padding (up to the required Ethernet payload size of 42 bytes). The wordcount is the total byte count of all buckets in this frame, divided by 4.
uint8[6] mac_dest
uint8[6] mac_src
uint16 ethertype
uint16 wordcount
<buckets>
<padding>
uint32 crc
Buckets
A bucket starts with a 2 byte node ID, followed by a 2 byte instruction count. This is followed by a number of 4 byte instructions.
uint16 node
uint16 length
uint32[] instructions
When a node receives a bucket that matches it’s ID, it processes each instruction and modifies it as needed before sending the bucket on to the next node. When receiving a bucket with a non-matching ID, the bucket is immediately passed onto the next node. Nodes will also process buckets addressed to the broadcast address.
Node State
Each node has the following bus-visible state:
uint16 address
uint24[16] registers
A node will only process buckets which match its address
. This address is set with the AB_SETADDR
instruction. AB_READ
and AB_WRITE
instructions allow operation on the node’s registers.
At startup a node has its address initialised to 0xFF
(the broadcast address).
Instructions
An instruction fits into a 4 byte word. All instructions take the following form:
uint24 value
uint4 register
uint4 opcode
Packed as follows into a word:
31 8 4 0
+----------------+----+----+
| value | reg| op |
+----------------+----+----+
The lowest 4 bits specify the instruction’s opcode, which in turn determines its behaviour and format. Any unused bits in an instruction are set to 0.
The opcodes are described below. Each instruction takes a word as input (in), processes (and potentially modifies the word and the node state), and returns a new word (out).
AB_NOP (0x0)
This instruction does nothing.
+---------------------+----+ +---------------------+----+
| | 00 | -> | | 00 |
+---------------------+----+ +---------------------+----+
out = in
AB_READ (0x1)
This instruction returns the specified register’s 24 bit value.
+----------------+----+----+ +----------------+----+----+
| | reg| 01 | -> | value | reg| 01 |
+----------------+----+----+ +----------------+----+----+
out = in | (node.registers[reg] << 8)
AB_WRITE (0x2)
This instruction sets the specified register’s 24 bit value.
+----------------+----+----+ +----------------+----+----+
| value | reg| 02 | -> | value | reg| 02 |
+----------------+----+----+ +----------------+----+----+
node.registers[reg] = value
out = in
AB_PING (0x3)
This instruction simply returns an AB_NOP
instruction. This is useful for detecting if a node exists at a certain address (An AB_PING
written to a unallocated node address will not return AB_NOP
).
+---------------------+----+ +---------------------+----+
| | 03 | -> | | 00 |
+---------------------+----+ +---------------------+----+
if (address != 0xFF):
out = AB_NOP
AB_SETADDR (0x4)
This instruction sets the node address, only if it is currently not set. When the address is set, the node returns an AB_NOP
instruction, otherwise it passes the instruction through.
+----------------+----+----+ +---------------------+----+
| address | 00 | 04 | -> | | 00 |
+----------------+----+----+ +---------------------+----+
if (address == 0xFF):
node.address = address
out = AB_NOP
else:
out = in