Skip to content

Commit

Permalink
SPI DMA master for framebuffer transfers + hacks
Browse files Browse the repository at this point in the history
  • Loading branch information
vk2seb committed Oct 7, 2023
1 parent d7c8ab9 commit 21d6ec4
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 3 deletions.
6 changes: 6 additions & 0 deletions example-colorlight-i5.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

from rtl.pca9635_master import *

from spi_dma import Wishbone2SPIDMA

_io_eurolut_proto1 = [
("eurorack_pmod_p3b", 0,
Subsignal("mclk", Pins("A3")),
Expand Down Expand Up @@ -175,6 +177,10 @@ def add_oled(soc):
spi_master.add_clk_divider()
soc.submodules.oled_ctl = GPIOOut(soc.platform.request("oled_ctl"))

# SPI DMA for framebuffer dumping
soc.submodules.spi_dma = Wishbone2SPIDMA()
soc.bus.add_master(master=soc.spi_dma.bus)

def add_pca9635_master(soc):
pca9635_master = PCA9635Master(soc.platform, soc.platform.request("pca9635"))
soc.add_module("pca9635", pca9635_master)
Expand Down
23 changes: 21 additions & 2 deletions firmware/litex-fw/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use riscv_rt::entry;
use litex_hal::hal::digital::v2::OutputPin;
use heapless::String;
use embedded_midi::MidiIn;
use core::arch::asm;

use embedded_graphics::{
pixelcolor::{Gray4, GrayColor},
Expand Down Expand Up @@ -254,6 +255,15 @@ fn main() -> ! {
let mut modif: bool = false;
let mut btn_held_ms: u32 = 0;

unsafe {
peripherals.SPI_DMA.spi_control_reg_address.write(
|w| w.bits(litex_pac::OLED_SPI::PTR as u32));
peripherals.SPI_DMA.spi_status_reg_address.write(
|w| w.bits(litex_pac::OLED_SPI::PTR as u32 + 0x04));
peripherals.SPI_DMA.spi_mosi_reg_address.write(
|w| w.bits(litex_pac::OLED_SPI::PTR as u32 + 0x08));
}

loop {

Text::with_alignment(
Expand Down Expand Up @@ -388,13 +398,22 @@ fn main() -> ! {
.draw(&mut disp).ok();
}

disp.swap_clear();
let fb = disp.swap_clear();
unsafe {
while peripherals.SPI_DMA.done.read().bits() == 0 {
// Don't start to DMA a new framebuffer if we're still
// pushing through the last one.
}
peripherals.SPI_DMA.read_base.write(|w| w.bits(fb.as_ptr() as u32));
peripherals.SPI_DMA.read_length.write(|w| w.bits(fb.len() as u32));
peripherals.SPI_DMA.start.write(|w| w.start().bit(true));
peripherals.SPI_DMA.start.write(|w| w.start().bit(false));
}

let cycle_cnt_now = timer.uptime();
let cycle_cnt_last = cycle_cnt;
cycle_cnt = cycle_cnt_now;
let delta = (cycle_cnt_now - cycle_cnt_last) as u32;
td_us = Some(delta / (SYSTEM_CLOCK_FREQUENCY / 1_000_000u32));

}
}
2 changes: 1 addition & 1 deletion firmware/ssd1322
120 changes: 120 additions & 0 deletions spi_dma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/python3

from migen import *

from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone

SPI_START = ((8<<8) | (1<<0))
SPI_LENGTH = (1<<8)
SPI_DONE = (1<<0)

class Wishbone2SPIDMA(Module, AutoCSR):
def __init__(self):
# Wishbone
self.bus = bus = wishbone.Interface()

# Control
self.start = CSR()
self.done = CSRStatus()

# Read parameters: base and length of DMA
self.read_base = CSRStorage(32)
self.read_length = CSRStorage(32)

# SPI parameters: address of control/status/mosi registers
self.spi_control_reg_address = CSRStorage(32)
self.spi_status_reg_address = CSRStorage(32)
self.spi_mosi_reg_address = CSRStorage(32)

# # #

# Shorten CSR's names
start = self.start.re
done = self.done.status

read_base = self.read_base.storage[2:]
read_length = self.read_length.storage

spi_mosi_reg_address = self.spi_mosi_reg_address.storage[2:]
spi_control_reg_address = self.spi_control_reg_address.storage[2:]
spi_status_reg_address = self.spi_status_reg_address.storage[2:]

# internals
word_offset = Signal(32)
byte_offset = Signal(3)
byte_count = Signal(32)
data = Signal(32)

# fsm
self.submodules.fsm = fsm = FSM()
fsm.act("IDLE",
If(start,
NextValue(word_offset, 0),
NextValue(byte_offset, 0),
NextValue(byte_count, 0),
NextState("WISHBONE-READ-DATA")
).Else(
done.eq(1),
)
)
fsm.act("WISHBONE-READ-DATA",
bus.stb.eq(1),
bus.cyc.eq(1),
bus.sel.eq(2**(bus.data_width//8)-1),
bus.adr.eq(read_base + word_offset),
If(bus.ack,
NextValue(data, bus.dat_r),
NextState("SPI-WRITE-DATA")
)
)
fsm.act("SPI-WRITE-DATA",
bus.stb.eq(1),
bus.cyc.eq(1),
bus.we.eq(1),
bus.sel.eq(2**(bus.data_width//8)-1),
bus.adr.eq(spi_mosi_reg_address),
bus.dat_w.eq(data),
If(bus.ack,
NextState("SPI-WRITE-START")
)
)
fsm.act("SPI-WRITE-START",
bus.stb.eq(1),
bus.cyc.eq(1),
bus.we.eq(1),
bus.sel.eq(2**(bus.data_width//8)-1),
bus.adr.eq(spi_control_reg_address),
bus.dat_w.eq(SPI_START),
If(bus.ack,
NextState("SPI-WAIT-DONE")
)
)
fsm.act("SPI-WAIT-DONE",
bus.stb.eq(1),
bus.cyc.eq(1),
bus.sel.eq(2**(bus.data_width//8)-1),
bus.adr.eq(spi_status_reg_address),
If(bus.ack,
If(bus.dat_r & SPI_DONE,
NextValue(byte_count, byte_count + 1),
NextValue(byte_offset, byte_offset + 1),
NextState("SHIFT-BYTE")
)
)
)
fsm.act("SHIFT-BYTE",
If(byte_count >= read_length,
NextState("IDLE")
).Elif(byte_offset >= 4,
NextValue(byte_offset, 0),
NextState("INC-WORD-OFFSET")
).Else(
NextValue(data, data >> 8),
NextState("SPI-WRITE-DATA")
)
)
fsm.act("INC-WORD-OFFSET",
NextValue(word_offset, word_offset + 1),
NextState("WISHBONE-READ-DATA")
)

0 comments on commit 21d6ec4

Please sign in to comment.