From 37a3cae3f6475d828f4605c1579d29892848c95c Mon Sep 17 00:00:00 2001 From: Thomas Griffiths Date: Fri, 4 Sep 2026 15:08:00 +0100 Subject: [PATCH 1/2] splashasm: Add port option and bump version With the rp1 support, the port cannot be assumed as there are now multiple usable ports. Add in a port option, since this is a silently breaking change, bump version --- splashasm/README.md | 9 ++++++--- splashasm/splash_assembler.py | 24 ++++++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/splashasm/README.md b/splashasm/README.md index 514ed44..6cd82d1 100644 --- a/splashasm/README.md +++ b/splashasm/README.md @@ -109,7 +109,7 @@ What are valid params for each instruction are better defined in the binary docs +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Protocol four cc | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size | Reserved | + | Size | Port | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : : Parameters : @@ -119,7 +119,11 @@ What are valid params for each instruction are better defined in the binary docs Protocol : A four cc code, currently either "SPI " or "I2C " Size : length in bytes of the parameters block that follows. This is unique to a four cc and file version - Reserved : three pad bytes (struct alignment after Size), always 0x00 + Port : Sets the output block we will be writing to, this + needs to be in agreement with the pins to get an + output. Not relevant for Pis earlier than the Pi 5 + Reserved : three pad bytes (struct alignment after Size), always + 0x00 Defines are implicitly numbered: the index used later by COMMAND's "Out idx" field is just the order in which DEFINE @@ -211,7 +215,6 @@ What are valid params for each instruction are better defined in the binary docs ## Limitations -- This is currently incompatible with the Pi 5 family - You can have a maximum of 4 SPI defines - You can have a maximum of 10 I2C defines - The delays are blocking and therefore a long splash description will slow down a boot diff --git a/splashasm/splash_assembler.py b/splashasm/splash_assembler.py index 8536905..a25bd53 100755 --- a/splashasm/splash_assembler.py +++ b/splashasm/splash_assembler.py @@ -17,6 +17,7 @@ FILE_DIR = "" MAGIC = b'SPLASH ASM\x00\x00\x00\x00\x00' +version = 1 class WireProtocols(Enum): I2C = "i2c" @@ -59,12 +60,14 @@ class SPI_PARAMS(Enum): CPHA: Param = Param(default=1, allowed_values=(1, 2)) CSPOL: Param = Param(default=1, allowed_values=(1, 2)) FREQ: Param = Param(default=25000000) + PORT: Param = Param(default=0) class I2C_PARAMS(Enum): SDA: Param = Param(default=2, allowed_values=(2,)) SCL: Param = Param(default=3, allowed_values=(3,)) ADDR: Param = Param(default=0xFF) FREQ: Param = Param(default=100000) + PORT: Param = Param(default=1) class State: lines: str @@ -297,6 +300,7 @@ def _do_parse(cls, state): return define def _do_emit_binary(self, state, arr_ptr): + global version arr_ptr.extend(Instructions.DEFINE.value) arr_ptr.extend(bytearray("SPI " if self.protocol_type == WireProtocols.SPI else "I2C ", 'ascii')) @@ -308,15 +312,20 @@ def _do_emit_binary(self, state, arr_ptr): if self.protocol_type == WireProtocols.SPI: default_params = SPI_PARAMS + if "PORT" in values.keys(): + version = max(version, 2) + elif self.protocol_type == WireProtocols.I2C: default_params = I2C_PARAMS + if "PORT" in values.keys(): + version = max(version, 2) for p in default_params: if p.name not in values.keys() and p.value.default is not None: print(f"Didn't specify nessercary param {p.name} in {self.__class__.__name__} on in file {self.file_name} on line {self.start_line}, defaulting to {p.value.default}") values[p.name] = p.value.default elif p.name in values.keys() and not p.value.check(values[p.name]): - raise ValueError(f"{p.name} cannot be set to {values[p.name]}, the only valid options are {", ".join(str(v) for v in p.value.allowed_values)}") + print(f"{p.name} set to {values[p.name]}, the options are {", ".join(str(v) for v in p.value.allowed_values)} are you sure?") elif p.name not in values.keys() and p.value.default is None: values[p.name] = 0xFF @@ -327,11 +336,14 @@ def _do_emit_binary(self, state, arr_ptr): values["CPHA"], values["CSPOL"]]) packed += values["FREQ"].to_bytes(4, byteorder='little') else: + if version == 1: + values["PORT"] = 0 packed = bytes([values["SDA"], values["SCL"], values["ADDR"], 0]) packed += values["FREQ"].to_bytes(4, byteorder='little') arr_ptr.append(len(packed)) - arr_ptr.extend(b'\x00\x00\x00') # reserved (struct padding after param_len) + arr_ptr.append(values["PORT"] & 0xff) + arr_ptr.extend(b'\x00\x00') # reserved (struct padding after param_len) arr_ptr.extend(packed) class Delay(Instruction): @@ -591,11 +603,11 @@ def pretty_print(data): return '\n'.join(lines) def make_file(input_file): + body = compile_file(input_file) buf = bytearray() - buf.extend(MAGIC) # magic - buf.append(1) # version - - buf.extend(compile_file(input_file)) + buf.extend(MAGIC) + buf.append(version) + buf.extend(body) return buf From a6e5af9345e887824973b9e98f58d5290a42c445 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 17 Sep 2026 20:55:05 +0100 Subject: [PATCH 2/2] splashasm: Change SPI mode values to 0 & 1 Conventionally the SPI mode settings - CPOL, CPHA and CSPOL - are specified as 0 or 1. Change the interface to set use those values on the wire in v2. --- splashasm/README.md | 23 +++++++++++------------ splashasm/splash_assembler.py | 23 ++++++++++++++++++----- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/splashasm/README.md b/splashasm/README.md index 6cd82d1..b4a2c5f 100644 --- a/splashasm/README.md +++ b/splashasm/README.md @@ -2,7 +2,7 @@ ## Introduction -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. +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 ## Usage @@ -25,7 +25,7 @@ A toy language and binary format for describing SPI & I2C dumps, aimed at puttin ## The language -There are five kinds of instructions you can write +There are five kinds of instructions you can write: - `define` - `command` - this is not a keyword, you write the command name as defined in define to invoke it @@ -122,7 +122,7 @@ What are valid params for each instruction are better defined in the binary docs Port : Sets the output block we will be writing to, this needs to be in agreement with the pins to get an output. Not relevant for Pis earlier than the Pi 5 - Reserved : three pad bytes (struct alignment after Size), always + Reserved : two pad bytes (struct alignment after Size), always 0x00 Defines are implicitly numbered: the index used later by @@ -153,15 +153,14 @@ What are valid params for each instruction are better defined in the binary docs | Frequency | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - CPOL/CPHA : SPI mode bits (default mode 0) - CPOL : clock polarity, if 1 the data is transmitted on - rising edges, if 2 the data is transmitted on - falling edges (default 1) - CSPOL : chip-select polarity active-low is 1 - active high is 2, (default 1) - CPHA : clock phase, if 1 the clock transitions in the - middle of bits, if 2, the data transitions are in - phase with the clock + CPOL : clock polarity, if 0 the data is transmitted on + rising edges, if 1 the data is transmitted on + falling edges (default 0) + CSPOL : chip-select polarity active-low is 0 + active high is 1 (default 0) + CPHA : clock phase, if 0 the clock transitions in the + middle of bits, if 1, the data transitions are in + phase with the clock (default 0) ----------------------------------------------------------------------- 4. COMMAND (0x10) - 5-byte header + #Size data diff --git a/splashasm/splash_assembler.py b/splashasm/splash_assembler.py index a25bd53..ddc1a48 100755 --- a/splashasm/splash_assembler.py +++ b/splashasm/splash_assembler.py @@ -56,9 +56,9 @@ class SPI_PARAMS(Enum): CS: Param = Param(default=8) SCLK: Param = Param(default=11) DC: Param = Param(default=None) - CPOL: Param = Param(default=1, allowed_values=(1, 2)) - CPHA: Param = Param(default=1, allowed_values=(1, 2)) - CSPOL: Param = Param(default=1, allowed_values=(1, 2)) + CPOL: Param = Param(default=0, allowed_values=(0, 1)) + CPHA: Param = Param(default=0, allowed_values=(0, 1)) + CSPOL: Param = Param(default=0, allowed_values=(0, 1)) FREQ: Param = Param(default=25000000) PORT: Param = Param(default=0) @@ -312,7 +312,7 @@ def _do_emit_binary(self, state, arr_ptr): if self.protocol_type == WireProtocols.SPI: default_params = SPI_PARAMS - if "PORT" in values.keys(): + if "PORT" in values.keys() or "CPOL" in values.keys() or "CPHA" in values.keys() or "CSPOL" in values.keys(): version = max(version, 2) elif self.protocol_type == WireProtocols.I2C: @@ -322,24 +322,37 @@ def _do_emit_binary(self, state, arr_ptr): for p in default_params: if p.name not in values.keys() and p.value.default is not None: - print(f"Didn't specify nessercary param {p.name} in {self.__class__.__name__} on in file {self.file_name} on line {self.start_line}, defaulting to {p.value.default}") + print(f"Didn't specify necessary param {p.name} in {self.__class__.__name__} in file {self.file_name} on line {self.start_line}, defaulting to {p.value.default}") values[p.name] = p.value.default elif p.name in values.keys() and not p.value.check(values[p.name]): print(f"{p.name} set to {values[p.name]}, the options are {", ".join(str(v) for v in p.value.allowed_values)} are you sure?") elif p.name not in values.keys() and p.value.default is None: values[p.name] = 0xFF + if self.protocol_type == WireProtocols.SPI: + if version == 1: + values["CPOL"] += 1 + values["CSPOL"] += 1 + values["CPHA"] += 1 if self.protocol_type == WireProtocols.SPI: packed = bytes([values["COPI"], values["CIPO"], values["SCLK"], values["CS"], values["DC"], values["CPOL"], values["CPHA"], values["CSPOL"]]) packed += values["FREQ"].to_bytes(4, byteorder='little') + if values["PORT"] != 0: + version = max(version, 2) + else: + values["PORT"] = 0 else: if version == 1: values["PORT"] = 0 packed = bytes([values["SDA"], values["SCL"], values["ADDR"], 0]) packed += values["FREQ"].to_bytes(4, byteorder='little') + if values["PORT"] != 1: + version = max(version, 2) + else: + values["PORT"] = 0 arr_ptr.append(len(packed)) arr_ptr.append(values["PORT"] & 0xff)