Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions splashasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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 :
Expand All @@ -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 : two 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
Expand Down Expand Up @@ -149,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
Expand Down Expand Up @@ -211,7 +214,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
Expand Down
45 changes: 35 additions & 10 deletions splashasm/splash_assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

FILE_DIR = ""
MAGIC = b'SPLASH ASM\x00\x00\x00\x00\x00'
version = 1

class WireProtocols(Enum):
I2C = "i2c"
Expand Down Expand Up @@ -55,16 +56,18 @@ 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)

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
Expand Down Expand Up @@ -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'))
Expand All @@ -308,30 +312,51 @@ def _do_emit_binary(self, state, arr_ptr):

if self.protocol_type == WireProtocols.SPI:
default_params = SPI_PARAMS
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:
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}")
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]):
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

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.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):
Expand Down Expand Up @@ -591,11 +616,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

Expand Down