{----------------------------------------------------------------------------- dec2stg.asm: Two Stage Decimator ------------------------------------------------------------------------------ Description: Uses two real time Direct Form Finite Impulse Response Filters of N_1 and N_2 taps, decimates by M_1*M_2 for a decrease of 1/(M_1*M_2) times the input sample rate. Uses an input buffer which allows the filter computations to proceed in parallel with the acquisition of the next M_1*M_2 inputs, allowing a larger order filter to be used than if all calculations had to be made between samples. A register file register is used to count the M_1*M_2 samples, and a buffer twice the size of the data (2 * M_1 * M_2) is used to store input samples. The internal timer is programmed to generate interrupts at the desired sample rate, and software interrupt 0 is used to trigger an interrupt after M_1*M_2 samples have been input. ------------------------------------------------------------------------------ Theory: See Theory section of decimate.asm ------------------------------------------------------------------------------ Program Characteristics: Input: adc Output: dac Start Labels: init_dec reset-time initialization dec2stg called by software 0 interrupt Altered Registers: r0 FIR data in / ADC fltg-pt data r3 counter r4 FIR coefficients r8 FIR accumulate result reg r12 FIR multiply result reg / temporary reg r14 = 16 = exponent scale factor r15 ADC data raw Computation Time: sft0_svc = N_2 + 10 + M_2*(N_1 + 2*M_1 + 7) tmzh_svc = 7 ------------------------------------------------------------------------------ Note on processing input data: Because M_1 is less than three for this test case, the movement of data from the input buffer to the buffer data1 is accomplished at the start of the subroutine "dec2stg" as straight-line (i.e., not looped) code. If M_1 were greater than three, the following code would be used: r12=dm(i6,m0); r4=ashift r12 by -16; f0=float r4, r12=dm(i6,m0); lcntr=M_1-2, do load_data until lce; r4=ashift r12 by -16, dm(i0,m0)=f0; load_data: f0=float r4, r12=dm(i6,m0); r4=ashift r12 by -16, dm(i0,m0)=f0; f0=float r4; dm(i0,m0)=f0; ------------------------------------------------------------------------------ Creating & Using the Test Case: Running the test case requires two data files: coef1.dat 32-tap FIR filter #1 coefficients (floating point) coef2.dat 128-tap FIR filter #2 coefficients (floating point) wave.1 waveform data file coef1 & coef2 are the two parks-McClellan FIR filters used in this two-stage decimator. Assume that the input sample rate is 192kHz, and the decimated output is 192/4 = 48kHz. The first FIR filter has 32 taps, and is designed to have maximum of 1dB of attenuation from 0 - 48kHz, then >90dB attenuation at >96kHz. The second FIR filter has 128 taps, and is designed to have maximum of 1dB of attenuation from 0 - 20kHz, then >90dB attenuation at >24kHz. Descriptions of wave.1 can be found in decimate.asm & interpol.asm. The test case writes the decimated output to a dac port. Since there are 512 samples in wave.1, 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. 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 dec2stg ld21k -a generic dec2stg sim21k -a generic -e dec2stg -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 129th 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 in FIR #1 */ #define N_2 128 /* number of taps in FIR #2 */ #define M_1 2 /* 1st decimation factor */ #define M_2 2 /* 2nd decimation factor */ #define CNTR r3 /* counter */ #define FP 20.0e6 /* Processor Frequency = 20MHz */ #define FI 192.0e3 /* Input Frequency = 192KHz */ #ifndef TEST /*----------------------------------------------------------*/ #define TPER_VAL 312 /* TPER_VAL = FP/FI - 1 */ #else /*----------------------------------------------------------*/ #define TPER_VAL 104 /* interrupt every 160 cycles */ #endif /*----------------------------------------------------------*/ .SEGMENT /dm dm_data; .VAR data1[N_1]; { FIR input data #1 } .VAR data2[N_2]; { FIR input data #2 } .VAR input_buf[M_1*M_2*2]; { raw ADC data } .ENDSEG; .SEGMENT /pm pm_data; .VAR coef1[N_1]="coef1.dat"; { FIR floating-point coefficients } .VAR coef2[N_2]="coef2.dat"; { FIR floating-point 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 } call init_dec; { initialize the test case } wait: idle; { infinite wait loop } jump wait; .ENDSEG; {------------------------------------------------------------------------------- TMZH Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm tmzh_svc; { sample input: this code occurs at the sample rate } tmzh_svc: { decrement counter and get a sample } CNTR=CNTR-1,r15=dm(i2,m2); if eq jump zero_yes; zero_no: rti(db); nop; dm(i7,m0)=r15; { load in M long buffer } zero_yes: rti(db); irptl=SFT0I; { if counter==0, set interrupt} dm(i7,m0)=r15; { load in M long buffer } .ENDSEG; {------------------------------------------------------------------------------- Interrupt Request 3 Service Routine Dummy procedure so that the TMZH Service Routine does not fetch instructions from empty memory -------------------------------------------------------------------------------} .SEGMENT /pm irq3_svc; { dummy procedure } irq3_svc: rti; .ENDSEG; {------------------------------------------------------------------------------- Software Interrupt 0 Service Routine -------------------------------------------------------------------------------} .SEGMENT /pm sft0_svc; { process input data: this code occurs at 1/M times the sample rate } sft0_svc: jump dec2stg (db); { call the 2-stage decimator} CNTR=M_1*M_2; { reset input counter } nop; .ENDSEG; {------------------------------------------------------------------------------- efficient decimator initialization -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; init_dec: b0=data1; l0=@data1; m0=1; { data buffer #1 } b1=data2; l1=@data2; { data buffer #2 } b8=coef1; l8=@coef1; m8=1; { coefficient buffer #1 } b9=coef2; l9=@coef2; { coefficient buffer #2 } b7=input_buf; l7=@input_buf; { input buffer sample ptr } b6=input_buf; l6=@input_buf; { inp. buffer working ptr } b2=adc; l2=1; m2=0; { set A/D conv. pointer } r14=16; { exponent scale factor } CNTR=M_1*M_2; { set input counter } r0=0; { clear data buffers } lcntr=N_1, do clrbuf1 until lce; clrbuf1: dm(i0,m0)=r0; lcntr=N_2, do clrbuf2 until lce; clrbuf2: dm(i1,m0)=r0; tperiod=TPER_VAL; { program timer } tcount=TPER_VAL; bit set mode2 TIMEN; bit clr irptl TMZHI|SFT0I; { clear any pending irpts } rts (db); { enable Timer high priority, software 0 interrupts } bit set imask TMZHI|SFT0I; { enable interrupts, nesting, ALU saturation } bit set mode1 IRPTEN|NESTM|ALUSAT; .ENDSEG; {------------------------------------------------------------------------------- efficient decimator routine executes at 1/(M_1*M_2) times the sample rate -------------------------------------------------------------------------------} .SEGMENT /pm pm_code; dec2stg: lcntr=M_2, do stage_1 until lce; { transfer input samples to the FIR data memory space } { using the input buffer working pointer. Perform } { fix-->float conversion in parallel } r12=dm(i6,m0); r4=ashift r12 by -16; f0=float r4, r12=dm(i6,m0); r4=ashift r12 by -16, dm(i0,m0)=f0; f0=float r4; dm(i0,m0)=f0; { 1st FIR filter } fir_1: 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-3, do taps_1 until lce; taps_1: f12=f0*f4, f8=f8+f12, f0=dm(i0,m0), f4=pm(i8,m8); f12=f0*f4, f8=f8+f12; f0=f8+f12; stage_1: dm(i1,m0)=f0; { output to next filter data buffer } { 2nd FIR filter } fir_2: f0=dm(i1,m0), f4=pm(i9,m8); f8=f0*f4, f0=dm(i1,m0), f4=pm(i9,m8); f12=f0*f4, f0=dm(i1,m0), f4=pm(i9,m8); lcntr=N_2-3, do taps_2 until lce; taps_2: f12=f0*f4, f8=f8+f12, f0=dm(i1,m0), f4=pm(i9,m8); f12=f0*f4, f8=f8+f12; f0=f8+f12; rti (db); r0 = fix f0 by r14; { float --> fix } output: dm(dac)=r0; { output data sample } .ENDSEG;