ToneCore DSP Developer Kit Guide - Projects - Audio Artillery

ToneCore DSP Developer Kit Guide

  1. So you want to design your own effects?
  2. Specs
  3. Signal Processing for Dummies
  4. Assembly Programming
  5. Program Flow
  6. Delay Pedal Example
  7. Debug Interfaces
  8. Using the Simulator
  9. Prototyping in Python
  10. Developing under Linux (or OS X)
  11. Code Library

So you want to design your own effects?

Note: this page is a work in progress.


This page is intended to help new users of the Line6 ToneCore DSP Developer Kit get up to speed. You should also read the TCDDK FAQ and Getting Started pages as well. And if you get stuck go ask questions in the Line6 forum.

Unless you're already a DSP developer of some sort you're going to find yourself stretched with this kit. But hang in there, it's really not as hard as it seems at first.


 

Specs

Here's what is under the hood:

  • Freescale DSP56364 24-bit DSP processor @ 100 MHz.
  • Memory:
    • 1.5 Kwords program RAM
    • 1 Kwords X RAM
    • 0.75 Kwords Y RAM
    • 512 KB external SRAM (accessible via DMA or normal move instructions). The SRAM is mapped to address 0x20000 in both X and Y memory spaces.
  • DAC/ADC/other audio glue logic to get samples in and out of the DSP. Sample frequency is 39.0625 kHz.
  • Some regular CPU that deals with loading your program from flash and updating the GUI. You can ignore this CPU entirely for most effects.

Note that 100 MHz/39.0625 kHz = 2560 CPU cycles per audio sample (assuming a mono effect and optimized code). This is quite a bit of horsepower.


 

Signal Processing for Dummies

Here we'll discuss briefly how to think about an effect and basic building blocks. We'll keep it simple because most people don't have a background in signal processing.

The analog waveform from your guitar is sampled into a series of digital waveform points. This is done using an ADC. Beyond the ADC the audio data is equivalent to a WAV file on your PC or a captured recording in your PC's recording software. On the output side of the effect is a DAC which converts back to an analog signal.


Once in the digital domain we can modify the signal easily using a processor. In our case we are using a specialized DSP processor. Because of the sheer amount of data involved (2 channels * 24 bits * 39.0625 kHz = ~19Mbps) and the sensitivity of the human ear to latency, we tend to act on the data one sample at a time. As an example, say you made an effect that merely reduced the signal by 6 dB. You would take each sample from the ADC, divide it by 2, and then output it. And you would repeat this for each sample. To bypass the effect you would merely output the input sample without dividing. Easy enough, right?

Many effects use input or output samples from the past to compute the effect. Obvious examples of this are delay and reverb effects. You can hear the past input samples in a delay effect clearly if the delay is long enough. So one basic building block you'll be using is a delay memory. On the TCDDK this comes in the form of the external memory. You can store up to ~4.5 seconds of samples there which is plenty for delay/reverb type effects. It convenient to use the external memory as a circular buffer.


 

Assembly Programming

"Give me a lever and I will move the contents of the accumulator to memory." -- Archimedes, 225 B.C.

If you bought the kit under the impression that C programming was possible you're probably a little mad right now. It is possible with a non-free compiler (the Tasking toolchain). But forget about that, we're going to do it all in assembly because we're real men and that's what real men program in when nobody gives them a compiler. We'll also end up learning a lot more about how the DSP works by coding for it in assembly.

The assembly language used is Motorola 56k assembly. The Freescale 563xx line is a spinoff of the Motorola 563xx line and uses the same tools. The good news is the 563xx processors are very simple and are easy to program for.

Most of the syntax can be summed up by this example:

my_loop
        ; some instructions to populate some registers
        move #>0.5,x0
        move #>0.2,x1
        
        mpy x0,x1,a ; x0*x1 -> a

        jmp my_loop
This shows a few things. Labels ("my_loop") are anything that starts in the first column. The assembler converts these to addresses, so you can jump/branch to them. Instructions must be one or more spaces/tabs in. Operands to the instructions are comma separated (no spaces!). Comments are anything following a semicolon. Easy enough, right?

The below table lists some of the instructions you'll be using most frequently. Freescale's DSP56300 family manual contains full documentation for all instructions.

Fundamental Instructions
InstructionUsageExample
moveMove between registers or between a register and a memory location. Equivalent to store/load instructions on some other processors.
move #>$0a0b0c,x0 ; x0 gets 0x0a0b0c
move x0,a ; a gets x0
jmpJumps to a particular place in memory. Typically used to jump to a label.
jmp SomeLabel
jsrJumps to a sub routine. Similar to jmp except you can use rts to get back.
jsr SomeSubRoutine
cmpComparison. Use with conditional branches (beq, bne, blt, ble, bgt, bge, etc) to achieve program control.
move x0,a
cmp x1,a ; do comparison
blt SomeLabel ; branch if a < x1
add, subAddition and subtraction.
; add x0 + x1
move x1,a
add x0,a ; result in a
; subtract x0 - x1
move x1,a
sub x0,a ; result in a
; increment x0 by 5
move x0,a
add #5,a
move a,x0
mpy, divMultiply and divide. Note that these are fixed-point operations and won't do exactly what you expect with plain integers.
mpy x0, x1, a ; a gets x0*x1

One last thing about the DSP code. The DSP processor can execute certain things in parallel. I don't have a good handle on this yet. It appears that moves involving different data busses can be parallelized. You'll see instructions that have more than one set of operands in the EQ example code; these are parallel operations. This detail can be ignored for the most part until you are hurting for cycles.


 

Program Flow

Starting with the example code (the EQ example from Line6) is the best thing to do. Read it over and then read Getting Started for an explanation of the major sections. The even RX ISR (esai_rxeven_isr) is where the samples are received from the ADC and sent out to the DAC.


 

Delay Pedal Example

Delay is an easy effect to implement and it will famliarize you with the external memory interface and DMA engine. Below is a diagram of a simple delay effect. Input samples are saved into the circular buffer and summed with a previous sample from N samples in the past. Usually the past sample is attenuated. Both the attenuation and the delay length (N) are typically controlled by the user (knobs).


More complicated delay effects may have many delayed copies (N, 2N, 3N, etc) and may do some amount of processing on the delayed copies to emulate a particular analog delay effect.

TODO show how to modify the EQ example to be a delay pedal.


 

Debug Interfaces

What you really need is a JTAG emulator and maybe an ETM. What you have is a GUI that can read four special memory locations and write four others. Good thing we're real men.

For simple unit testing of a routine you can use the writable debug registers ("Debug write to DSP X") to input values and the readable debug registers ("Debug read from DSP X") to output results. This is quite useful for many things. You can also just output interesting things (for example, the input and output values) to the readable registers. You can't really read the data fast enough but it can give you a clue what is going on (you can see if any signal is getting into your device, for example).

There is one more interface you've probably overlooked. The dev kit pedal is a stereo pedal. Unless you're doing a stereo effect you can shove all kinds of data out the 2nd channel. You could then record both channels into your PC and have a pretty nice waveform of the output of your effect plus some debug value. I haven't done this yet but it is possible.


 

Using the Simulator

Configuration steps:

  1. Start gui56300.exe.
  2. Configure device (Dv0) to be 56364 processor.
  3. Load COFF and select your .cld output file.
  4. Add a working path so that it can find your source code. It didn't work initially for me, but did the 2nd time.
  5. Do normal debugging things (run, stop, set breakpoint, etc). The Source window is what you want to be looking at.
Note that the sample in/out interrupts (esai_xxxx_isr) will not be firing in the simulator, but the main loop will be functional.


 

Prototyping in Python

Because of the difficulty in debugging assembly code on target (and even in the simulator) it makes sense to prototype some things first. I use Python. That way I can get all the kinks worked out of an algorithm and then port it to the native assembly. For more complex things like signal processing I can leverage PyLab to manipulate large amounts of data, graph things, etc.

Here is a simple example of a routine I wrote to do integer multiplication. I first implemented in Python:


Then copied it into my assembly project, commented out all the Python code, and filled in the equivalent assembly code:


Note that the selection of registers is arbitrary in most cases. You're limited to A/B registers when doing multiply/accumulate instructions but otherwise it's mostly up to you, you're the compiler. When everything is done you can rip out the Python comments:


But I usually leave them in. That way if I find bugs I can fix them in the Python version and propagate the change easily.


 

Developing under Linux (or OS X)

I have been developing on Linux and using a virtual machine running Windows to talk to the pedal. It works well. Here are my notes on getting this running.

  1. Using VirtualBox 3.1.2 binary from Sun. Open-source edition doesn't supportt USB.
  2. WinXP SP2 guest on a Fedora 7 host. Presumably any Linux host will do (OS X??? I don't see why not).
  3. Not sure if this helped (I think this is more for an issue w/ thumbdrives and older VirtualBox releases):
    $ mount -t usbfs -o remount,devgid=$(awk -F: '/^vboxusers:/{print $3}' /etc/group),devmode=664  /proc/bus/usb /proc/bus/usb
    
  4. With machine off edit USB settings, add filter fore Tonecore.
  5. Have Tonecore plugged in when at boot time.
  6. Unplug and plug back in, it should show up in the Devices->USB menu as not greyed out. I don't understand what is going on here, but at least it works.
  7. Windows should recognize, do the usual driver dance, and the GUI should now recognize it.

Knobs/foot pedal events show up in the GUI. DSP downloads work. I haven't tried MCU downloads but I assume they will work.

I haven't tried this on a Mac yet. I expect it will work.


 

Code Library

The following are freely licensed code snippets that anyone is welcome to use.

  • int_math.inc - integer multiplication and division routines for where you don't want fixed-point math.