Skip to content

Commit

Permalink
readme: update with dma router and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
vk2seb committed Dec 5, 2023
1 parent 4023a70 commit cd4c59e
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 70 deletions.
47 changes: 3 additions & 44 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,17 @@ name: build & test
on: [push]

jobs:
ubuntu-build-colorlight-i5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: YosysHQ/setup-oss-cad-suite@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: True
components: rustfmt, clippy
target: riscv32imac-unknown-none-elf
- run: cargo install svd2rust
- run: svd2rust --version
- uses: gregdavill/setup-riscv-gnu-toolchain@v2
- run: riscv-none-elf-gcc --version
- name: Install python packages
run: |
python3 -m pip install setuptools
python3 -m pip install pycrc
python3 -m pip install wheel
python3 -m pip install construct
python3 -m pip install Sphinx sphinxcontrib-wavedrom meson ninja setuptools_scm Jinja2
- run: git submodule update --init --recursive
- run: mkdir -p build
- run: python3 example-colorlight-i5.py --ecppack-compress --cpu-type vexriscv --cpu-variant imac --csr-svd build/colorlight_i5/csr.svd --build
- uses: actions/upload-artifact@v3
with:
name: bitstream-artifacts
path: |
build/colorlight_i5/gateware/colorlight_i5.bit
build/colorlight_i5/csr.svd
- name: Build firmware (rust)
run: |
cd firmware
BOARD=colorlight_i5 ./build.sh
- uses: actions/upload-artifact@v3
with:
name: firmware-artifacts
path: |
build/colorlight_i5/rust-fw.bin
ubuntu-build-ecpix-5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: YosysHQ/setup-oss-cad-suite@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: nightly
override: True
components: rustfmt, clippy
target: riscv32imac-unknown-none-elf
target: riscv32im-unknown-none-elf
- run: cargo install svd2rust
- run: svd2rust --version
- uses: gregdavill/setup-riscv-gnu-toolchain@v2
Expand All @@ -68,7 +27,7 @@ jobs:
python3 -m pip install Sphinx sphinxcontrib-wavedrom meson ninja setuptools_scm Jinja2
- run: git submodule update --init --recursive
- run: mkdir -p build
- run: python3 example-ecpix-5.py --ecppack-compress --cpu-type vexriscv --cpu-variant imac --csr-svd build/lambdaconcept_ecpix5/csr.svd --build
- run: python3 example-ecpix-5.py --ecppack-compress --cpu-type vexriscv_smp --cpu-variant standard --csr-svd build/lambdaconcept_ecpix5/csr.svd --lx-ignore-deps --timer-uptime --cpu-count 1 --with-wishbone-memory --build
- uses: actions/upload-artifact@v3
with:
name: bitstream-artifacts
Expand Down
97 changes: 80 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@

# `eurorack-pmod` LiteX examples

Basic example of using `eurorack-pmod` inside a LiteX environment with firmware written in Rust.
Example of using `eurorack-pmod` inside a LiteX environment with firmware written in Rust.

It consists of a basic SoC that includes one instance of `eurorack-pmod` working alongside a soft RISCV CPU. Currently only tested on Colorlight i5, ECPIX-5 but should be quite easy to port to other ECP5 boards.
The `example-ecpix-5.py` SoC has the following main parts:
- A softcore (RISCV `vexriscv_smp`, here we use `rv32im` without compressed instructions).
- An instance of `eurorack-pmod` gateware where inputs/outputs can be peeked or poked through CSRs.
- A custom `dma_router` DMA engine which can shuttle data between RAM and the eurorack-pmod in real-time for glitch-free audio processing on the softcore (i.e in firmware).

This example targets ECPIX-5, however given how little platform-specific code is in the SoC implementation, you should be able to easily port this to other FPGA boards.

# Example firmware

The example firmware, primarily found in `firmware/litex-fw/src` sets up the RISCV interrupt controller and maps the DMA interrupts such that we fire an IRQ at half and full on a shared circular DMA buffer.

Some example fixed-point DSP code in `dsp.rs` implements a low-pass filter. This example uses audio channel 1 as audio input, and audio channel 2 (CV) as the filter cutoff.

# Dependencies

Expand All @@ -13,8 +24,8 @@ For building the bitstream and LiteX BIOS:
- [RISCV toolchain](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/install/)

For building the firmware:
- Rust toolchain with some extras:
- `rustup target add riscv32imac-unknown-elf`
- Rust NIGHTLY toolchain with some extras:
- `rustup target add riscv32im-unknown-elf`
- `cargo install svd2rust`

The bitstream and firmware are built in CI inside `.github/workflows/main.yml`, it may be useful to look at if you are missing some dependencies.
Expand All @@ -25,18 +36,14 @@ The bitstream and firmware are built in CI inside `.github/workflows/main.yml`,
```
<from this repository>
git submodule update --init --recursive
# e.g. for Colorlight i5
python3 example-colorlight-i5.py --ecppack-compress --cpu-type vexriscv --cpu-variant imac --csr-svd build/colorlight_i5/csr.svd --build
# e.g. for ECPIX-5
python3 example-ecpix-5.py --ecppack-compress --cpu-type vexriscv --cpu-variant imac --csr-svd build/lambdaconcept_ecpix5/csr.svd --build
python3 example-ecpix-5.py --ecppack-compress --cpu-type vexriscv_smp --cpu-variant standard --csr-svd build/lambdaconcept_ecpix5/csr.svd --lx-ignore-deps --timer-uptime --cpu-count 1 --with-wishbone-memory --build
```

# Flashing the bitstream

```
# e.g. for Colorlight i5
openFPGALoader -b colorlight-i5 build/colorlight_i5/gateware/colorlight_i5.bit
# e.g. for ECPIX-5
# e.g. for latest ECPIX-5 revision
openFPGALoader -b ecpix5 --vid 0x0403 --pid 0x6011 build/lambdaconcept_ecpix5/gateware/lambdaconcept_ecpix5.bit
```

Expand All @@ -48,23 +55,79 @@ To build the bindings for LiteX CSRs (`litex-pac`), compile the firmware, and co

```
cd firmware/
# e.g. for Colorlight i5
BOARD=colorlight_i5 ./build.sh
# e.g. for ECPIX-5 with your own OBJCOPY binary --
BOARD=lambdaconcept_ecpix5 OBJCOPY=riscv64-elf-objcopy ./build.sh
```

To see the LiteX terminal and download firmware to the device (again replacing all instances of the board with your own type) --
# Running the firmware

To see the LiteX terminal and download firmware to the device --

```
<from repo root>
./bin/litex_term --kernel build/colorlight_i5/rust-fw.bin /dev/ttyACM0
./bin/litex_term --kernel build/lambdaconcept_ecpix5/rust-fw.bin /dev/ttyACM0
You should see something like this:
```
__ _ __ _ __
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/\__/\__/_/|_|
Build your hardware, easily!

(c) Copyright 2012-2023 Enjoy-Digital
(c) Copyright 2007-2015 M-Labs

BIOS built on Dec 5 2023 16:19:28
BIOS CRC passed (44bb1c1c)

LiteX git sha1: 2bf54c2d

--=============== SoC ==================--
CPU: VexRiscv SMP-STANDARD @ 75MHz
BUS: WISHBONE 32-bit @ 4GiB
CSR: 32-bit data
ROM: 128.0KiB
SRAM: 8.0KiB
L2: 8.0KiB
SDRAM: 512.0MiB 16-bit @ 300MT/s (CL-6 CWL-5)
MAIN-RAM: 512.0MiB

--========== Initialization ============--
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
....... and so on
```
Note -- firmware uses the `defmt` rust framework for logging such that we can print things quite a bit faster than ordinary UART/printf. Basically all strings are stripped from firmware on the device and the original .elf file is used to decode strings inside the `defmt` stream from the device. You must use `defmt-print` to reinterpret the UART stream into something you can read. To flash the firmware and see the output of the application you normally want something like:
And then once your firmware is loaded:
```
./bin/litex_term --kernel build/colorlight_i5/rust-fw.bin /dev/ttyACM0 | defmt-print -e firmware/litex-fw/target/riscv32imac-unknown-none-elf/release/litex-fw
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LITEX-TERM] Received firmware download request from the device.
[LITEX-TERM] Uploading build/lambdaconcept_ecpix5/rust-fw.bin to 0x40000000 (13256 bytes)...
[LITEX-TERM] Upload calibration... (inter-frame: 10.00us, length: 64)
[LITEX-TERM] Upload complete (9.8KB/s).
[LITEX-TERM] Booting the device.
[LITEX-TERM] Done.
Executing booted program at 0x40000000

--============= Liftoff! ===============--
UART logger up!
hello from litex-fw!
<... garbage here ...>
irq_period: 199979
irq_len: 10093
irq_load_percent: 4
0@f4f0,e115
1@1c7c,1c7e
2@0,0
3@0,0
```
You can build your own `defmt-print` by cloning the `defmt` repository (see their documentation).
In this example on the ECPIX-5 the simple LPF DSP algorithm is consuming <5% of the available softcore CPU bandwidth. You also see a dump of the ADC sample values for each channel and see how often the IRQ is firing (200k cycles of 75e6Hz is about once every 2.7msec, which can be helpful for calculating latency).
4 changes: 2 additions & 2 deletions firmware/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ svd2rust --log error -i $BUILD_DIR/csr.svd --target riscv

# Build the firmware .elf file
cd $FW_ROOT/litex-fw
cargo build --target=riscv32imac-unknown-none-elf --release
cargo build -Zbuild-std=core --target=riscv32im-unknown-none-elf --release

# Copy it into a binary that litex_term can upload.
${OBJCOPY} target/riscv32imac-unknown-none-elf/release/litex-fw -O binary $BUILD_DIR/rust-fw.bin
${OBJCOPY} target/riscv32im-unknown-none-elf/release/litex-fw -O binary $BUILD_DIR/rust-fw.bin
7 changes: 2 additions & 5 deletions firmware/litex-fw/.cargo/config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[target.riscv32imac-unknown-none-elf]
[target.riscv32im-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tregions.ld",
"-C", "link-arg=-Tmemory.x",
Expand All @@ -7,7 +7,4 @@ rustflags = [
]

[build]
target = "riscv32imac-unknown-none-elf"

#[profile.release]
#opt-level = 1
target = "riscv32im-unknown-none-elf"
6 changes: 5 additions & 1 deletion firmware/litex-fw/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ panic-halt = "*"
litex-hal = { path = "../deps/rust-litex-hal" }
litex-pac = { path = "../deps/generated-litex-pac", features = ["rt"] }
micromath = "2.0.0"
heapless = { version = "0.7.16", features = ["ufmt-impl"] }
heapless = { version = "0.7.16", default-features = false, features = ["ufmt-impl", "atomic-polyfill"] }
ufmt = "0.2.0"
aligned-array = "1.0.1"
fixed = "1.24.0"

[profile.release]
lto = true
opt-level = 3
2 changes: 1 addition & 1 deletion firmware/litex-fw/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use plic::*;
use dsp::*;

/// TODO: Modify `svd2rust` so this can be automatically forwarded?
const SYSTEM_CLOCK_FREQUENCY: u32 = 60_000_000;
const SYSTEM_CLOCK_FREQUENCY: u32 = 75_000_000;

/// Number of channels per section (4x input, 4x output)
const N_CHANNELS: usize = 4;
Expand Down

0 comments on commit cd4c59e

Please sign in to comment.