const ZERO: u32 = 0;
const RA: u32 = 1;
const T0: u32 = 5;
const T1: u32 = 6;
const T2: u32 = 7;
const A0: u32 = 10;
const A1: u32 = 11;
const A2: u32 = 12;
const A3: u32 = 13;
const A5: u32 = 15;
const OP_LUI: u32 = 0b011_0111;
const OP_JAL: u32 = 0b110_1111;
const OP_JALR: u32 = 0b110_0111;
const OP_BRANCH: u32 = 0b110_0011;
const OP_LOAD: u32 = 0b000_0011;
const OP_STORE: u32 = 0b010_0011;
const OP_IMM: u32 = 0b001_0011;
const fn i_type(imm: i32, rs1: u32, funct3: u32, rd: u32, opcode: u32) -> u32 {
((imm as u32 & 0xfff) << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode
}
const fn s_type(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
let imm = imm as u32;
(((imm >> 5) & 0x7f) << 25)
| (rs2 << 20)
| (rs1 << 15)
| (funct3 << 12)
| ((imm & 0x1f) << 7)
| opcode
}
const fn b_type(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
let imm = imm as u32;
(((imm >> 12) & 1) << 31)
| (((imm >> 5) & 0x3f) << 25)
| (rs2 << 20)
| (rs1 << 15)
| (funct3 << 12)
| (((imm >> 1) & 0xf) << 8)
| (((imm >> 11) & 1) << 7)
| opcode
}
const fn u_type(imm: u32, rd: u32, opcode: u32) -> u32 {
(imm << 12) | (rd << 7) | opcode
}
const fn j_type(imm: i32, rd: u32, opcode: u32) -> u32 {
let imm = imm as u32;
(((imm >> 20) & 1) << 31)
| (((imm >> 1) & 0x3ff) << 21)
| (((imm >> 11) & 1) << 20)
| (((imm >> 12) & 0xff) << 12)
| (rd << 7)
| opcode
}
const fn addi(rd: u32, rs1: u32, imm: i32) -> u32 {
i_type(imm, rs1, 0b000, rd, OP_IMM)
}
const fn andi(rd: u32, rs1: u32, imm: i32) -> u32 {
i_type(imm, rs1, 0b111, rd, OP_IMM)
}
const fn lw(rd: u32, rs1: u32, imm: i32) -> u32 {
i_type(imm, rs1, 0b010, rd, OP_LOAD)
}
const fn lbu(rd: u32, rs1: u32, imm: i32) -> u32 {
i_type(imm, rs1, 0b100, rd, OP_LOAD)
}
const fn sw(rs2: u32, rs1: u32, imm: i32) -> u32 {
s_type(imm, rs2, rs1, 0b010, OP_STORE)
}
const fn sb(rs2: u32, rs1: u32, imm: i32) -> u32 {
s_type(imm, rs2, rs1, 0b000, OP_STORE)
}
const fn beq(rs1: u32, rs2: u32, offset: i32) -> u32 {
b_type(offset, rs2, rs1, 0b000, OP_BRANCH)
}
const fn bne(rs1: u32, rs2: u32, offset: i32) -> u32 {
b_type(offset, rs2, rs1, 0b001, OP_BRANCH)
}
const fn jal(rd: u32, offset: i32) -> u32 {
j_type(offset, rd, OP_JAL)
}
const fn jalr(rd: u32, rs1: u32, imm: i32) -> u32 {
i_type(imm, rs1, 0b000, rd, OP_JALR)
}
const fn ret() -> u32 {
jalr(ZERO, RA, 0)
}
const fn li_hi(rd: u32, value: u32) -> u32 {
u_type((value.wrapping_add(0x800) >> 12) & 0xf_ffff, rd, OP_LUI)
}
const fn li_lo(rd: u32, value: u32) -> u32 {
let low = (value & 0xfff) as i32;
let low = if low >= 0x800 { low - 0x1000 } else { low };
addi(rd, rd, low)
}
pub const SPI1: u32 = 0xf000_0000;
pub const OCTOSPI: u32 = 0xf000_1000;
pub const RAM: u32 = 0x2000_0000;
pub const WINDOW: u32 = 0x9000_0000;
pub const SENTINEL: u32 = 0x00c0_ffee;
const DCR1_1M: u32 = 19 << 16;
const CCR_SINGLE_24: u32 = 1 | (1 << 8) | (2 << 12) | (1 << 24);
const CR_MEMORY_MAPPED: u32 = 1 | (3 << 28);
const SPI_CR1: u32 = 0x04 | 0x40 | (3 << 3);
pub const PAYLOAD_BYTES: u32 = 24;
const XFER: i32 = 1;
const XWAIT: i32 = 2;
const MAIN: i32 = 7;
const COPY: i32 = 63;
const PAYLOAD: i32 = 83;
const DONE: i32 = 88;
const fn rel(from: i32, to: i32) -> i32 {
(to - from) * 4
}
const PROGRAM: [u32; DONE as usize + 1] = [
jal(ZERO, rel(0, MAIN)),
sw(A1, T0, 0x0c),
lw(A0, T0, 0x08),
andi(A0, A0, 1),
beq(A0, ZERO, rel(4, XWAIT)),
lw(A0, T0, 0x0c),
ret(),
li_hi(T0, SPI1),
li_lo(T0, SPI1),
li_hi(T1, OCTOSPI),
li_lo(T1, OCTOSPI),
li_hi(T2, RAM),
li_lo(T2, RAM),
li_hi(A0, 4),
li_lo(A0, 4),
sw(A0, T0, 0x04),
li_hi(A0, SPI_CR1),
li_lo(A0, SPI_CR1),
sw(A0, T0, 0x00),
li_hi(A1, 0x9f),
li_lo(A1, 0x9f),
jal(RA, rel(21, XFER)),
li_hi(A1, 0),
li_lo(A1, 0),
jal(RA, rel(24, XFER)),
sb(A0, T2, 0),
li_hi(A1, 0),
li_lo(A1, 0),
jal(RA, rel(28, XFER)),
sb(A0, T2, 1),
li_hi(A1, 0),
li_lo(A1, 0),
jal(RA, rel(32, XFER)),
sb(A0, T2, 2),
sw(ZERO, T0, 0x00),
li_hi(A0, DCR1_1M),
li_lo(A0, DCR1_1M),
sw(A0, T1, 0x08),
li_hi(A0, 1),
li_lo(A0, 1),
sw(A0, T1, 0x00),
li_hi(A0, 1),
li_lo(A0, 1),
sw(A0, T1, 0x100),
li_hi(A0, 6),
li_lo(A0, 6),
sw(A0, T1, 0x110),
li_hi(A0, CCR_SINGLE_24),
li_lo(A0, CCR_SINGLE_24),
sw(A0, T1, 0x100),
li_hi(A0, 2),
li_lo(A0, 2),
sw(A0, T1, 0x110),
li_hi(A0, PAYLOAD_BYTES - 1),
li_lo(A0, PAYLOAD_BYTES - 1),
sw(A0, T1, 0x40),
li_hi(A0, 0),
li_lo(A0, 0),
sw(A0, T1, 0x48),
li_hi(A1, PAYLOAD as u32 * 4),
li_lo(A1, PAYLOAD as u32 * 4),
li_hi(A2, PAYLOAD_BYTES),
li_lo(A2, PAYLOAD_BYTES),
lbu(A3, A1, 0),
sb(A3, T1, 0x50),
addi(A1, A1, 1),
addi(A2, A2, -1),
bne(A2, ZERO, rel(67, COPY)),
li_hi(A0, CCR_SINGLE_24),
li_lo(A0, CCR_SINGLE_24),
sw(A0, T1, 0x100),
li_hi(A0, 8),
li_lo(A0, 8),
sw(A0, T1, 0x108),
li_hi(A0, 0x0b),
li_lo(A0, 0x0b),
sw(A0, T1, 0x110),
li_hi(A0, CR_MEMORY_MAPPED),
li_lo(A0, CR_MEMORY_MAPPED),
sw(A0, T1, 0x00),
li_hi(A5, WINDOW),
li_lo(A5, WINDOW),
jalr(ZERO, A5, 0),
li_hi(T1, RAM),
li_lo(T1, RAM),
li_hi(A0, SENTINEL),
li_lo(A0, SENTINEL),
sw(A0, T1, 4),
jal(ZERO, 0),
];
pub const SPI_FLASH_DEMO: &[u8] = &{
let mut out = [0u8; (DONE as usize + 1) * 4];
let mut i = 0;
while i < PROGRAM.len() {
let word = PROGRAM[i];
out[i * 4] = word as u8;
out[i * 4 + 1] = (word >> 8) as u8;
out[i * 4 + 2] = (word >> 16) as u8;
out[i * 4 + 3] = (word >> 24) as u8;
i += 1;
}
out
};
#[must_use]
pub fn payload() -> &'static [u8] {
let at = PAYLOAD as usize * 4;
&SPI_FLASH_DEMO[at..at + PAYLOAD_BYTES as usize]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_label_indices_match_the_program() {
assert_eq!(PROGRAM[XFER as usize], sw(A1, T0, 0x0c), "xfer");
assert_eq!(PROGRAM[XWAIT as usize], lw(A0, T0, 0x08), "xwait");
assert_eq!(PROGRAM[MAIN as usize], li_hi(T0, SPI1), "main");
assert_eq!(PROGRAM[COPY as usize], lbu(A3, A1, 0), "copy");
assert_eq!(PROGRAM[PAYLOAD as usize], li_hi(T1, RAM), "payload");
assert_eq!(PROGRAM[DONE as usize], jal(ZERO, 0), "done");
}
#[test]
fn the_payload_is_the_tail_of_the_image() {
assert_eq!(payload().len(), PAYLOAD_BYTES as usize);
assert_eq!(
payload(),
&SPI_FLASH_DEMO[SPI_FLASH_DEMO.len() - PAYLOAD_BYTES as usize..],
"the payload is the last thing in the image, so a test can compare \
the flash's contents with it directly"
);
}
#[test]
fn a_li_pair_reconstructs_the_value_it_names() {
for value in [SPI1, OCTOSPI, RAM, WINDOW, SENTINEL, CCR_SINGLE_24, DCR1_1M] {
let hi = (li_hi(A0, value) >> 12) << 12;
let lo = (li_lo(A0, value) as i32) >> 20;
assert_eq!(hi.wrapping_add(lo as u32), value, "{value:#x}");
}
}
}