Summary: The six-channel board for the TI EVM320C54 offers two channels of input and six channels of output at a sample rate of 44.1 kHz. It can also communicate with the PC via a serial port connection. The file thru6.asm exercises these inputs and outputs. The linker provides several sections of memory for storing program instructions and data. Test-vector input and output with vectcore.asm permits careful testing of DSP systems.
Note: Your browser may not currently support MathML. See our browser support page for additional details. You can always view the correct math in the PDF version.
The six-channel board attaches to the top of the DSP evaluation module and replaces its onboard, one-channel A/D and D/A with six channels of D/A and two channels of A/D running at a sample rate of 44.1 kHz. Alternatively, the A/D can be disabled and a SP/DIF digital input enabled, allowing PCM digital data from a CD or DVD to be sent directly into the DSP for processing. The two input channels and six output channels all sample at the same time; clock skew between channels is not an issue. By default, the core code reads and writes blocks of 64 samples for each channel of input and output; however, this aggregation can be changed to any value between 1 and 80 samples1. If your code needs a larger aggregation of samples - for instance, for a 256 point FFT - you will need to do this aggregation yourself.
Other features include buffered serial communication code, which allows you to send and receive data from the serial port. This can be used to control your DSP program with a graphical user-interface on the PC; it can also be used to report status back to the PC for applications such as speech recognition.
The core code, core.asm (which requires globals.inc, ioregs.inc, and misc.inc) also initializes the DSP itself. It enables the fractional arithmetic mode for the ALU, programs the wait states for the external memory, and sets the DSP clock to 80 MHz2.
We will start with a sample application, which simply sends the inputs into the outputs--relaying both the audio inputs from the A/D converters to the D/A converters, and any data that comes in on the serial port back to the PC. To familiarize yourself with this sample application, locate a copy of thru6.asm and assemble it.
Once you have done that, start Code Composer. Since we are
using the on-chip RAM on the TMS320C549 to hold program
code, we need to map that RAM into program space before we
can load our code. This can be done by opening the CPU
Registers window (the same one you use to look at the
ARx registers and the accumulators) and
changing the PMST register to
0xFFE0. This sets the OVLY bit to
1, switching the internal RAM into the DSP's program memory
space.
Finally, load the thru6.out file, use Code
Composer's Reset DSP menu option to reset the
DSP, and run the code. Probe at the connections with the
function generator and the oscilloscope; inputs and outputs
are shown in Figure 1. Note that output
channels 1-3 come from input channel 1, and output channels
4-6 come from input channel 2. Figure 1 shows
the six-channel board's connector configuration.
![]() |
Also test the serial communications portion of the thru6.asm application. This can be done by starting a provided terminal emulator package (such as Teraterm Pro or HyperTerminal), configuring it to communicate at 38400 bps, with no parity, eight data bits, and one stop bit, and attaching the correct serial port on the computer to the TI TMS320C54x EVM. A serial port is a 9-pin D-shell connector; it is located on the DSP EVM next to the power connector. Typically, there will be two matching D-shell connectors on your computer, often labeled COM1 and COM2; make sure you connect your serial cable to the right one!
Once you have started the terminal emulator, and the emulator has been correctly set to communicate with the DSP board, reload and rerun the thru6.asm application. Once it is running, you should be able to communicate with the DSP by typing text into the terminal emulator's window. Characters that you type should be echoed back; this indicates that the DSP has received and retransmitted the characters. If the DSP is not connected properly or not running, nothing will be displayed as you type. If this happens, check the connections and the terminal emulator configuration, and try again. Due to a terminal emulation quirk, the "Enter" key does not work properly.
After you have verified that the EVM is communicating with the PC, close the terminal window.
Because the DSP has separate program and data spaces, you would expect for the program and data memory to be independent. However, for the DSP to run at its maximum efficiency, it needs to read its code from on-chip RAM instead of going off-chip; off-chip access requires a one- or two-cycle delay whenever an instruction is read. The 32K words of on-chip RAM, however, are a single memory block; we cannot map one part of it into program space and another part of it into data space. It is possible to configure the DSP so that the on-chip RAM is mapped into both program space and data space, allowing code to be executed from the onboard memory and avoiding the extra delay. Figure 2 shows the DSP's memory map with the DSP's on-chip memory mapped into program space.
![]() |
Because the same on-chip RAM is mapped into both program and
data space, you must be careful not to overwrite your code
with data or vice versa. To help you, the linker will place
the code and data in different parts of the memory map. If you
use the .word or .space directives
to inform the linker of all of your usage of data memory, you
will not accidentally try to reuse memory that has been used
to store code or other data. (Remember that
.word allocates one memory location and
initializes it to the value given as its
parameter. .space 16*<words> allocates
<words> words of uninitialized storage.)
Avoid using syntaxes like stm #2000h,AR3 to point
auxiliary registers to specific memory locations directly, as
you may accidentally overwrite important code or
data. Instead, use syntaxes like stm #hold,AR3,
where hold is a label for memory explicitly
declared by .word or .space
directives.
There are two types of internal memory on the TI TMS320C549
DSP: SARAM (Single Access RAM) and DARAM (Dual Access
RAM). The first 8K of internal memory is DARAM; the next 24K
is SARAM. The difference between these two types of memory is
that while SARAM can only be read or written once in a cycle,
DARAM can be read or written twice in a cycle. This is
relevant because the TMS320C549 DSP core can access memory up
to three times in each cycle: two accesses in Data RAM to read
or write operands, and one access in Program RAM to fetch the
next instruction. Both DARAM and SARAM are divided into
"pages"; access to memory located in different "pages" will
never conflict. If, however, two operands are fetched from the
same "page" of SARAM (which is divided into 8K word pages:
2000h-3FFFh,
4000h-5FFFFh, and
6000h-7FFFh) in the same cycle, a
one-cycle stall will occur while the second memory location is
accessed. Due to the pipeline, two memory accesses in the
same instruction execute in different cycles. However, if two
successive instructions access the same area of SARAM, a stall
can occur.
Part of the SARAM (from 6000h to
7FFFh) is used for storing your program code; a
small amount of SARAM below 6000h is also used
for storing the DSP's stack. Part of the DARAM (from
0800h to 0FFFh) is used for the
input and output buffers and is also unavailable. Ensure that
any code you write does not use any of these reserved sections
of data memory. In addition, the core file reserves six
locations in scratch-pad RAM (060h to
065h); do not use these locations in your program
code.
You can use the .text directive to declare
program code, and the .data directive to declare
data. However, there are many more sections defined by the
linker control file. Note that the core file uses memory in
some of these sections.
You can place program code in the following sections using the
.sect directive:
.text: (.sect ".text") SARAM
between 6000h and 7FFFh (8192
words)
.etext: (.sect ".etext")
External RAM between 8000h and
FEFFh (32,512 words) The test-vector version
of the DSP core stores the test vectors in the
.etext section.
.data: (.sect ".data") DARAM between
1000h and 1FFFh (4096 words)
.sdata: (.sect ".sdata") SARAM between
2000h and 5EFFh (16,128 words)
.ldata: (.sect ".ldata") DARAM between
0080h and 07FFh (1,920 words)
.scratch: (.sect ".scratch") Scratchpad RAM
between 0060h and 007Fh (32
words)
.edata: (.sect ".edata") External RAM between
8000h and FFFFh (32,768 words)
(Requires special initialization; if you need to use this
memory, load and run the thru6.asm
application before you load your application to initialize
the EVM properly.)
Figure 3 shows the memory regions and sections defined by the linker control file. Note that the sections defined in the linker control file but not listed above are reserved by the core file and should not be used.
![]() |
To simplify discussion, we have split up the thru6.asm file into two separate files for discussion. One, thru6a.asm contains only the code for using the A/D and D/A converters on the six-channel surround board; the other, ser_echo.asm contains only the code to send and receive data from the serial port.
Here we will discuss thru6a.asm, which is shown below. ser_echo.asm is discussed in Core File: Serial Port Communication Between MATLAB and TI TMS320C54x.
1 .copy "core.asm"
2
3 .sect ".text"
4 main
5 ; Your initialization goes here.
6
7 loop
8 ; Wait for a new block of 64 samples to come in
9 WAITDATA
10
11 ; BlockLen = the number of samples that come from WAITDATA (64)
12 stm #BlockLen-1, BRC ; Repeat BlockLen=64 times
13 rptb block-1 ; ...from here to the "block" label
14
15 ld *AR6,16, A ; Receive ch1
16 mar *+AR6(2) ; Rcv data is in every other word
17 ld *AR6,16, B ; Receive ch2
18 mar *+AR6(2) ; Rcv data is in every other word
19
20 ; Code to process samples goes here.
21
22 sth A, *AR7+ ; Store into output buffer, ch1
23 sth A, *AR7+ ; ch2
24 sth A, *AR7+ ; ch3
25
26 sth B, *AR7+ ; Store into output buffer, ch4
27 sth B, *AR7+ ; ch5
28 sth B, *AR7+ ; ch6
29
30 block
31 b loop
Line 1 copies in the core code, which initializes the six-channel board and the serial interface, provides the interface macros, and then jumps to "main" in your code. Line 3 declares that what follows should be placed in the program-code area in internal memory.
On Line 4, we find the label "main". This is the entry point for your code; once the DSP has finished initializing the six-channel board and the serial port, the core file jumps to this label.
On Line 9, there is a call to
WAITDATA. WAITDATA waits for the
next block of 64 samples to arrive from the A/D. When it
returns, a pointer to the samples captured by the A/D is
returned in AR6 (which can also be referred to
as pINBUF); a pointer to the start of the
output buffer is returned in AR7 (also
pOUTBUF). Note that WAITDATA
simply calls the wait_fill subroutine in the
core file, which uses the B register
internally, along with the DP register and the
TC flag; therefore, do not expect the value of
B to be preserved across the
WAITDATA call.
Lines 12 and 13 set up a block
repeat. BlockLen is set by the core code as the
length of a block; the repeat instruction therefore repeats
for every sample time. Lines 15-18 retrieve
one sample from each of the two channels; note that the
received data is placed in every other memory
location. Lines 22-24 place the first input
channel into the first three output channels, and
lines 26-28 place the second input channel into
the last three output channels. Figure 1 shows the relationship between the channel
numbers shown in the code and the inputs and outputs on the
six-channel board.
Line 31 branches back to the top, where we wait for the next block to arrive and start over.
A second version of the core file offers the same interface as the standard core file, but instead of reading input samples from the A/D converters on the six-channel board and sending output samples to the D/A converters, it reads and writes from test vectors generated in MATLAB.
Test vectors provide a method for testing your code with known input. Given this known input and the specifications of the system, we can use simulations to determine the expected output of the system. We can then compare the expected output with the measured output of the system. If the system is functioning properly, the expected output and measured output should match3.
Testing your system with test vectors may seem silly in some cases, because you can see if simple filters work by looking at the output on the oscilloscope as you change the input frequency. However, they become more useful as you write more complicated code. With more complicated DSP algorithms, testing becomes more difficult; when you correct an error that results in one case not working, you may introduce an error that causes another case to work improperly. This may not be immediately visible if you simply look at the oscilloscope and function generator; the oscilloscope does not display the signal continuously and transient errors may be hidden. In addition, it is easy to forget to check all possible input frequencies by sweeping the function generator after making a change.
More importantly, the test vectors also allow you to test signals that cannot be generated or displayed with the oscilloscope and function generator. One important signal that cannot be generated or tested with the function generator and oscilloscope is the impulse function; there is no way to view the impulse response of a filter directly without using test vectors. The unit impulse represents a particularly good test vector because it is easy to compare the actual impulse response of a digital filter against the expected impulse response. Testing using the impulse response also exposes the entire range of digital frequencies, unlike testing using periodic waveforms generated by the function generator.
Lastly, testing using test vectors allows us to isolate the DSP from the analog input and output section. This is useful because the analog sections have some limitations, including imperfect anti-aliasing and anti-imaging filters. Testing using test vectors allows us to ensure that what we see is due only to the digital signal processing system, and not imperfections in the analog signal or electronics.
After generating a test vector in MATLAB, save it to a file
that can be brought into your code using the MATLAB command
save_test_vector (available as save_test_vector.m):
>> save_test_vector('testvect.asm',ch1_in,ch2_in); % Save test vector
(where ch1_in and ch2_in are the
input test vectors for input channel 1 and input channel 2;
ch2_in can be omitted, in which case both
channels of the test-vector input will have the same data.)
Next, modify your code to include the test-vector support
code and the test-vector file you have created. This can be
done by replacing the first line of the file (which is a
linker directive to copy in core.asm) with two
lines. Instead of:
.copy "core.asm"
use:
.copy "testvect.asm"
.copy "vectcore.asm"
Note that, as usual, the whitespace in front of the
.copy directive is required. (Download
vectcore.asm into your work directory if you do not
already have a copy.)
The test vectors occupy the .etext section of
program memory between 08000h and
0FEFFh. If you do not use this section, it will
not interfere with your program code or data. This memory
block is large enough to hold a test vector of up to 4,000
elements. Both channels of input, and all six channels of
output, are stored in each test vector element.
Now assemble and load the file, and reset and run as usual.
After a few seconds, halt the DSP (using the Halt command
under the Debug window) and verify that the DSP has halted
at a branch statement that branches to itself: spin b
spin.
Next, the test vector should be saved and loaded back into
MATLAB. This is done by saving
08000h in program memory. Do this by
choosing File->Data->Save... in Code Composer,
then entering the filename output.dat and
pressing Enter. Next, enter
0x8000 in the Address field of the dialog box
that appears,
Last, use the read_vector function (available
as read_vector.m)
to read the saved test vector output into MATLAB. Do this
using the following MATLAB command:
>> [ch1, ch2, ch3, ch4, ch5, ch6] = read_vector('output.dat');
The MATLAB vectors ch1 through ch6
now contain the output of your program code in response to
the input from the test vector.
"Doug course at UIUC using the TI C54x DSP has been adopted by many EE, CE and CS depts Worldwide "