SoC Projects
Background: Tiliqua SoC designs
Many Tiliqua projects contain an SoC alongside the DSP logic, in an arrangement like this:
Overview
At a very high level, we have a vexriscv RISCV softcore running firmware (written in Rust), that interfaces with a bunch of peripherals through CSR registers. As the Vex also runs the menu system, often there is a dedicated peripheral with CSRs used to tweak parameters of the DSP pipeline.
PSRAM
PSRAM bandwidth is important to keep under control. For this reason, the SoC only interfaces with the PSRAM for text/line draw operations. Normal instruction and data fetches are to a local SRAM and so do not touch external PSRAM (which is usually hammered with video traffic).
TODO: describe each peripheral in detail
All Projects
polysyn
8-voice polyphonic synthesizer with video display and menu system.
Pitch / Touch Audio / CV ┌────┐ C2 touch0 │in0 │◄─ phase modulation G2 touch1 │in1 │◄─ - C3 touch2 │in2 │◄─ - Eb3 touch3 │in3 │◄─ - └────┘ ┌────┐ G3 touch4 │out0│─► - C4 touch5 │out1│─► - - touch6 │out2│─► audio out (L) - touch7 │out3│─► audio out (R) └────┘
The synthesizer can be controlled through touching jacks 0-5 or using a MIDI keyboard (TRS MIDI or USB host is supported). The control source must be selected in the menu system.
Output audio is sent to output channels 2 and 3 (last 2 jacks).
In touch mode, the touch magnitude controls the filter envelopes of each voice. In MIDI mode, the velocity of each note as well as the value of the modulation wheel affects the filter envelopes.
Input jack 0 also controls phase modulation of all oscillators, so you can patch input jack 0 to an LFO for retro-sounding slow vibrato, or to an oscillator for some wierd FM effects.
Each voice is hard panned left or right in the stereo field, with 2 end-of-chain effects: distortion and diffusion (delay), both of which can be mixed in with the UI.
USB MIDI TRS MIDI ─────┐ ┌───── (`usb-host` setting) ┌─────▼─▼─────┐ touch──►│Voice Tracker├──►┌─────────┐ └─────────────┘ │ Voices │ ┌─────────┐ │ (x8) │ (resonance = cv in0─►Phase Mod├──►│NCO + SVF│ (`reso` setting) └─────────┘ └─────────┘ ┌────▼────┐ │Mix L/R │ │(stereo) │ └────┬────┘ ┌────▼────┐ (wet/dry mix = │Diffuser │ `diffuse` setting) └────┬────┘ ┌────▼────┐ │ Drive │ (`drive` setting) └────┬────┘ ┌────┴───┐ │Audio │ │OUT (4x)├──────► out2 (L) └────────┴──────► out3 (R)
- class top.polysyn.top.PolySoc(*args, src_loc_at=0, **kwargs)
8-voice polyphonic synthesizer with video display and menu system.
Pitch / Touch Audio / CV ┌────┐ C2 touch0 │in0 │◄─ phase modulation G2 touch1 │in1 │◄─ - C3 touch2 │in2 │◄─ - Eb3 touch3 │in3 │◄─ - └────┘ ┌────┐ G3 touch4 │out0│─► - C4 touch5 │out1│─► - - touch6 │out2│─► audio out (L) - touch7 │out3│─► audio out (R) └────┘The synthesizer can be controlled through touching jacks 0-5 or using a MIDI keyboard (TRS MIDI or USB host is supported). The control source must be selected in the menu system.
Output audio is sent to output channels 2 and 3 (last 2 jacks).
In touch mode, the touch magnitude controls the filter envelopes of each voice. In MIDI mode, the velocity of each note as well as the value of the modulation wheel affects the filter envelopes.
Input jack 0 also controls phase modulation of all oscillators, so you can patch input jack 0 to an LFO for retro-sounding slow vibrato, or to an oscillator for some wierd FM effects.
Each voice is hard panned left or right in the stereo field, with 2 end-of-chain effects: distortion and diffusion (delay), both of which can be mixed in with the UI.
USB MIDI TRS MIDI ─────┐ ┌───── (`usb-host` setting) ┌─────▼─▼─────┐ touch──►│Voice Tracker├──►┌─────────┐ └─────────────┘ │ Voices │ ┌─────────┐ │ (x8) │ (resonance = cv in0─►Phase Mod├──►│NCO + SVF│ (`reso` setting) └─────────┘ └─────────┘ ┌────▼────┐ │Mix L/R │ │(stereo) │ └────┬────┘ ┌────▼────┐ (wet/dry mix = │Diffuser │ `diffuse` setting) └────┬────┘ ┌────▼────┐ │ Drive │ (`drive` setting) └────┬────┘ ┌────┴───┐ │Audio │ │OUT (4x)├──────► out2 (L) └────────┴──────► out3 (R)
xbeam
Vectorscope/oscilloscope with menu system, USB audio and tunable delay lines.
In vectorscope mode, rasterize X/Y, intensity and color to a simulated CRT, with adjustable beam settings, scale and offset for each channel.
In oscilloscope mode, all 4 input channels are plotted simultaneosly with adjustable timebase, trigger settings and so on.
The channels are assigned as follows:
Vectorscope │ Oscilloscope ┌────┐ │ │in0 │◄─ x │ channel 0 + trig │in1 │◄─ y │ channel 1 │in2 │◄─ intensity │ channel 2 │in3 │◄─ color │ channel 3 └────┘
A USB audio interface, tunable delay lines, and series of switches is included in the signal path to open up more applications. The overall signal flow looks like this:
in0/x ───────►┌───────┐ in1/y ───────►│Audio │ in2/i ───────►│IN (4x)│ in3/c ───────►└───┬───┘ ▼ ┌───◄─[SPLIT]─►────┐ │ │ ▼ │ ▼ ┌──────────────┐ ┌────────┐ │ │ │4in/4out USB ├────►│Computer│ │ │ │Audio I/F │◄────│(USB2) │ │ │ └──────┬───────┘ └────────┘ │ └───┐ ┌───┘ │ usb=bypass ▼ ▼ usb=enabled │ [MUX] │ ┌──────────────┐ │ │4x Delay Lines│ (tunable) │ └──────┬───────┘ │ ▼ └────┐ ┌─◄─[SPLIT]─►────┐ │ │ │ src=inputs ▼ ▼ src=outputs │ [MUX] │ │ ▼ ┌─────▼──────┐ ┌────────┬──────► out0 │Vectorscope/│ │Audio ├──────► out1 │Oscilloscope│ │OUT (4x)├──────► out2 └────────────┘ └────────┴──────► out3
The [MUX] elements pictured above can be switched by the menu system, for
viewing different parts of the signal path (i.e inputs or outputs to delay
lines, USB streams). Some usage ideas:
With
plot_src=inputsandusb_mode=bypass, we can visualize our analog audio inputs.With
plot_src=outputsandusb_mode=bypass, we can visualize our analog audio inputs after being affected by the delay lines (this is fun to get patterns out of duplicated mono signals)With
plot_src=outputsandusb_mode=enable, we can visualize a USB audio stream as it is sent to the analog outputs. This is perfect for visualizing oscilloscope music being streamed from a computer.With
plot_src=inputsandusb_mode=enable, we can visualize what we are sending back to the computer on our analog inputs.Note
The USB audio interface will always enumerate if it is connected to a computer, however it is only part of the signal flow if
usb_mode=enabledin the menu system.Note
By default, this core builds for
48kHz/16bitsampling. However, Tiliqua is shipped with--fs-192khzenabled, which provides much higher fidelity plots. If you’re feeling adventurous, you can also synthesize with the environment variableTILIQUA_ASQ_WIDTH=24to use a completely 24-bit audio path. This mostly works, but might break the scope triggering and use a bit more FPGA resources.
- class top.xbeam.top.XbeamSoc(*args, src_loc_at=0, **kwargs)
Vectorscope/oscilloscope with menu system, USB audio and tunable delay lines.
In vectorscope mode, rasterize X/Y, intensity and color to a simulated CRT, with adjustable beam settings, scale and offset for each channel.
In oscilloscope mode, all 4 input channels are plotted simultaneosly with adjustable timebase, trigger settings and so on.
The channels are assigned as follows:
Vectorscope │ Oscilloscope ┌────┐ │ │in0 │◄─ x │ channel 0 + trig │in1 │◄─ y │ channel 1 │in2 │◄─ intensity │ channel 2 │in3 │◄─ color │ channel 3 └────┘
A USB audio interface, tunable delay lines, and series of switches is included in the signal path to open up more applications. The overall signal flow looks like this:
in0/x ───────►┌───────┐ in1/y ───────►│Audio │ in2/i ───────►│IN (4x)│ in3/c ───────►└───┬───┘ ▼ ┌───◄─[SPLIT]─►────┐ │ │ ▼ │ ▼ ┌──────────────┐ ┌────────┐ │ │ │4in/4out USB ├────►│Computer│ │ │ │Audio I/F │◄────│(USB2) │ │ │ └──────┬───────┘ └────────┘ │ └───┐ ┌───┘ │ usb=bypass ▼ ▼ usb=enabled │ [MUX] │ ┌──────────────┐ │ │4x Delay Lines│ (tunable) │ └──────┬───────┘ │ ▼ └────┐ ┌─◄─[SPLIT]─►────┐ │ │ │ src=inputs ▼ ▼ src=outputs │ [MUX] │ │ ▼ ┌─────▼──────┐ ┌────────┬──────► out0 │Vectorscope/│ │Audio ├──────► out1 │Oscilloscope│ │OUT (4x)├──────► out2 └────────────┘ └────────┴──────► out3The
[MUX]elements pictured above can be switched by the menu system, for viewing different parts of the signal path (i.e inputs or outputs to delay lines, USB streams). Some usage ideas:With
plot_src=inputsandusb_mode=bypass, we can visualize our analog audio inputs.With
plot_src=outputsandusb_mode=bypass, we can visualize our analog audio inputs after being affected by the delay lines (this is fun to get patterns out of duplicated mono signals)With
plot_src=outputsandusb_mode=enable, we can visualize a USB audio stream as it is sent to the analog outputs. This is perfect for visualizing oscilloscope music being streamed from a computer.With
plot_src=inputsandusb_mode=enable, we can visualize what we are sending back to the computer on our analog inputs.
Note
The USB audio interface will always enumerate if it is connected to a computer, however it is only part of the signal flow if
usb_mode=enabledin the menu system.Note
By default, this core builds for
48kHz/16bitsampling. However, Tiliqua is shipped with--fs-192khzenabled, which provides much higher fidelity plots. If you’re feeling adventurous, you can also synthesize with the environment variableTILIQUA_ASQ_WIDTH=24to use a completely 24-bit audio path. This mostly works, but might break the scope triggering and use a bit more FPGA resources.
macro_osc
‘Macro-Oscillator’ runs a downsampled version of the DSP code from a famous Eurorack module (credits below), on a softcore, to demonstrate the compute capabilities available if you do everything in software, using a really large CPU that has big caches and an FPU.
┌────┐ │in0 │◄─ frequency modulation │in1 │◄─ trigger │in2 │◄─ timbre modulation │in3 │◄─ morph modulation └────┘ ┌────┐ │out0│─► - │out1│─► - │out2│─► 'out' output (mono) │out3│─► 'aux' output (mono) └────┘
Most engines are available for tweaking and patching via the UI. A couple of engines use a bit more compute and may cause the UI to slow down or audio to glitch, so these ones are disabled. A scope and vectorscope is included and hooked up to the oscillator outputs so you can visualize exactly what the softcore is spitting out.
(write samples to) ┌─────────────────┐ │ ▼ poll ┌────┴─────┐┌───────────────────┐ audio/ ────►│VexiiRiscv││AudioFifoPeripheral│ CV └──────────┘└──────────┬────────┘ (plaits ▼ engines) ┌──[SPLIT]────────► │ (audio out) ▼ ┌────────────┐ │Vectorscope/│ │Oscilloscope│ └────────────┘
The original module was designed to run at 48kHz. Here, we instantiate a powerful (rv32imafc) softcore (this one includes an FPU), which is enough to run most engines at ~24kHz-48kHz, however with the video and menu system running simultaneously, it’s necessary to clock this down to 24kHz. Surprisingly, most engines still sound reasonable. The resampling from 24kHz <-> 48kHz is performed in hardware below.
There is quite some heavy compute here and RAM usage, as a result, the audio buffers are too big to fit in BRAM. In this demo, both the firmware and the DSP buffers are allocated from external PSRAM.
Credits to Emilie Gillet for the original Plaits module and firmware.
- Credits to Oliver Rockstedt for the Rust port of said firmware:
The Rust port is what is running on this softcore.
- class top.macro_osc.top.MacroOscSoc(*args, src_loc_at=0, **kwargs)
‘Macro-Oscillator’ runs a downsampled version of the DSP code from a famous Eurorack module (credits below), on a softcore, to demonstrate the compute capabilities available if you do everything in software, using a really large CPU that has big caches and an FPU.
┌────┐ │in0 │◄─ frequency modulation │in1 │◄─ trigger │in2 │◄─ timbre modulation │in3 │◄─ morph modulation └────┘ ┌────┐ │out0│─► - │out1│─► - │out2│─► 'out' output (mono) │out3│─► 'aux' output (mono) └────┘
Most engines are available for tweaking and patching via the UI. A couple of engines use a bit more compute and may cause the UI to slow down or audio to glitch, so these ones are disabled. A scope and vectorscope is included and hooked up to the oscillator outputs so you can visualize exactly what the softcore is spitting out.
(write samples to) ┌─────────────────┐ │ ▼ poll ┌────┴─────┐┌───────────────────┐ audio/ ────►│VexiiRiscv││AudioFifoPeripheral│ CV └──────────┘└──────────┬────────┘ (plaits ▼ engines) ┌──[SPLIT]────────► │ (audio out) ▼ ┌────────────┐ │Vectorscope/│ │Oscilloscope│ └────────────┘The original module was designed to run at 48kHz. Here, we instantiate a powerful (rv32imafc) softcore (this one includes an FPU), which is enough to run most engines at ~24kHz-48kHz, however with the video and menu system running simultaneously, it’s necessary to clock this down to 24kHz. Surprisingly, most engines still sound reasonable. The resampling from 24kHz <-> 48kHz is performed in hardware below.
There is quite some heavy compute here and RAM usage, as a result, the audio buffers are too big to fit in BRAM. In this demo, both the firmware and the DSP buffers are allocated from external PSRAM.
Credits to Emilie Gillet for the original Plaits module and firmware.
- Credits to Oliver Rockstedt for the Rust port of said firmware:
The Rust port is what is running on this softcore.
sid
This example instantiates a SID chip, which can be modulated via CV.
┌────┐ │in0 │◄─ modulation source 0 │in1 │◄─ modulation source 1 │in2 │◄─ modulation source 2 │in3 │◄─ modulation source 3 └────┘ ┌────┐ │out0│─► voice 0 (solo) │out1│─► voice 1 (solo) │out2│─► voice 2 (solo) │out3│─► voices 0-2 (sum) └────┘
Using the menu system, each input channel can be assigned to a modulation target (i.e pitch / gate of specific voices or multiple voices).
The soft CPU then uses this mapping to redirect CV to perform specific register writes on the SID chip. To add new modulation types or for more complex modulation, only the rust firmware needs to be changed.
The audio routing out the SID chip to the audio outputs however is pure gateware. The softcore is only used for register writes.
┌──────────┐ ┌───┐ (poll CV) ─────►│VexiiRiscv│ │SID│ ─────► (audio out) └────┬─────┘ └───┘ │ ▲ └──────────┘ (register writes)
There is a lot of design space left to explore here. For example, adding MIDI input, more modulation sources, adding end of chain effects and so on…
- class top.sid.top.SIDSoc(*args, src_loc_at=0, **kwargs)
This example instantiates a SID chip, which can be modulated via CV.
┌────┐ │in0 │◄─ modulation source 0 │in1 │◄─ modulation source 1 │in2 │◄─ modulation source 2 │in3 │◄─ modulation source 3 └────┘ ┌────┐ │out0│─► voice 0 (solo) │out1│─► voice 1 (solo) │out2│─► voice 2 (solo) │out3│─► voices 0-2 (sum) └────┘
Using the menu system, each input channel can be assigned to a modulation target (i.e pitch / gate of specific voices or multiple voices).
The soft CPU then uses this mapping to redirect CV to perform specific register writes on the SID chip. To add new modulation types or for more complex modulation, only the rust firmware needs to be changed.
The audio routing out the SID chip to the audio outputs however is pure gateware. The softcore is only used for register writes.
┌──────────┐ ┌───┐ (poll CV) ─────►│VexiiRiscv│ │SID│ ─────► (audio out) └────┬─────┘ └───┘ │ ▲ └──────────┘ (register writes)There is a lot of design space left to explore here. For example, adding MIDI input, more modulation sources, adding end of chain effects and so on…
selftest
Collect some information about Tiliqua health, display it on the video output and log it over serial. This is mostly used to check for hardware issues and for calibration.