{----------------------------------------------------------------------------- ratiobuf.asm: Timer-Driven Real Time Rational Rate Changer ------------------------------------------------------------------------------ Description: Uses an N-tap Real time Direct Form Finite Impulse Filter. Program performs interpolation by L and decimation by M for a L/M change in the input sample rate. Uses an input buffer so that the filter computations can occur in parallel with the input and output of data. This allows the use of a larger order filter for a given input sample rate. The timer is used to interrupt the 21020 at L*(input sample rate), where L is the interpolation factor. ------------------------------------------------------------------------------ Theory: See Theory section of decimate.asm and interpol.asm. ------------------------------------------------------------------------------ Program Characteristics: Input: adc. Output: dac. Labels: Registers Used: r0 FIR data in / ADC fltg-pt data / temp register r1 temp register r2 input counter r3 output counter r4 FIR coefficients r5 scale value L r8 FIR accumulate result reg r12 FIR multiply result reg / temporary reg r14 = 16 = exponent scale factor r15 ADC data raw Maximum Computation Time: tmzh_svc = 15 sft0_svc = NoverL + 2+intMoverL + 16 ------------------------------------------------------------------------------ Comments on Source Code: See comments in rat_2int.asm. ------------------------------------------------------------------------------ Creating & Using the Test Case: Running the test case requires two data files: coef.dat 32-tap FIR filter coefficients (floating point) wave.1 waveform data file Descriptions of coef.dat and wave.1 can be found in decimate.asm & interpol.asm. The test case writes the rate-changed output to a dac port. Since there are 512 samples in wave.1, and the test case performs interpolation by 4 followed by decimation by 3, there will be 682 data values written to the dac port if all data samples are read in and processed. The values written out are left-shifted by 16 bits in parallel with the float-->fixed conversion, based again on the assumption that the D/A converter port is located in the upper 16 bits of data memory. Armed with this information, you are ready to run: 1. Assemble & Start Simulator: asm21k -DTEST ratiobuf ld21k -a generic ratiobuf sim21k -a generic -e ratiobuf -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 input, set to break on 513th 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/4/91 -----------------------------------------------------------------------------} #include "def21020.h" #define N 32 /* N taps, N coefficients */ #define L 4 /* interpolate by factor of L */ #define NoverL 8 /* N must be an integer multiple of L */ #define M 3 /* decimate by factor of M */ #define intMoverL 2 /* smallest integer GE M/L */ #define FP 20.0e6 /* Processor Frequency = 20MHz */ #define FI 64.0e3 /* Interrupt Frequency = 64KHz */ #define IN_CNTR r2 /* input counter register */ #define OUT_CNTR r3 /* output counter register */ #ifndef TEST /*----------------------------------------------------------*/ #define TPER_VAL 312 /* TPER_VAL = FP/FI - 1 */ #else /*-----------------------------------------------------------------*/ #define TPER_VAL 39 /* interrupt every 40 cycles */ #endif /*----------------------------------------------------------------*/ .SEGMENT /pm pm_data; .VAR coef[N]="coef.dat"; { coefficient circular buffer } .ENDSEG; .SEGMENT /dm dm_data; .VAR data[NoverL]; { data delay line } .VAR input_buf[intMoverL]; { input buffer is not circular } .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_rat; { initialize the test case } .ENDSEG; {------------------------------------------------------------------------------- Timer=zero high priority Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm tmzh_svc; tmzh_svc: IN_CNTR=IN_CNTR-1; { test if time for input } if ne jump skipin(db); OUT_CNTR=OUT_CNTR-1; { test if time for output } nop; jump input (db); { calculate the input sample } r0=dm(adc); { get input sample } r0=ashift r0 by -16; { right-justify the data } .ENDSEG; {------------------------------------------------------------------------------- Software Interrupt 0 Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm sft0_svc; sft0_svc: jump calc_out (db); { calculate the output sample } r0=i7; { get current ptr to input buffer } r1=input_buf; { get start addr of input buffer } .ENDSEG; {------------------------------------------------------------------------------- Initialize Interpolation Routine -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; init_rat: { initialize buffer index registers } b0=data; l0=@data; m0=1; { data buffer } b7=input_buf; l7=0; { input buffer } b8=coef; l8=@coef; m8=L; { modifier for coef is L! } b9=0;l9=0;m9=-M; { track coefficient updates } IN_CNTR = 1; { initialize flags, counters } OUT_CNTR = 1; r5=L; { scale register for interp. } f5=float r5; r14=16; { exponent scale factor } r0=0; { clear data buffer } lcntr=NoverL, do clrbuf until lce; clrbuf: dm(i0,m0)=r0; tperiod=TPER_VAL; { program timer } tcount=TPER_VAL; bit set mode2 TIMEN; { enable timer zero high priority, software 0 interrupts } bit set imask TMZHI|SFT0I; bit clr irptl TMZHI|SFT0I; { clear any pending irpts } bit set mode1 IRPTEN|ALUSAT; { enable irpts, ALU sat } wait: idle; jump wait; { infinite wait loop } .ENDSEG; {------------------------------------------------------------------------------- Calculate output initiated by software interrupt 0, code below occurs at the output sample rate -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; calc_out: r1=r0-r1, i7=b7; { calculate amount in inbuffer } modify(i9,-M); { modify coef update by -M } m10=i9; if eq jump modify_coef; { skip do loop if buffer empty } { dump the input buffer into delay line if L inputs have } { been acquired } lcntr=r1, do load_data until lce; f1=dm(i7,m0); load_data: dm(i0,m0)=f1; modify_coef: modify(i8,m10); { modify coef ptr by coef update } { filter pass, occurs at L times the input sample rate } fir: 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=NoverL-3, do taps until lce; taps: f12=f0*f4, f8=f8+f12, f0=dm(i0,m0), f4=pm(i8,m8); f12=f0*f4, f8=f8+f12; f0=f8+f12,i7=b7; { reset input buffer in || w/comp } i9=0; { reset coef update } rti (db); r0 = fix f0 by r14; { float -> fixed } dm(dac)=r0; { output data sample } .ENDSEG; {------------------------------------------------------------------------------- Acquire input initiated by Timer Interrupt, code occurs at L times the input rate -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; input: { convert fixed->float, modify the coef update by L } f0=float r0,modify(i9,m8); f0=f0*f5; { scale by L } dm(i7,m0)=f0; { load in M long buffer } { set AZ flag w/ OUT_CNTR, reset the input count to L } OUT_CNTR=pass OUT_CNTR, IN_CNTR=m8; skipin: if ne rti; { return if OUT_CNTR!=0 } output: rti(db); { cause output routine if OUT_CNTR==0 } bit set irptl SFT0I; OUT_CNTR=M; { reset output counter to M } .ENDSEG;