Programming the DSP

This article outlines the programming and usage of the DSP in the tablets.

Contents

General sources of information

TI's docs for DSPs. Do a search down the page for things with c55 in the description to get the documents that are of interest. Some direct pointers are listed at the end of this page.

DSP Gateway project. This is used by the Nokia 770, N800 and N810 to interface the ARM processor with the DSP.

Setup the toolchain

Get the toolchain from TI's Linux DSP Tools page (you need a free registration to access the page). Select "Linux DSP Tools v1.00" and download it to /tmp and unpack it:

chmod a+rx /tmp/linuxdsptools_v1_00_00_06.bin
/tmp/linuxdsptools_v1_00_00_06.bin 

Select the installation directory under /tmp. Once unpacked, you need to actually install it. Make sure you have expect (sudo aptitude install expect) and Java installed. Don't try to install the DSP tools under Scratchbox, as this does not work (i.e. this is an x86 based cross-toolchain for the DSP).

Install it under /usr/local/ti_dsptools (use sudo or make otherwise sure you can make the directory):

cd /tmp/Linux-DSP-Tools-1.00.00.06 
./subpkg_install_linuxdsptools.exp /usr/local/ti_dsptools 

Copy the avs_kernel.out from your device (in /lib/dsp/ these days) to /usr/local/ti_dsptools/dspgw.

Now you should be able to compile (try something like Speex port to DSP GW).

Check this thread for longer instructions.

Or email/IRC lardman/Simon Pickering (s dot g dot pickering at bath dot ac dot uk).

Compiling a program

Get the DSP Gateway source packages from the download page. Download both the ARM-side package (dspgw-3.3-arm.tar.bz2) and the DSP-side package (dspgw-3.3-dsp.tar.bz2). You need version 3.3(.0) not version 3.3.1!

There is no need to go patching and rebuilding the 770 kernel or fiddling with the DSP kernel as these are both setup to use the DSP Gateway already.

Unpack the tarballs. Go to 'dspgw-3.3-arm/host_src/mod_utils and build the coff_unresolve and gen_dummy_kernel tools using your PC native toolchain (the one you use to build anything else not ARM-related). Simple make should be sufficient.

gen_dummy_kernel is used to generate a dummy kernel based on the DSP kernel on your tablet (called avs_kernel.out) against which you will link any modules you create so that they will run on the device. This should allow any unresolved symbols and function call addresses to be resolved in a static manner, but without needing to link statically (as you then remove the dummy kernel in the step below). coff_unresolve is then run on the modules you will have just linked against the dummy kernel to remove the sections linked in from the dummy kernel.

Either copy these tools to your path or adjust dspgw-3.3-dsp/src/apps/demo_mod/Makefile to use the tools from the directory where they are (e.g., create a folder for these utilities and place these files there, and use absolute path in Makefile like /usr/local/ti_dsptools/gateway/gen_dummy_kernel and /usr/local/ti_dsptools/gateway/coff_unresolve). The other alternative to having explicit paths in all of your makefiles is to have a bash script that you source before doing DSP development to set relevant paths (add an example here).

To build the demo console (as an example):

Build the ARM side of the demo console in dspgw-3.3-arm/apps/demo<c/ode> with <code>make demo_console.

You can either do this inside scratchbox or adjust CC in the Makefile to point to your arm cross compiler (e.g., /scratchbox/compilers/arm-gcc-3.3.4-glibc-2.3.2/bin/arm-linux-gcc).

Get the avs_kernel.out from your tablet and place this in the DSP-side build directory (i.e., the directory containing the code to run on the DSP in dspgw-3.3-dsp/src/apps/demo_mod/). The avs_kernel.out file is in the /lib/dsp/ directory.

Adjust the Makefile (e.g., dspgw-3.3-dsp/src/apps/demo_mod/Makefile) to use the avs_kernel.out file to generate the dummy_kernel.obj rather than the default tinkernel.out (tinkernel is the name used in all of the documentation for the DSP kernel, it just happens that Nokia decided to name their kernel file differently. You don't need to mess about with the tinkernel source found with the toolchain, all you need is the actual binary avs_kernel.out from your tablet).

Run:

make omap1

The code is compiled into an object file (*.obj), the dummy kernel is created from your avs_kernel.out file by the gen_dummy_kernel utility you compiled earlier, the object file is linked against the dummy kernel and then the dummy kernel is stripped away from the resulting object file by the coff_unresolve utility you created earlier. You are left with a file named *.o.

Install the demo

After compiling your code, you'll need to place the DSP-side object file (*.o) and linker command file (*.cmd) in /lib/dsp/modules/, you'll also need to make an entry in the /lib/dsp/dsp_dld_avs.conf file to let the DSP know this module exists.

# echo "demo_console _task_demo_console 1 /lib/dsp/modules/demo_console.o" >> /lib/dsp/dsp_dld_avs.conf

You'll then want to run dsp_dld to restart the DSP and reload the module list. This might complain that it can't find the conf file:

Nokia-N800-10:~# dsp_dld
sending SIGBUS signal to all task users...
killing pid 832.
killing pid 827.
killing pid 1582.
Can't open /lib/dsp/dsp_dld.conf

If so, you need to create a symlink from the actual file dsp_dld_avs.conf, to the one it expects dsp_dld.conf (both in the /lib/dsp/ directory). Then try again and you'll see something like this:

Nokia-N800-10:~# dsp_dld
sending SIGBUS signal to all task users...
killing pid 4234.
killing pid 4236.
killing pid 4235.
mapping external memory: adr = 0x028000, size = 0x1000
mapping external memory: adr = 0x100000, size = 0x200000
mapping external memory: adr = 0x400000, size = 0x180000
detected binary version 3.3.0.0
setting DSP reset vector to 0x10389e
releasing DSP reset
DSP configuration ...
succeeded.

Then copy the ARM-side binary somewhere on the tablet (anywhere, but not on any FAT partitions on the flash cards, as these have no-execute enabled) and run it.

Nokia-N800-10:~# ./demo_console
Congratulations! DSP is working!

If you see a message like this:

Nokia-N800-10:~# ./demo_console
open: No such file or directory

It means you've either not added the module to the /lib/dsp/dsp_dld_avs.conf file (correctly), or you've not run dsp_dld to refresh the DSP. In either case the /dev/dsptask/ device hasn't been created as the module couldn't be loaded, hence the open error.

If you see a message like this:

~ $ ./demo_console
open: Interrupted system call

Then something is wrong with the code on the DSP side. Make sure that you added the exact line shown above to /lib/dsp/dsp_dld_avs.conf.

Compiling other demos

If you decide to copy any code from the DSP Gateway PDF files, bear the following in mind (and also consider that the tarballs contain almost identical code so you might as well use them!):

Any \ characters appears as a ¥ in the PDFs and the ' sometimes comes out as a different character (smart quote) so make sure you check your code if you start getting weird errors (the last one had me scratching my head for a while as they look pretty much the same in my editor).

You'll also need to remove references to the include file. I don't have this, the tarball demos don't have this line either (and are otherwise identical to the PDFs).

Steps forward

Although video out seems to have been dropped by Nokia with the N800 (and the DSP has become more stable, not sure if not using the framebuffer is the reason, or simply more mature hardware), anyway audio decoding and output is still performed on the DSP, and this is a suitable task for people to get stuck into. Framebuffer access works for the N8x0 and 770. Contact me (lardman) or read the maemo-developers list for more info. I should update this page when I have some time.

A pair of drivers are required to access the audio codec (a low-level mini-driver and a standard high-level class driver). These are obviously already present on the device, but no documentation is available about how to use them directly. In fact it can be seen from the functions called by existing modules (such as pcm2.o) that it's a stream class driver. Therefore it may be possible to access this class driver directly (there are plenty of examples in the DDK, see below).

It appears that this stream class driver is wrapped in some other function calls (i.e., the following functions are called by pcm2.o):

_EAP_CC_Mute
_EAP_CC_RemoveStream
_EAP_CC_RequestStream
_EAP_CC_SetPanning
_EAP_CC_SetVolume
_EAP_CC_UnMute
_EAP_clock
_EAP_getTicks

These _EAP_* functions are built into the avs_kernel.out DSP kernel. It would be useful to have a header file for these functions, and if possible some example code and/or instructions illustrating their use.

Another option is to write a completely new mini-driver + class driver + wrapper implementation. This initially looks like a reasonable task. One requires the DSP/BIOS Driver Developer Kit 1.20 for examples and information about writing drivers, the Chip Support Library with which the DDK interfaces, and information about the hardware codec itself.

On the 770, the codec is called aic23 (see dmesg output). On the N800 the codec is called TSC2301 (part of the touch screen chip) (again see your dmesg output—see this thread.

[ 312.232727] #0: OMAP24xx EAC with codec TSC2301

Data about both of these codecs is available on the Texas Instruments website. See the page for TSC2301. It includes a data sheet with the info needed to write the mini-driver.

Unfortunately I'm not sure that it's possible to add in a new mini-driver without re-compiling the DSP kernel (see chapter 3 of spru616a.pdf contained in the DDK - link above). It might be possible to create a dsptask containing the driver, but again, I'm not sure about this.

For those who want more information about disassembly, take a look at these documents:

spru374g DSP assembly code memonics
spru281f c/c++ calling conventions (ch6 Run-time environment)
spru371f cpu/register info (ch2 CPU registers, etc.)
spru280h.pdf TMS320C55x Assmbly Language Tools User's Guide (ch2 Introduction to
Common Object File Format c12 disassembler

Tips, tricks and troubleshooting

Check that you're linking against the current avs_kernel (well the dummy kernel generated from this). If not, it won't load.

Check that you've correctly defined the flags for the task. This is especially easy to miss as the flag names are rather similar (and cryptic). A list can be found in the DSP gateway spec PDF file. Look for the TCFG section.

dbg() function: This function allows one to output messages (like printf) to dmesg from the DSP-side. Before OS2008 you needed to recompile the kernel to enable DSP debugging messages; in OS2008 this is already enabled. You use it like so:

dbg('this is a message, this is a value=%d', some_value);

Note that the variable some_value must be a short or smaller (16bit or less). If you pass it a 32bit number, as often as not it returns zero (probably due to the strange endianness of the 32bit data type that means the first 16bits are the high bytes). Therefore split your 32bit data into 16bit chunks (or cast if you are sure about the limited range) if you want to see them.

Location of data

I had some troubles trying to access an array of data defined as a global const int array. The problem was that every element of the array returned 0 rather than the value which was set in it. After messing about for a while it occurred to me that the problem is to do with the location in memory of global const data. Changing the definition from a global const int array to a simple global int array solved the problem and the data could be accessed.

I didn't investigate this further, but in the avs_kernelcfg.cmd file you can see the locations of the various parts of memory and it may be that to obtain the actual data (rather than a zero) one needs to either use a far function (see the C/C++ manual for the DSP) or make sure the data is in the right section of memory.

Documentation pointers

Compiler optimization

TMS320C55x Optimizing C/C++ Compiler User's Guide

  • p79: 3.2 Performing File-Level Optimization (-O3 Option)
  • p80: 3.3 Performing Program-Level Optimization (-pm and -O3 Options)

ASM with C/C++

TMS320C55x Optimizing C/C++ Compiler User's Guide

  • p83: 3.3.2 Optimization Considerations When Mixing C and Assembly
  • p176: 6.5 Interfacing C/C++ With Assembly Language
  • p178: Example 6-3. Calling an Assembly Language Function From C
  • p181: 6.5.3 Using Inline Assembly Language

TMS320C55x DSP Programmer's Guide

  • p76: Table 3-6. TMS320C55x C Compiler Intrinsics

Misc

TMS320C55x Optimizing C/C++ Compiler User's Guide

  • p122: 5.3 Data Types