{------------------------------------------------------------------------------- int2stg.asm: Two Stage Interpolator -------------------------------------------------------------------------------- Description: Uses two real time N-tap Direct Form Finite Impulse Response Filters to interpolate by L_1*L_2 for an increase of L_1*L_2 times the input sample rate. A restriction on the number of taps is that N divided by L_1*L_2 be an integer. The internal timer is used to generate interrupts at L_1*L_2 times the sample rate. ------------------------------------------------------------------------------ Theory: See Theory section of interpolate.asm ------------------------------------------------------------------------------ Program Characteristics: Input: adc Output: dac Start Labels: init_int reset-time initialization int2stg called by Timer Zero High Priority interrupt Altered Registers: r0 FIR data in / ADC data / temp register r1 scale factor L r2 counter #1 r3 counter #2 r4 FIR coefficients r8 FIR accumulate result reg r12 FIR multiply result reg / temporary reg r13 = 1 = counter compare register r14 = 16 = exponent scale factor Computation Time: tmzh_svc = N_1/L_1 + N_2/L_2 + 25 ------------------------------------------------------------------------------ Creating & Using the Test Case: Running the test case requires two data files: coef.dat 32-tap FIR filter coefficients (floating point) sinX.dat sine-wave data files (X=1,2,3,4,5) coef.dat contains a 32-tap FIR filter which bandlimits the input signal to 1/8 of the input frequency. Since the decimator in the test case decimates by a factor M=4, this bandlimitation is equivalent to the required limit of 1/2 the output frequency. The filter is a Parks-McLellan filter with a passband ripple of 1.5dB out to about 1/20 the input frequency, and a stopband with greater than 70dB of attenuation above 1/8 the input frequency. As an example, if the oversampled input frequency is 64kHz, the passband extends out to about 3.2kHz. The stopband starts at 4kHz, and the output frequency is 16kHz. The data files are all of the form sin.X, where X ranges from 1 to 5. Each data file contains 512 signed fixed-point data values in the range +/- 32767. These data points are meant to resemble data read from a 16-bit A/D converter which generates signed data. sin.1 through sin.4 contain simple sine waves at different frequencies. sin.5 contains a composite waveform generated from 3 sine wave of differing frequency and magnitude. The test case writes the interpolated output to a dac port. Since there are 512 samples in sin.X, and the test case performs decimation by 4, there will be 128 data values written to the dac port if all data samples are read in and processed. Armed with this information, you are ready to run: 1. Assemble & Start Simulator: asm21k -DTEST int2stg ld21k -a generic.ach int2stg sim21k -a generic.ach -e int2stg -k port1 Note: the keystroke file port1.ksf opens two ports: 1st port - DM, F0000000, Fixed-Pt., auto-wrap, wave.1 2nd port - DM, F0000001, Fixed-Pt., no wrap, out.1 2. In the simulator, a. Go to the program memory (disassembled) window, d. Go to symbol output, set to break on 4097th occurence, and c. Run 3. Compare wave.1 to out.1 on the graphing program of your choice. ------------------------------------------------------------------------------ Author: Jim Donahue, Analog Devices DSP Division Created: 3/18/91 -----------------------------------------------------------------------------} #include "def21020.h" #define N_1 32 /* number of taps, FIR #1 */ #define N_2 32 /* number of taps, FIR #2 */ #define L_1 4 /* interpolate by factor of L_1 */ #define L_2 2 /* interpolate by factor of L_2 */ #define CNT1 r2 #define CNT2 r3 #define FP 20.0e6 /* Processor Frequency = 20MHz */ #define FI 64.0e3 /* Input Frequency = 64KHz */ #ifndef TEST /*----------------------------------------------------------*/ #define TPER_VAL 312 /* TPER_VAL = FP/FI - 1 */ #else /*----------------------------------------------------------*/ #define TPER_VAL 53 /* interrupt every 54 cycles */ #endif /*----------------------------------------------------------*/ .SEGMENT /dm dm_data; .VAR data1[N_1/L_1]; { ADC fixed-point data buffer #1 } .VAR data2[N_2/L_2]; { ADC fixed-point data buffer #2 } .ENDSEG; .SEGMENT /pm pm_data; .VAR coef1[N_1]="coef1.dat"; { fltg-pt. FIR #1 coefficients } .VAR coef2[N_2]="coef2.dat"; { fltg-pt. FIR #2 coefficients } .ENDSEG; .SEGMENT /dm ports; .PORT adc; { Analog to Digital (input) converter } .PORT dac; { Digital to Analog (output) converter } .ENDSEG; {------------------------------------------------------------------------------- RESET Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm rst_svc; rst_svc: PMWAIT=0x0021; { no wait states,internal ack only } DMWAIT=0x8421; { no wait states,internal ack only } jump init_int; { initialize the test case } .ENDSEG; {------------------------------------------------------------------------------- TMZH Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm tmzh_svc; { sample input: this interrupt occurs at the L_1*L_2*input rate } tmzh_svc: CNT2=CNT2-1,modify(i9,m15); { decrement counter, and } { shift coef #2 pointer back } if ne jump do_fir2; { do second FIR if CNT2!=0 } CNT1=CNT1-1,modify(i8,m15); { decrement counter, and } { shift coef #1 pointer back } if ne jump int2stg; { go to first FIR if CNT1!=0 } jump sample (db); { get sample if CNT1==CNT1==0 } r15=dm(adc); { input data sample } r15=ashift r15 by -16; { right-justify & zero garbage bits } .ENDSEG; {------------------------------------------------------------------------------- Initialize Interpolation Routine -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; init_int: { initialize buffer index registers } b0=data1; l0=@data1; m0=1; { data buffer #1 } b1=data2; l1=@data2; { data buffer #2 } b8=coef1; l8=@coef1; m8=L_1; { modifier for coef1 = L_1 } b9=coef2; l9=@coef2; m9=L_2; { modifier for coef2 = L_2 } m15=-1; { Register Initialization } r1=L_1*L_2; { value for upscaling sample } f1=float r1; { fix --> float conversion } CNT1=1; { set interpolate cntrs to 1 } CNT2=1; { for first data sample } r13=1; { counter compare reg } r14=16; { exponent scale factor } r0=0; { clear data buffers } lcntr=N_1/L_1, do clrbuf1 until lce; clrbuf1: dm(i0,m0)=r0; lcntr=N_2/L_2, do clrbuf2 until lce; clrbuf2: dm(i1,m0)=r0; tperiod=TPER_VAL; { program timer } tcount=TPER_VAL; bit set imask TMZHI; { enable TMZH interrupt } bit clr irptl TMZHI; { clear any pending irpts } { enable interrupts, ALU sat } bit set mode1 IRPTEN|ALUSAT; bit set mode2 TIMEN; { turn timer on } wait_interrupt: idle; jump wait_interrupt; { infinite wait loop } .ENDSEG; {------------------------------------------------------------------------------- Two-stage Interpolate code executes at L_1*L_2*(sample_rate) -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; int2stg: { filter pass, occurs at L_1 times the input sample rate } f0=dm(i0,m0), f4=pm(i8,m8); f8=f0*f4, f0=dm(i0,m0), f4=pm(i8,m8); f12=f0*f4, f0=dm(i0,m0), f4=pm(i8,m8); lcntr=N_1/L_1-3, do taps1 until lce; taps1: f12=f0*f4, f8=f8+f12, f0=dm(i0,m0), f4=pm(i8,m8); f12=f0*f4, f8=f8+f12; f0=f8+f12; dm(i1,m0)=f0; modify(i9,m9); CNT2=m9; { reset counter #2 to L_2 } { filter pass, occurs at L_1*L_2 times the input sample rate } do_fir2: f0=dm(i1,m0), f4=pm(i9,m9); f8=f0*f4, f0=dm(i1,m0), f4=pm(i9,m9); f12=f0*f4, f0=dm(i1,m0), f4=pm(i9,m9); lcntr=N_2/L_2-3, do taps2 until lce; taps2: f12=f0*f4, f8=f8+f12, f0=dm(i1,m0), f4=pm(i9,m9); f12=f0*f4, f8=f8+f12; f0=f8+f12; dm(i1,m0)=f0; rti (db); r0 = fix f0 by r14; { float -> fixed } output: dm(dac)=r0; { output data sample } .ENDSEG; {------------------------------------------------------------------------------- Acquire Sample -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; { sample input: this code occurs at the L_1*L_2*input rate } sample: { do fix->float, and scale data up by L_1*L_2 } f0=float r15, modify(i8,m8); f0=f0*f1; { upscale sample } jump int2stg (db); dm(i0,m0)=f0; { update delay line w/latest } CNT1=m8; { reset counter #1 to L_1 } .ENDSEG;