;--------------------------------------------------------------- ; DSKSG.ASM ; Keith Larson ; TMS320 DSP Applications ; (C) Copyright 1995,1996 ; Texas Instruments Incorporated ; ; This is unsupported freeware with no implied warranties or ; liabilities. See the disclaimer document for details ;---------------------------------------------------------------- ; DSKSG.ASM is a precision signal generator application which uses ; a wave table lookup to create multiple precision signals. The ; table lookup scheme uses both interpolation in time and level to ; achieve precision in frequency and in level ; ; NOTE: Please see DSKDTMF.ASM for generating ultra-precise 'pure' ; sine waves. This application will even produce both the ; sine and cosine (complex conjugate) at the same time. ; ; Sine Generation ; --------------- ; DSKSG.ASM generates a sine wave when the SINE_SG.ASM file has been ; assembled and loaded into the signal lookup table. ; ; The output waveform is produced by assuming that the table length ; of N=128 samples is 2*PI radians. The desired frequency (radian ; accumulation rate) is then used to determine both the whole step ; step values and also a residual step which is used in a first order ; interpolation (slope and intercept). ; ; NOTE: This technique produces 32 bit floating point frequency accuracy ; with excellent amplitude precision for signals that fit the ; interpolation and sampling criteria. For a sine wave, linear ; interpolation will result in approximately 2x the bits of precision ; used in the lookup, or about 14 bits in a 7 bit (128 element) table. ; ; The output amplitude of each signal wave is then controlled by a scale ; factor. ;--------------------------------------------------------------- TA .set 11 ; AIC Default startup matches host side app TB .set 18 ; RA .set 11 ; RB .set 18 ; .include "C3XMMRS.ASM" ;================================================== ; Create a bit-reversed (circular addressable) sinewave lookup table ;================================================== .start "TABLE",0X809800 .sect "TABLE" N .set 128 ;================================================== .start "CODE",0x809C00 ; Start of RAM1 .sect "CODE" ; ;================================================== _STOP .set 1 _START .set 2 ; MSG_BOX .word 0 ; 0x809C00 A_REG .word (TA<<9)+(RA<<2)+0 ; 0x809C01 B_REG .word (TB<<9)+(RB<<2)+2 ; 0x809C02 C_REG .word 00000011b ; 0x809C03 +/- 1.5 V LOAD .word 1 ; 0x809C04 set to 1 to reload AIC .word 0 ; 0x809C05 NOISE ON/OFF WNOISELVL .float 1.0 ; 0x809C06 WNOISESCL .word -18<<24 ; 0x809C07 <- NOT MODIFIED BY HOST OFF_LVL .float 0.0 ; 0x809C08 MAXTBL .set 16 ; Maximum entries in table PHASEx .loop MAXTBL .float 0 ; 0x809C09 Phase accumulation rate .float 0 ; 0x809C0A Current offset into array .float 0 ; 0x809C0B Amplitude of term .endloop .float 0.0 ; Terminate table ;-------------------------------- FTBL .word PHASEx First_Add .word TABLE ; Last_Add .word TABLE+N ; FLAGS .word 0 ; NF .float N ; ; S0_gctrl_val .word 0x0E970300 ; Runtime value XINT/RINT enabled S0_xctrl_val .word 0x00000111 ; S0_rctrl_val .word 0x00000111 ; ; PHASE .set 0 OFFS .set 1 A_LVL .set 2 ;------------------------------- main ldi 0x34,IE ; idle ; Wait for next ADC sample ;- - - - - - - - - - - ldi @LOAD,R2 ; If signalled by host, reinitialize AIC bz main ; call AIC_INIT ; Restart with new AIC setup andn GIE,ST ldi MAXTBL-1,RC ldi @FTBL,AR0 ldi 0,R2 rptb INITBL sti R2,*+AR0(OFFS) INITBL nop *++AR0(3) ldi 0,R2 ; reset the LOAD flag sti R2,@LOAD ; b main ; close loop ;============================================================================= MinS .float -16000.0 MaxS .float 16000.0 RACC .set 0 IACC .set 1 RSIG .set 2 ISIG .set 3 MAGN .set 4 pi .set 3.14159265 Rpi .set 2.0*pi/360.0 SFFTdata .word $+1 .float cos(Rpi*40.0) ; phase accumulation rate .float sin(Rpi*40.0) ; 10' per step .float 1.0 ; vector length is unity at all times .float 0.0 ; and rotates at phase accumulation rate .float 0.5 ; .float cos(Rpi*41.01) ; .float sin(Rpi*41.01) ; .float 1.0 ; .float 0.0 ; .float 0.5 ; .float 0.0 FX .float 0 ADC push ST ; On interrupt, set a software flag to push R0 ; let the CPU know that the RINT has been ldi @S0_rdata,R0 ; received ldi @FLAGS,R0 ; or 0x20,R0 ; sti R0,@FLAGS ; pop R0 ; pop ST ; reti ; ;- - - - - - - - - - - DAC push ST ; push R0 ; pushf R0 ; push R1 ; pushf R1 ; push R2 ; pushf R2 ; push R3 ; pushf R3 ; push R4 ; pushf R4 ; push AR1 push IR1 ; ;======================================================================= ldf 0,R1 ; ldi @FTBL,AR1 ; Load start of F,A pairs stf R1,@FX ; ;======================; Calculate F,A pairs NextPair ldf *+AR1(OFFS),R1 ; Accumulate phase addf *+AR1(PHASE),R1 ; 2*PI radians of phase offset == N samples cmpf @NF,R1 ; Check for phase offset rollover blt Bypass ; subf @NF,R1 ; Bypass stf R1,*+AR1(OFFS) ; Save the new phase offset ldf R1,R2 ; R2>= R1 fix R1,R1 ; R1 = offset is now 0-N integer float R1,R1 ; subf R1,R2 ; R2 = 0.000->0.999 fraction of step fix R1,R1 ; ldi @First_Add,AR0 ; addi R1,AR0 ; ldf *AR0++,R1 ; Get 1 st data point, point to next andn N,AR0 ; Circular roll for 2^N tables on 2^N boundary ;- - - - - - - - - - - ldf R1,R0 ; Linear Interpolation doubles mantissa subrf *AR0,R0 ; bit accuracy (7 bits to 14 bits) mpyf R0,R2 ; addf R2,R1 ; accuracy is now improved mpyf *+AR1(A_LVL),R1 ; Magnitude scaling ;===================== addf @FX,R1 ; Sum results stf R1,@FX ; Save sum addi 3,AR1 ; point to next pair ldf *+AR1(PHASE),R1 ; is this the end (phase accum=0)? bnz NextPair ; ;======================================================================= ldf @FX,R1 ; Reload sum of pairs addf @OFF_LVL ,R1 ; Offset addition ;--------------------- ldf @WNOISELVL,R0 ; If white noise level != 0.0 ADD_NOISE callnz RANDX ; Get next 32 bit random float R0,R0 ; convert to float mpyf @WNOISESCL,R0 ; scale to +/-1.0 mpyf @WNOISELVL,R0 ; addf R0,R1 ; ;--------------------- NO_NOISE mpyf @SATURATE,R1 ; Using the ALU saturation mode saves 2 mpyf @UNSATUR8,R1 ; cycles compared to magnitude comparison fix R1,R1 ; Convert to fixed point andn 3,R1 ; Clear MSBs to prevent AIC reprogramming sti R1,@S0_xdata ; output the DAC value ;- - - - - - - - - - - pop IR1 ; Restore register context before return pop AR1 popf R4 ; pop R4 ; popf R3 ; pop R3 ; popf R2 ; pop R2 ; popf R1 ; pop R1 ; popf R0 ; pop R0 ; pop ST ; reti ; return from interrupt ;---------------------------------------- ; Fast 32 bit random number generator ;---------------------------------------- RANDX: ldi @SEED,R0 ; Call here for last SEED RAND: mpyi @A,R0 ; Calculate RAND(R0) addi @C,R0 ; sti R0,@SEED ; Result is returned in R0 rets ; ;---------------------- A .word 0107465h ; Constants needed for RAND C .word 0234567h ; SEED .word 0 ; ;- - - - - - - - - - - - - - - - ; By using the saturation feature of the ; TMS320C3x ALU during multiplies, several ; cycles can be shaved off of a compare/load ; clipping operation ; SATURATE .word 113<<24 ; Since max AIC magnitude is 2^15, multiply UNSATUR8 .word -113<<24 ; by 2^113 which results in 2^128. The ; multiplier constant is created by directly ; placing the value of 113 into the exponent ; exponent field of a 32 bit hex number. ;-------------------------------- GIE .set 0x2000 myidle ; idle ; rets andn GIE,ST ; Use XINT polling during setup to tstb 0x10,IF ; avoid contaminating the DXR during bz $-1 ; programming andn 0x10,IF ; ;idle ; rets ; prog_AIC push R1 ; push IE ; ldi 0x10,IE ; andn 0x30,IF ; ldi @S0_xdata,R1 ; Use original DXR data during 2 ndy sti R1,@S0_xdata ; call myidle ldi @S0_xdata,R1 ; Use original DXR data during 2 ndy or 3,R1 ; Request 2 ndy XMIT sti R1,@S0_xdata ; call myidle ; sti R0,@S0_xdata ; Send register value call myidle ; ; andn 3,R1 ; ldi 0,R1 sti R1,@S0_xdata ; Leave with original safe value in DXR pop IE ; pop R1 ; rets ; ;======================================================; ; This section of code is called by the initialization ; ; code as well as by the main program loop. It is ; ; therfor assembled into the regular program RAM ; ;======================================================; AIC_INIT push R0 ; LDI 0x10,IE ; Enable XINT interrupt andn 0x34,IF ; AIC_reset LDI 2,IOF ; XF0=0 resets AIC ldi 0,R0 ; rpts 0x40 ; sti R0,@S0_xdata ; LDI 6,IOF ; XF0=1 runs AIC ldi 0,R0 ; rpts 0x40 ; sti R0,@S0_xdata ; ldi @S0_rdata,R0 ; ;----------------------------- ldi @C_REG,R0 ; Setup control register call prog_AIC ; ldi 0xfffc ,R0 ; Program the AIC to be real slow call prog_AIC ; ldi 0xfffc|2,R0 ; call prog_AIC ; ldi @B_REG,R0 ; Bump up the Fs to final rate call prog_AIC ; (smallest divisor should be last) ldi @A_REG,R0 ; call prog_AIC ; pop R0 ; ldi 0,R0 ; Put a safe 0 in DXR sti R0,@S0_xdata ; ldi @S0_rdata,R0 ; Clear receive underrun rets ; ;*****************************************************; ; Startup stub... ; ; ; ; The following section of code is used only once for ; ; initialization and can be safely overwritten by ; ; assembling it into the stack or volatile data ; ; storage. ; ;*****************************************************; .entry ST_STUB ST_STUB ldp T0_ctrl ; Use kernel data page and stack ldi @stack,SP ldi 0,R0 ; Halt TIM0 & TIM1 sti R0,@T0_ctrl ; sti R0,@T1_ctrl ; sti R0,@T0_count ; Set counts to 0 sti R0,@T1_count ; ldi 1,R0 ; Set periods to 1 sti R0,@T0_prd ; sti R0,@T1_prd ; ldi 0x2C1,R0 ; Restart both timers sti R0,@T0_ctrl ; sti R0,@T1_ctrl ; ;--------------------- ldi @S0_xctrl_val,R0; sti R0,@S0_xctrl ; transmit control ldi @S0_rctrl_val,R0; sti R0,@S0_rctrl ; receive control ldi 0,R0 ; sti R0,@S0_xdata ; DXR data value ldi @S0_gctrl_val,R0; Setup serial port sti R0,@S0_gctrl ; global control ;--------------------- andn 0x30,IF ldi 0x30,IE ; Service both RINT/XINT ; call AIC_INIT ; Initialize the AIC ldi @S0_rdata,R0 ; b main ; stack .word $+1 ;****************************************************; ; Install the XINT/RINT ISR handler directly into ; ; the vector RAM location it will be used in ; ;****************************************************; .start "SP0VECTS",0x809FC5 .sect "SP0VECTS" B DAC ; XINT0 B ADC ; RINT0