Summary: You will implement a multirate system that includes three fininte impulse response filters.
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.
As this is your first experience with the C environment, most of the programming for the assignment is to be done directly in assembly. A C skeleton will provide access to input samples and a way to output samples. From the C skeleton, an assembly module for implementing the complete multirate system (for a single sample) is called. In the assembly module, the downsampling and upsampling blocks are implemented by using a loop or counter to determine which samples to keep and when to insert zeros.
As there was a core file for working in the assembly
environment (Labs 0-2), there is a core file for the C
environment (V:\ece320\54x\dspclib\core.asm)
which handles the interrupts from the CODEC (A/D and D/A) and
the serial port. Here, we will describe the important aspects
of the core code necessary to complete the assignment. The
complete documentation on the core code developed for the C
environment will be made available soon.
Let's examine the following C main program lab3main.c
which calls assembly FIR filter functions
init_filter and filter.
1 #include "v:/ece320/54x/dspclib/core.h" /* Declarations for core file */
2 void init_filter(void); /* Prototypes for assembly functions */
3 int filter(int sample);
4 extern int dec_rate; /* Default decimation rate is 4 */
5
6 main()
7 {
8 int *Rcvptr,*Xmitptr; /* pointers to Xmit & Rcv Bufs */
9 int i, sample1, sample2;
10
11 init_filter(); /* Initialize the filter */
12
13 while( 1 )
14 {
15 /* Wait for a new block of samples */
16 WaitAudio(&Rcvptr,&Xmitptr);
17
18 /* Process a block of samples */
19 for( i = 0; i < BlockLen; i ++ )
20 {
21 sample1 = Rcvptr[4*i]; /* Ch1 input sample */
22 sample2 = Rcvptr[4*i+2]; /* Ch2 input sample */
23
24 Xmitptr[6*i] = sample1; /* First output is input */
25 Xmitptr[6*i+1] = filter(sample1); /* Secound output is result */
26 }
27
28 i = SerialRX(); /* Check serial port */
29 if( i > 0 )
30 dec_rate = i; /* Save new decimation rate if we got it */
31 }
32 }
In the main program, an infinite loop operates over the
input samples accessed by the pointer Rcvptr
and writes the output samples via the pointer
Xmitptr. In C, pointers may be used as array
names so that Xmitptr[0] is the first word
pointed to by Xmitptr. The function
WaitAudio is a assembly function in the core
code which handles the CODEC interrupts. It returns a block
of BlockLen samples and writes
BlockLen samples to each of the six channels.
As in the assembly core, the input samples are not in
consecutive order. The right and left inputs are offset
from Rcvptr respectively by
Xmitptr.
Let's examine the calls to the assembly functions
init_filter and filter. The
assembly file containing these functions is
v:\ece320\54x\dspclib\lab3filt.asm
1 ; Lab 3 assembly module
2
3 .copy "v:\ece320\54x\dspclib\core.inc"
4 ; Useful macros for C interfacing
5
6 .global _dec_rate ; Decimation rate - in lab3main.c
7 .global _filter ; Filter code in this file
8 .global _init_filter
9
10 FIR_len .set 13
11
12 .sect ".data"
13
14 .align 16 ; Align to a multiple of 16
15 firstate
16 .space 16*13 ; Allocate 13 words of storage for
17 ; filter state.
18 .align 16
19 coef .copy "coef1.asm"
20
21 stateptr .word 0
22
23 _dec_rate .word 4
24
25 .sect ".text"
26
27 _init_filter ; need the leading _ for a C name.
28 ENTER_ASM
29
30 stm #firstate, AR3
31 mvmd AR3, stateptr ; Save AR3
32
33 LEAVE_ASM
34 ret
35
36 _filter
37 ENTER_ASM
38 ; Input in low part of A accumulator
39
40 mvdm stateptr, AR3 ; Restore state pointer
41
42 stm #FIR_len,BK ; initialize circular buffer length
43 stm #coef,AR2 ; initialize coefficient pointer
44 stm #1,AR0 ; initialize AR0 for pointer increment
45
46 stl A,*AR3+% ; store current input into state buffer
47 rptz A,(FIR_len-1) ; clear A and repeat
48 mac *AR2+0%,*AR3+0%,A ; multiply coefficient by state & accumulate
49
50 rnd A ; Round off value in 'a' to 16 bits
51
52 sfta a,-16 ; Shift output to low part of accumulator
53
54 mvmd AR3, stateptr ; Save state pointer
55
56 ; Output in low part of A accumulator
57
58 LEAVE_ASM
59 ret
The assembly file contains two main parts, the data section
starting with .sect ".data" and the program
section starting with .sect ".text". Every
function and variable accessed in C must be preceded by a
single underscore _ in assembly and a
.global _name must be placed in the assembly
file for linking. In this example, filter is
an assembly function called from the C program with a label
_filter in the text portion of the assembly
file and a .global _filter declaration. In
each assembly function, the macro ENTER_ASM is
called upon entering and LEAVE_ASM is called
upon exiting. These macros are defined in
v:\ece320\54x\dspclib\core.inc. The
ENTER_ASM macro saves the status registers and
AR1, AR6, and AR7
when entering a function as required by the register use
conventions. The ENTER_ASM macro also sets the
status registers to the assembly conventions we have been
using (i.e, FRCT=1 for
fractional arithmetic and CPL=0 for
DP referenced addressing). The
LEAVE_ASM macro just restores the saved
registers.
The parameter passing convention between assembly and C is
simple for single input, single output assembly functions.
From a C program, the input to an assembly program is in the
low part of accumulator A with the output
returned in the same place. In this example, the function
filter takes the right input sample from
A and returns a single output in A
(note the left shift by 16 to put the result in the low part
of A). When more than one parameter is passed
to an assembly function, the parameters are passed on the
stack (see the core file description for more information).
We suggest that you avoid passing or returning more than one
parameter. Instead, use global memory addresses to pass in
or return more than one parameter. Another alternative is
to pass a pointer to the start of a buffer intended for
passing and returning parameters.
When entering and leaving an assembly function, the
ENTER_ASM and LEAVE_ASM macros
ensure that certain registers are saved and restored. Since
the C program may use any and all registers, the state of a
register cannot be expected to remain the same between calls
to assembly function(s). Therefore, any
information that needs to be preserved across calls to an
assembly function must be saved to memory! In
this example, stateptr keeps track of the
location of the current sample in the circular buffer
firstate. Why don't we need to keep track of
the location of the coefficient pointer (AR2
in this example) after every sample?
A working program can be produced by compiling the C code
and linking assembly modules and the core module. The
compiler translates C code to a relocatable assembly form.
The linker assigns physical addresses on the DSP to the
relocatable data and code segments, resolves
.global references and links runtime libraries.
The procedure for compiling C code and linking assembly
modules has been automated for you in the batch file
v:\ece320\54x\dsptools\C_ASM.bat. Copy the
files lab3main.c, and lab3filt.asm
from the v:\ece320\54x\dspclib\ directory into
your own directory on the W: drive. Using
Matlab, write the coefficients you created in the prelab
into a coef1.asm file. Then, type c_asm
lab3main lab3filt to produce a
lab3main.out file to be loaded onto the DSP.
Load the output file onto the DSP as usual and check that is
the FIR filter you designed.
Modify the lab3filt.asm assembly module to
implement a cascade of filters FIR1 and FIR2. Note that
both _filter and _init_filter will
need to be modified. Compile and link the new assembly
module and confirm it has the frequency response which you
expect from cascading FIR1 and FIR2.
Once you have the cascaded system working, implement the
multirate system composed of the three FIR filters by
modifying the assembly modules in lab3filt.asm.
In order to implement the sample rate converters, you will
need to use a counter or a loop. The upsampling
block and downsampling block are not implemented as seperate
sections of code. Your counter or loop will
determine when the decimated rate processing is to occur as
well as when to insert zeros into FIR3 to implement the
zero-filling up-sampler.
Some instructions that may be useful for implementing your
multirate structure are the addm (add to
memory) and bc (branch conditional)
instructions. You may also find the banz
(branch on auxiliary register not zero) instruction useful,
depending on how you implement your code. As the
counter is state information that needs to be preserved
between calls to filter, the counter must be
saved in memory.
In order to experiment with multirate effects in your
system, make the downsampling factor
(
It will be useful both for debugging and for experimentation
to show the output of your system at various points in the
block diagram. By modifying the C code in
lab3main.c and the assembly modules in
lab3filt.asm, send the following sequences to
the DSP output
For the quiz, you should be prepared to change the decimation rate upon request, and explain the effects of changing the decimation rate on the system's output.
As usual, your grade will be split up into three sections:
One of the main benefits of multirate systems is efficiency.
Because of downsampling, the output of FIR1 is used only one
of
Similarly, at the input of FIR3,