|
| 1 | +# Splash Asm |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +A toy language and binary format for describing SPI & I2C dumps, aimed at putting splash screens on SPI & I2C displays during vc4 boot on Raspberry Pis. This currently only supports non RP1 RPis, i.e. everything but the Pi 5. |
| 6 | + |
| 7 | +## Usage |
| 8 | + |
| 9 | +1. Clone this repository |
| 10 | + |
| 11 | +2. Write a .splash file, or use one of the examples |
| 12 | + |
| 13 | +3. Run `python3 splash_assembler.py <input>.splash -o output.bin` (You need python 3.12 or later) |
| 14 | + |
| 15 | +4. Either already be on or mount a bootable Raspberry Pi drive |
| 16 | + |
| 17 | +5. Copy this output.bin file into `/boot/firmware/` |
| 18 | + |
| 19 | +6. Edit `/boot/firmware/config.txt`, adding a line `splash_screen=output.bin` anywhere in the file |
| 20 | + |
| 21 | +## Tips |
| 22 | + |
| 23 | +* To import an image for splash screens, write a python script to output your image binary in the correct format into a human readable splash file |
| 24 | +* Consts are useful, the way I have written this is so that you can structure a generic configuration file for your ic, and then have the actual data in another file. This allows the configuration files to be reused. Use extern consts and expressions like I have in st7789.splash to keep things clean |
| 25 | + |
| 26 | +## The language |
| 27 | + |
| 28 | +There are five kinds of instructions you can write |
| 29 | + |
| 30 | +- `define` |
| 31 | +- `command` - this is not a keyword, you write the command name as defined in define to invoke it |
| 32 | +- `const` |
| 33 | +- `delay` |
| 34 | +- `import` |
| 35 | + |
| 36 | +The syntax is as follows: |
| 37 | + |
| 38 | +```bnf |
| 39 | +<define> ::= "define" <command-name> <protocol> <params> |
| 40 | + <protocol> ::= "spi" | "i2c" |
| 41 | + |
| 42 | +
|
| 43 | +<command> ::= <command-name> <params> <opt-data> |
| 44 | + <opt-data> ::= <data> <opt-data> | <data> |
| 45 | +
|
| 46 | +<const> ::= "const" <const-name> "extern" | const <const-name> <data> |
| 47 | +
|
| 48 | +<delay> ::= "delay" <params> <int> |
| 49 | +
|
| 50 | +<import> ::= "import" <filename> |
| 51 | +
|
| 52 | +
|
| 53 | +<params> ::= "[" <key> <value> "]" | "[" <flag> "]" |
| 54 | +<data> ::= <hex-value> | <const-name> |
| 55 | +``` |
| 56 | + |
| 57 | +Newlines and whitespaces are treated the same, the only seperator is either a newline or a whitespace, and one newline or whitespace is the same as n newlines and/or whitespace |
| 58 | + |
| 59 | +What are valid params for each instruction are better defined in the binary docs, I always learn best from examples, so I recomend having a look through those |
| 60 | + |
| 61 | +## The binary |
| 62 | + |
| 63 | +``` |
| 64 | +----------------------------------------------------------------------- |
| 65 | + 1. FILE HEADER - 16 bytes |
| 66 | +
|
| 67 | + 0 1 2 3 |
| 68 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 69 | + | | |
| 70 | + : : |
| 71 | + | "SPLASH ASM" | |
| 72 | + : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 73 | + | | 0x00 | 0x00 | |
| 74 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 75 | + | 0x00 | 0x00 | 0x00 | Version | |
| 76 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 77 | +
|
| 78 | + Magic : ASCII "SPLASH ASM" followed by five 0x00 pad bytes (15 B) |
| 79 | + Version : format version, currently 0x01 |
| 80 | +
|
| 81 | + Everything after byte 15 is a stream of instructions, each |
| 82 | + beginning with a one-byte opcode: |
| 83 | +
|
| 84 | + 0x00 DELAY |
| 85 | + 0x01 SETUP |
| 86 | + 0x10 COMMAND |
| 87 | +
|
| 88 | +
|
| 89 | +
|
| 90 | +----------------------------------------------------------------------- |
| 91 | + 2. DELAY (0x00) - 5 bytes |
| 92 | +
|
| 93 | +
|
| 94 | + +-+-+-+-+-+-+-+-+ |
| 95 | + | Opcode 0x00 | |
| 96 | + +-+-+-+-+-+-+-+-+ |
| 97 | +
|
| 98 | +
|
| 99 | + 0 1 2 3 |
| 100 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 101 | + | Delay | |
| 102 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 103 | +
|
| 104 | + Delay : microseconds to wait (source [US]/[MS]/[S] |
| 105 | + units are multiplied out by the assembler) |
| 106 | +
|
| 107 | +
|
| 108 | +
|
| 109 | +----------------------------------------------------------------------- |
| 110 | + 3. DEFINE (0x01) - 9-byte header + #Size Parameters |
| 111 | +
|
| 112 | +
|
| 113 | + +-+-+-+-+-+-+-+-+ |
| 114 | + | Opcode 0x01 | |
| 115 | + +-+-+-+-+-+-+-+-+ |
| 116 | +
|
| 117 | +
|
| 118 | + 0 1 2 3 |
| 119 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 120 | + | Protocol four cc | |
| 121 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 122 | + | Size | Reserved | |
| 123 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 124 | + | : |
| 125 | + : Parameters : |
| 126 | + : | |
| 127 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 128 | +
|
| 129 | + Protocol : A four cc code, currently either "SPI " or "I2C " |
| 130 | + Size : length in bytes of the parameters block that follows. |
| 131 | + This is unique to a four cc and file version |
| 132 | + Reserved : three pad bytes (struct alignment after Size), always 0x00 |
| 133 | +
|
| 134 | + Defines are implicitly numbered: the index used later by |
| 135 | + COMMAND's "Out idx" field is just the order in which DEFINE |
| 136 | + instructions appear in the stream (0, 1, 2, ...). |
| 137 | +
|
| 138 | + The shape of the N-byte param block depends on Protocol: |
| 139 | +
|
| 140 | + 3a. I2C Parameter block (8 bytes) |
| 141 | +
|
| 142 | + 0 1 2 3 |
| 143 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 144 | + | SDA pin | SCL pin | ADDR | Reserved | |
| 145 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 146 | + | Frequency | |
| 147 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 148 | +
|
| 149 | + ADDR : The I2C address of the endpoint |
| 150 | +
|
| 151 | + 3b. SPI Parameter block (12 bytes) |
| 152 | +
|
| 153 | + 0 1 2 3 |
| 154 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 155 | + | COPI pin | CIPO pin | SCLK pin | CS pin | |
| 156 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 157 | + | DC pin | CPOL | CPHA | CSPOL | |
| 158 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 159 | + | Frequency | |
| 160 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 161 | +
|
| 162 | + CPOL/CPHA : SPI mode bits (default mode 0) |
| 163 | + CPOL : clock polarity, if 1 the data is transmitted on |
| 164 | + rising edges, if 2 the data is transmitted on |
| 165 | + falling edges (default 1) |
| 166 | + CSPOL : chip-select polarity active-low is 1 |
| 167 | + active high is 2, (default 1) |
| 168 | + CPHA : clock phase, if 1 the clock transitions in the |
| 169 | + middle of bits, if 2, the data transitions are in |
| 170 | + phase with the clock |
| 171 | + |
| 172 | +
|
| 173 | +
|
| 174 | +----------------------------------------------------------------------- |
| 175 | + 4. COMMAND (0x10) - 5-byte header + #Size data |
| 176 | +
|
| 177 | +
|
| 178 | + +-+-+-+-+-+-+-+-+ |
| 179 | + | Opcode 0x10 | |
| 180 | + +-+-+-+-+-+-+-+-+ |
| 181 | +
|
| 182 | + 0 1 2 3 |
| 183 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 184 | + | Out idx | Flags | Size | |
| 185 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 186 | + | : |
| 187 | + : Data (Size bytes) : |
| 188 | + : | |
| 189 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 190 | +
|
| 191 | + Out idx : which DEFINE this command targets, by definition order |
| 192 | + Flags : 8-bit bitmask, only bit 0 is currently defined: |
| 193 | + Data : raw bytes to shift out over the target bus |
| 194 | +
|
| 195 | + 4a. SPI Flag bitmask |
| 196 | +
|
| 197 | + +-+-+-+-+-+-+-+-+ |
| 198 | + |S| Reserved |D| |
| 199 | + +-+-+-+-+-+-+-+-+ |
| 200 | +
|
| 201 | + bit 0 (D), DATA_ONLY : If this is 1 then the DC pin always |
| 202 | + indicates data in the command, if 0, |
| 203 | + the DC pin indicates the first byte |
| 204 | + as a command |
| 205 | + bit 7 (S), SWALLOW_ERRORS : If this is 1 then we don't end |
| 206 | + the splash on a transaction error |
| 207 | + (mainly caused by nacked i2c) |
| 208 | +
|
| 209 | + 4b. I2C Flag bitmask |
| 210 | +
|
| 211 | + +-+-+-+-+-+-+-+-+ |
| 212 | + |S| Reserved |R| |
| 213 | + +-+-+-+-+-+-+-+-+ |
| 214 | +
|
| 215 | + bit 0 (R), READ : Indicates an I2C read if 1, indicates an |
| 216 | + I2C write if 0 (default) |
| 217 | + bit 7 (S), SWALLOW_ERRORS : If this is 1 then we don't end |
| 218 | + the splash on a transaction error |
| 219 | + (mainly caused by nacked i2c) |
| 220 | +----------------------------------------------------------------------- |
| 221 | +``` |
| 222 | +*(inspired by https://www.ietf.org/rfc/rfc793.html)* |
| 223 | + |
| 224 | +## Limitations |
| 225 | + |
| 226 | +- This is currently incompatible with the Pi 5 |
| 227 | +- You can have a maximum of 4 SPI defines |
| 228 | +- You can have a maximum of 10 I2C defines |
| 229 | +- The delays are blocking and therefore a long splash description will slow down a boot |
| 230 | +- The only timing guarantee is that if you write a delay command, there will be a wait strictly greater than your delay command. This is not made for timing sensitive applications, there are further delays due to parsing |
0 commit comments