;************************************************************************* ; Some parts of this code, notably the start-up and table->xilinx encoder; ; code, and the include files are ; ; Copyright (2000) CRC for Cochlear Implant and Hearing Aid ; ; Innovation and HearWorks Pty. Ltd. Australia ; ; ; ; Main code Originally Written by Andrew Vandali ; ; ; ; Subsequently and severely modified by Dan Downs (in ways Andrew would ; ; probably not want to be associated with) for the MRC-CBU. ; ; ; ; Source: sprtbl09.asm, 03 Apr 2005. ; ; ; ;************************************************************************* ; Changes from sprtbl08: ; 1) Add an attenuator and subtractor, per side. These are X_ATTEN, Y_ATTEN, ; X_SUBTRACT, and Y_SUBTRACT. These are 8 bit values which ; operate on the pulse amplitude as follows (X/Right side shown only): ; -- the amplitude value taken from the Stimulus Table is multiplied ; by X_ATTEN, a value between 255 and 0, where 255 is a multiplication ; factor of 1, 128 is a multipliction factor of 0.5, etc, then ; -- Subtract from that product the value of X_SUBTRACT, and pass that ; value as the amplitude of the pulse. Negative values are converted ; to a zero amplitude. ; On each RELOAD command, these values will be re-read. ; 2) Bumped the beginning of the Stimulus Tables from 2C to 34 to make ; room for variables. I should have done this from the beginning. ;************************************************************************* ;************************************************************************* ; To get the HTML verison of this, type: ; perl -e 'while(<>){if (/^;/) {$IN=1;} if (/<\/html>/) {$IN=0;} s/^;// ;print if $IN ;} print "<\/html>"' sprtbl09.asm > sprtbl.html ;************************************************************************* ; ; ; Brief guide to the SPRTBL firmware ; ; ;
;
;

SPRTBL Firmware ;

;
;
;

Introduction ;

;
; This document describes the operation of the SPRTBL firmware. The SPRTBL ; firmware runs on a SPEAR3 Speech Processor and translates compressed tabluar ; data into output stimulus to its cochlear implant drivers. This firmware is ; intended for research work only. The SPEAR3 Speech Processor is from ; HearWorks Pty Ltd. ;

This document is intended as a guide to anyone needing to understand the SPRTBL ; firmware, presumably for "high level software" integration or modification of ; the firmware itself. ;

This HTML document itself is embedded in the SPRTBL firmware and is simply copied ; out of the comments in the firmware with a single line of Perl code. This means ; that there is only a single source of documentation. Any modifications to the ; document should be made as comments in the SPRTBL firmware and the HTML file ; should then be regenerated. ;

The SPRTBL firmware was written for the ; MRC-CBU ; under the direction of Dr. Christopher Long. ;

; ;
; ;

Contents ;

;
;
    ;
  1. Objectives ;
  2. Memory Map ;
  3. Driving SPRTBL ;
  4. SPRTBL internal operation ;
;
; ;
; ; ;

Objectives ;

;
; Briefly, the objectives are: ;
  • Whatever else, maximize precision of all timing aspects of the output stimulus ;
  • Minimize the delay between High Level Sofware to output stimulus ;

    The name "SPRTBL" is derived from SPEAR3 TaBLe, and is intended to reflect the ; particular method applied to solve the particular problem(s) in using the ; SPEAR3 in psychoacoustic research, according to the objectives. Really, there ; are a great deal of other concerns, but the SPRTBL firmware can only address the ; two of them listed above. ;

    Attempts to reach the first objective are described in detail below, and center ; around using the DSP56300's Fast Interrupt Mode. ;

    Attempts to reach the second objective, also described below, amount to minimizing ; the volume of data to be transfered to the SPEAR3 device to get it to output ; new stimulus. This resulted in implementing a compression scheme for the data which ; must be preloaded, and moves many burdens to the High Level Software where memory and ; time constraints are not an issue. ;

  • ;
    ; ; ;

    Memory Map ;

    ;
    ; This is not a guide to the SPEAR3 hardware nor the Motorola DSP56309 which it contains. ; This Memory Map section will only cover what the High Level Software, henceforth HLS, ; needs to know. ;
    ;
    ; ; ; ;
    ; ; P-Memory (Program Memory) ; ;
    ; Once the firmware has been loaded there are NO ;
    locations in P-Memory accessible to the HLS. ;
    ;
    ;
    ;
    ;
    ;
    Address ranges in the tables below are colour coded. ;
    This colour means these locations are RESERVED. ;
    This colour means that the values here are fixed per "run" (more on that below). ;
    This colour means that these locations are expected to be accessed during a test run by the HLS. ; Note that there are only 3 such locations, 2 in Y-Memory and 1 in X-Memory. ;
    ;
    ;
    ; ; ; ; ;
    ; ; Y-Memory / Left Side Encoder Data ; ;
    ;
      
    ; Address(es)      Default value       Name                   Description
    ;-----------------------------------------------------------------------------------------------------
    ; $000000             000000           Y_RELOAD              Write a 1 to start.
    ;
    ; $000001                                                    Internal. Don't touch.
    ;
    ; $000002               2000           Y_STptr               Pointer to Y/Left  Stimuli Table
    ;
    ; $000003 - $000004                                          Internal. Don't touch.
    ;
    ; ------------------ Y-fixed beginning ----------------------------------
    ; This section sets Active El, Ref El, Amplitude of pulse N
    ; $000005                 20           Y_ActE                Active electrode
    ; $000006                 30           Y_RefE                Reference elec (MP1+2)
    ; $000007 - $000008      0,1                                 Phase 1 duration (25 us)
    ; $000009 - $00000A    188,0                                 Inter-phase gap (45us)
    ; $00000B                  0           Y_AmpAE               Amplitude (0)
    ; $00000C - $00000D      0,0                                 Phase 2 duration (25 us)
    ; $00000E - $00000F      5,0                                 Inter-frame gap (8 us)
    ;
    ; This second section Sets Phase and IPG of pulse N (sets Active, Ref, Amp of N+1)
    ; $000010                 24                                 Active electrode (MP1)
    ; $000011                 25                                 Reference elec (MP2)
    ; $000012 - $000013      0,0                                 Phase 1 duration (25 us)
    ; $000014 - $000015    188,0                                 Inter-phase gap (8 us)
    ; $000016                  0                                 Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO
    ; $000017 - $000018      0,0                                 Phase 2 duration (25 us)
    ; $000019 - $00001A      5,0                                 Inter-frame gap (8 us)
    ; $00001B                $80                                 Encoder Halt bit, loaded in Active Elec location
    ;
    ; This section sets describes a null pulse, a Power pulses only
    ; $00001C                 20                                 Active electrode (electrode #20)
    ; $00001D                 30                                 Reference elec (MP1+2)
    ; $00001E - $00001F      0,0                                 Phase 1 duration (25 us)
    ; $000020 - $000021    188,0                                 Inter-phase gap (45 us)
    ; $000022                  0                                 Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO
    ; $000023 - $000024      0,0                                 Phase 2 duration (25 us)
    ; $000025 - $000026      5,0                                 Inter-frame gap (8 us)
    ; $000027                $80                                 Encoder Halt bit, loaded in Active Elec location
    ; ------------------ Y-fixed end ----------------------------------
    ;
    ; $000028 - $00002B                                          Internal. Don't touch.
    ;
    ; ------------------ Y-table beginning ----------------------------------
    ; Beginning of the Y/Left side Stimuli Table
    ; $000034            $14C81E           Eword 0               |elect #A|  AMP    |elec #R |
    ; $000035            $011000           Tword 0               |TTNP (Time Till Next Pulse)|
    ; $0000bobobobo      $14B81E           Eword 1                  ...
    ; $00002F            $010000           Tword 1                  ...
    ; $000030            $14A81E           Eword 2                  ...
    ; $000031            $00f000           Tword 2
    ; $000032            $14981E           Eword 3                  ...
    ; $000033            $00e000           Tword 3
    ; $000034            $14881E           Eword 4                  ...
    ; $000035            $00d000           Tword 4
    ; $000036            $14781E           Eword 5                  ...
    ; $000037            $00c000           Tword 5
    ; $000038            $14681E           Eword 6                  ...
    ; $000039            $00b000           Tword 6
    ; $00003A            $14581E           Eword                    ...
    ; $00003B            $00a000           Tword
    ; $00003C            $14481E           Eword                    ...
    ; $00003D            $009000           Tword
    ; $00003E            $14581E           Eword                    ...
    ; $00003F            $008000           Tword
    ; $000040            $14681E           Eword                    ...
    ; $000041            $007000           Tword
    ; $000042            $14781E           Eword                    ...
    ; $000043            $006000           Tword
    ; $000044            $14881E           Eword                    ...
    ; $000045            $005000           Tword
    ; $000046            $14981E           Eword                    ...
    ; $000047            $004000           Tword
    ; $000048            $14A81E           Eword                    ...
    ; $000049            $003000           Tword
    ; $00004A            $14B81E           Eword                    ...
    ; $00004B            $002000           Tword
    ; $00004C            $14C81E           Eword                    ...
    ; $00004D            $001000           Tword
    ; $00004E            $ffffff           Eword N               electrode = FF means end of table.
    ; $00004F - $FEFFFF                                          Available for table data             
    ; ------------------ Y-table end ----------------------------------
    ;
    ; $FF0000 - $FFFFFF                                          Reserved. Don't touch.
    ;
    ;
    ;
    ;
    ;
    ;
    ; ; ; ; ;
    ; ; X-Memory / Right Side Encoder Data ; ;
    ;
      
    ; Address(es)      Default value       Name                   Description
    ;-----------------------------------------------------------------------------------------------------
    ;
    ; $000000 - $000001                                          Internal. Don't touch.
    ;
    ; $000002               2000           X_STptr               Pointer to X/Right Stimuli Table
    ;
    ; $000003 - $000004                                          Internal. Don't touch.
    ;
    ; ------------------ X-fixed beginning ----------------------------------
    ; This section sets Active El, Ref El, Amplitude of pulse N
    ; $000005                 20           X_ActE                Active electrode (electrode #20)
    ; $000006                 30           X_RefE                Reference elec (MP1+2)
    ; $000007 - $000008      0,0                                 Phase 1 duration (25 us)
    ; $000009 - $00000A    188,0                                 Inter-phase gap (45us)
    ; $00000B                  0           X_AmpAE               Amplitude (0)
    ; $00000C - $00000D      0,0                                 Phase 2 duration (25 us)
    ; $00000E - $00000F      5,0                                 Inter-frame gap (8 us)
    ;
    ; This second section Sets Phase and IPG of pulse N (sets Active, Ref, Amp of N+1)
    ; $000010                 24                                 Active electrode (MP1)
    ; $000011                 25                                 Reference elec (MP2)
    ; $000012 - $000013      0,0                                 Phase 1 duration (25 us)
    ; $000014 - $000015    188,0                                 Inter-phase gap (8 us)
    ; $000016                  0                                 Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO
    ; $000017                0,0                                 Phase 2 duration (25 us)
    ; $000018 - $000019      5,0                                 Inter-frame gap (8 us)
    ; $00001A - $00001B      $80                                 Encoder Halt bit, loaded in Active Elec location
    ;
    ; This section sets describes a null pulse, a Power pulses only
    ; $00001C                 20                                 Active electrode (electrode #20)
    ; $00001D                 30                                 Reference elec (MP1+2)
    ; $00001E - $00001F      0,0                                 Phase 1 duration (25 us)
    ; $000020 - $000021    188,0                                 Inter-phase gap (45 us)
    ; $000022                  0                                 Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO
    ; $000023 - $000024      0,0                                 Phase 2 duration (25 us)
    ; $000025 - $000026      5,0                                 Inter-frame gap (8 us)
    ; $000027                $80                                 Encoder Halt bit, loaded in
    ; ------------------ X-fixed end ----------------------------------
    ;
    ; $000028 - $00002B                                          Internal. Don't touch.
    ;
    ; ------------------ X-table beginning ----------------------------------
    ; Beginning of the Y/Right side Stimuli Table
    ; $000034            $14C81E           Eword 0               |elect #A|  AMP    |elec #R |
    ; $000035            $009000           Tword 0               |TTNP (Time Till Next Pulse)|
    ; $0000bobobobo      $14C81E           Eword 1                  ...
    ; $00002F            $009000           Tword 1                  ...
    ; $000030            $14C81E           Eword 2                  ...
    ; $000031            $009000           Tword 2
    ; $000032            $14C81E           Eword 3
    ; $000033            $009000           Tword 3
    ; $000034            $14C81E           Eword 4
    ; $000035            $009000           Tword 4
    ; $000036            $14C81E           Eword 5
    ; $000037            $009000           Tword 5
    ; $000038            $14C81E           Eword 6
    ; $000039            $009000           Tword 6
    ; $00003A            $ffffff           Eword N               electrode = FF means end of table.
    ; $00003B - $FEFFFF                                          Available for table data             
    ; ------------------ X-table end ----------------------------------
    ;
    ; $FF0000 - $FFFFFF                                          Reserved. Don't touch.
    ;
    ;
    ;
    ;
    ; ; ;
    ; ; ;

    Driving SPRTBL ;

    ;
    ;

    Basic Operation ;

    ; The process begins with the High Level Software (HLS) loading the SPRTBL "in the usual ; way" via the serial port interface with Womera or a similar program. Once loaded and started, ; SPRTBL is idle until given a command to start, which for historical reasons is called ; "Y_RELOAD", or "RELOAD". The "usual way" of downloading the program is not covered here. ;

    ; During the idle time, the HLS is expected to load stimulus data into the SPEAR3's ; internal X and Y Memories. There are four separate regions of memory, two for each ; side. The X-Fixed and Y-Fixed sections (see the tables above) are stimulus parameters ; which do not change during the course of a "run", while the X-table and Y-table ; sections are the stimulus parameters which vary (can vary) between output pulses, ; and these sections are compressed/encoded. Here is an example of a table entry, copied ; from the Y-Memory Memory Map above: ;

    ; $00002C            $14C81E           Eword 0               |elect #A|  AMP    |elec #R |
    ; $00002D            $011000           Tword 0               |TTNP (Time Till Next Pulse)|
    ;
    ; Which is decoded as: ;
    The active electrode will be 14 hex, or 20 decimal, ;
    The amplitude of the active electrode will be C8 hex, or 200 decimal, ;
    The reference electrode will be 1E hex, or 30 decimal, and ;
    The time this pulse will be output is 011000 hex, or 69632 ; ; ; ; ;

    ; When the stimulus has been loaded, the RELOAD command can be issued. The RELOAD ; command is a "1" written to location 0x000000 of X-Memory. The other (highest) 23 ; bits of this location are reserved for future use and should be written as zeros. ;

    ; Following this RELOAD command SPRTBL reads data from tables in internal X ; and Y memories and converts them to stimuli (sends commands to the encoders). ; Data in the X-Memory area is for the Right side output and data in the Y-Memory is ; for the Left side output. SPRTBL reads the X-Memory/Right side data table from the ; address given in the X_STptr variable until a stop code is reached. Likewise, SPRTBL ; reads the Y-Memory/Left side data table from the address given in the Y_STptr variable ; until a stop code is reached. ; ;
    After both the sides have stopped, SPRTBL needs another "RELOAD" to begin again. ; In a way then, SPRTBL is ballistic. There are no provisions for stopping it until ; it is done reading/converting/outputing. ; ;
    In order the output a purst of pulses, ; ;

    ; Since there is only one simple command, all the complexity is in creating (and ; writing) the data tables. In order to understand the form of these tables it is ; important to understand the data which the encoders need. ;

    ;

    The Stimulus Parameters ;

    ; There are two encoders which drive the cochlear implants, one per side. Only one will ; be discussed here. The implants accept information to control the following parameters: ;
  • ;
  • ;
  • ;
  • ;
  • ; ;
  • ; ;
    ; ; ;

    SPRTBL internal operation ;

    ; ; ; ; ;Keep out of this memory space: ; X data memory ($FFFF80-$FFFFFF in ;and this space too: ;Y data ;memory ($FFFF80-$FFFFFF ; ;and here: ;The Y memory space at locations $FF0000 to $FFEFFF is reserved and should not be ;accessed by the programmer. ;and here: ;The X memory space at locations $FF0000 to $FFEFFF is reserved and should not be ;accessed by the programmer. ; ; 7168 is 0x1c00. ; 3584 Eword/Tword pairs. ; ; NOTES on variable naming ; --------------------------- ; Y_thingy means the variable is stored in Y mem, ; X_thingy means the variable is stored in X mem, ; thingy means the variable is stored in P (program) mem, ; X <==> Right <==> R, ; Y <==> Left <==> L, ; BUT, ; registers x0, x1, y0, and y1 are not fixed to X or Y, R or L. ; but x0 and x1 are dedicated as flags for noting that an interrupt ; occured on the right and left sides. ; ; There are a few variables not specific to either side, and they ; happen to be in the Y memory space. ; ;======================================================== ; Picture of the memory map (and other stuff too): ; X is RIGHT side Y is the LEFT side ; +---------------+ +---------------+ ; | static and | | static and | ; | pseudo-static | | pseudo-static | ; | parameters | | parameters | ; +---------------+ +---------------+ +-------------+ ; | Encoder Table | | Encoder Table |<----|decompression| ; | active | | active | | | ; +---------------+ +---------------+ | 0 1 2 ... N | ; | Encoder Table | | Encoder Table | +-------------+ ; | null pulses | | null pulses | / / / / ; +---------------+ +---------------+ / / / / ; | unused | | other vars | / / / / ; +---------------+ +---------------+ / / / / ; | Eword(0) |<--beginning of-->| Eword(0) | \/ / / / One at a time, decompress ; | Tword(0) | Stimuli Table | Tword(0) | / / / / from the Stimuli Table and ; | Eword(1) | | Eword(1) | \/ / / place in the Encoder Table, ; | Tword(1) | | Tword(1) | / / / using the Eword for Electrode ; | Eword(2) | | Eword(2) | \/ / data and the Tword for Timing ; | Tword(2) | | Tword(2) | / / data (the Time Till Next Pulse). ; ~ ... ~ ~ ... ~ / ; | Eword(N) | Stimuli Table | Eword(N) |\____/ ; | Tword(N) |<------end------->| Tword(N) |/ ; +---------------+ +---------------+ ; ;------------------------------------------------------- ; This code uses fixed locations for the Right and Left side ; Encoder Tables and a fixed location for the beginning of the ; Stimuli Tables, but no limit on the length of the Stimuli ; Table until the end of memory is reached. It is important ; to note ALL changes to this memory map since the high level ; SW writes to these addresses to set up stimuli, start and ; control the generation of stimuli. ; ; The way it works is that the contents of the fixed (fixed ; in memory location, not content) Encoder Table is copied to ; xilinx encoders on a RELOAD command (see below). That data ; is sent to the implant. ; One pass through an Encoder Table is a single pulse of a ; single electrode. The Right side tables, stimuli and ; null/power pulses, are in the space between the labels: ; X_CI24beginActive ; X_CI24endActive ; and ; X_CI24beginNull ; X_CI24endNull ; ; and the Left side tables, stimuli and ; null/power pulses, are in the space between the labels: ; Y_CI24beginActive ; Y_CI24endActive ; and ; Y_CI24beginNull ; Y_CI24endNull ; ; ; As one can see, unless the contents of the Encoder Table change, ; this will result in only a single pulse of unchanging characteristics ; being delivered. ; ; To drive more interesting stimuli, a compressed Stimuli Table is ; written before the RELOAD is triggered. One block of ; this compressed Stimuli Table is decompressed and replaces the ; contents of the Encoder Table, and this is "fired off" to the ; encoder on interrupt. An attempt to capture this pictorially is ; shown above to the right of the picture of the memory map. ; ; The ability to compress the stimuli results from the fact that ; not every parameter changes during a stimulus sequence, so these ; values do not get stored in the compressed Stimuli Table (and if ; there was a lot more on chip memory, none of this would be necessary. ; Compression is only to simulate a long sample table). ; The fixed stimuli are marked in the Encoder Table below with "FIXED": ; ; Encoder Table for CI24, Right, X ; Sets Active El, Ref El, Amplitude of pulse N ; ;X_CI24beginActive ; ;variab dc 20 ; Active electrode (#20) ; elect # ;variab dc 30 ; Reference elec (MP1+2) ; ref elec ;FIXED dc 0,0 ; Phase 1 duration (25 us) ; phase-1a phase-1b ;FIXED dc 5,0 ; Inter-phase gap (8 us) ; inter-1a inter-1b ;variab dc 200 ; Amplitude (200) ; AMP ;FIXED dc 0,0 ; Phase 2 duration (25 us) ; phase-2a phase-2b ;FIXED dc 5,0 ; Inter-frame gap (8 us) ; inter-2a inter-2b ; ; Sets Phase and IPG of pulse N (sets Active, Ref, Amp of N+1) ;variab dc 24 ; Active electrode (MP1) ;variab dc 25 ; Reference elec (MP2) ;FIXED dc 0,0 ; Phase 1 duration (25 us) ;FIXED dc 5,0 ; Inter-phase gap (8 us) ;FIXED dc 0 ; Amplitude (0) ;FIXED dc 0,0 ; Phase 2 duration (25 us) ;FIXED dc 5,0 ; Inter-frame gap (8 us) ; ; ;FIXED dc $80 ; Encoder Halt bit, loaded in Active Elec location ;X_CI24endActive ; ; ; ;X_CI24beginNull ; Power pulses only, Right. ;FIXED dc 20 ; Active electrode (electrode #20) ;FIXED dc 30 ; Reference elec (MP1+2) ;FIXED dc 0,0 ; Phase 1 duration (25 us) ;FIXED dc 5,0 ; Inter-phase gap (8 us) ;FIXED dc 75 ; Amplitude (75) ;FIXED dc 0,0 ; Phase 2 duration (25 us) ;FIXED dc 5,0 ; Inter-frame gap (8 us) ;FIXED dc $80 ; Encoder Halt bit, loaded in Active Elec location ; X_CI24endNull ; ; ; As each of the lines above with "variab" is only a byte value, ; these three can be compressed into a single 24 bit word. ; ; In addition to these three values, it is also needed to ; allow for the possibility of varying the time between pulses. ; This can be done with a single 24bit word which is stored as ; the next value to be loaded into the interrupt controller. ; This means that each stimulus pulse takes up 2 24bit words ; in either X or Y memory, and will be organized like so: ; |elect #A| AMP |elec #R | Eword ; |TTNP (Time Till Next Pulse)| Tword ; ; X and Y MEMORY MAP at the time of this writing (please ; always refer to the code for the actual map): ; X is RIGHT side Y is the LEFT side ;------------------------------------------------------- ; unused Y_RELOAD ; X_TTNP Y_TTNP ; X_Groups Y_Groups ; X_Delay Y_Delay ; X_TrainCount Y_TrainCount ; unused X_Delay_AMBI ; X_DONE Y_DONE ; ; X_CI24beginActive Y_CI24beginActive ; X_CI24endActive Y_CI24endActive ; X_CI24beginNull Y_CI24beginNull ; X_CI24endNull Y_CI24endNull ; unused save_x0 ; unused save_y0 ; unused save_aa ; unused ExtInputState ; M words of HEADROOM ; ; X_StimHead Y_StimHead ; Beginning of compressed Stimuli Tables ; Eword(0) Eword(0) ; Tword(0) Tword(0) ; Eword(1) Eword(1) ; Tword(1) Tword(1) ; Eword(2) Eword(2) ; Tword(2) Tword(2) ; ... ... ; Eword(N) Eword(N) ; Table terminates when elect#A == 255. ; Tword(N) Tword(N) ; ;------------------------------------------------------------------------------; ; THIS SECTION OBSOLETE FROM HERE TO ....... ;------------------------------------------------------------------------------; ; WHAT THIS CODE DOES ; =================== ; This code outputs trains of pulse groups to the right and left channels of the ; cochlear implant drivers according to pre-specified parameters. The goal is to ; provide pulse amplitude, width, and period control per channel; but especially ; to provide control of right vs left time delays, the Interaural Time Difference ; or ITD. ; The parameters are loaded from a host computer through the serial interface. ; The host computer then issues a RELOAD command, also through the serial ; interface, and the SPEAR3 then outputs pulse groups according to those ; parameters to the right, left or both channels. The RELOAD command consists ; simply of writing a "1" to location 0 in the Y memory space, which is cleared ; immediately by this firmware after detecting the "1". ; ; This code will do some setup, enable interrupts, and, while keeping count of ; the number of requested pulses, trigger the right and/or left side Xilinx ; encoders to output the contents of the encoder table at the specified rate. ; The encoder table could specify 0 to 22 electrodes, but whatever it is, that ; is what I am calling a pulse "group". The stimulus of 0 to 22 electrodes can ; be in any number or order, provided that an electrode is specified only once. ; A pulse train is defined as a periodic repeat of a pulse group, though a train ; length of 0 or 1 is allowed. ; ; The pulse train can consist of 0 to (2^32)-1 groups as specified prior to the ; RELOAD command. The pulse train will be transmitted with a specified period, ; a specified number of times, but parameters assigned to each electrode cannot ; change during the pulse train. The period between consecutive pulses in any ; given train can differ from channel to channel from ; ICPPDIFF_MIN to ICPPDIFF_MAX, and either side can be made to lead the other by ; ICPCDIFF_MIN to ICPCDIFF_MAX counts or by a time of ITDMIN to ITDMAX . The ; limits on those last six parameters are governed by the power storage ; capability of the implants. This is detailed below. ; ; Perhaps the most important thing to note for anyone wishing to edit this code ; is that in an effort to reduce the unpredictabiity associated with responding to ; interrupts, the Fast Interrput Mode has been used. An unfortunate consequence ; of this is that, given the existing hardware and the limits of instruction ; word size to use this mode, there is essentially only a single instruction that ; will accomplish the task. The reason for this is that two things must occur on ; that instruction, namely 1) Start the encoder to transmit, thus reducing the ; time and indeterminacy from interrupt to pulse output, and 2) making a note ; that this interrupt occured. In the code, that is: ; ; move b,x0 a,y:ENCSTRT24R ; Start Encoder and ; ; note that this interrupt happened. ; ; Most of the apparently peculiar coding is here merely to support that single ; instruction. Things like using the entirety of r4, x0, and y0 as flags so as ; to avoid condition code changes on interrput response. I have noted all these ; things in the code where I could. ; ; Another peculiarity is the inclusion of the entire encoder tables within ; the Y memory space. This was done because writing to the P memory space causes ; the device to reset, a behaviour mediated by the serial interface download code. ; This reset is unacceptable if one wishes to modify the electrode parameters during ; an experiment. ; ; OPERATING NOTES ; =============== ;-------------------------------------------------------- ; Do not write to the device while it is transmitting. ; The green LED will be ON when it is safe to transmit. ; But the user program also ought to know how long the ; pulse trains it is requesting are, so the LED is just ; an extra. ; ;-------------------------------------------------------- ; In order to start the device transmitting, write a 1 ; to bit zero of the zeroth location of Y memory. ; 0x000000 Y_RELOAD ; The other bits of this memory location are unused. ; ;-------------------------------------------------------- ; To change the period between groups in a train of either ; side, write new values to the two locations in Y memory: ; 0x000001 Y_RightPeriod ; 0x000002 Y_Left_Period ; where the period is a number of 0.13605 usec counts. ; ;-------------------------------------------------------- ; To set the number of pulses in a pulse train of either ; side, write new values to the two location in Y memory: ; 0x000003 Y_GroupsRight ; 0x000004 Y_Groups_Left ; These are unsigned ints, and zero is okay. ; ;-------------------------------------------------------- ; To delay the start of the right side pulse train with ; respect to the left, write a non-zero value to: ; 0x000005 Y_DelayRight ; where the time is a number of 0.13605 usec counts. ; ;-------------------------------------------------------- ; To delay the start of the left side pulse train with ; respect to the right, write ZERO to: ; 0x000006 Y_DelayRight ; AND write a non-zero value to: ; 0x000007 Y_Delay_Left ; where the time is a number of 0.13605 usec counts. ; ;-------------------------------------------------------- ; The values in the counters are related to time by the ; CLK half frequency which drives the timers. The CLK is ; 14.7MHz, so CLK/2 is 7.35MHz, or 0.13605 usec period. ; ;-------------------------------------------------------- ; To decrease the ISR time and use the fast interrupt mode, ; entire registers are used as flags. This keeps the ; condition code register unchanged during the interrput. ; x0, is the flag for noting a right side, Timer0, interrupt occured. ; x1, is the flag for noting a left side, Timer1, interrupt occured. ; r4, is the flag for noting the ITD, Delay, is done. ;-------------------------------------------------------- ; ; ; ; TIMING ANALYSIS ; =============== ; There are several scales of time to be concerned with. The discussion below ; starts from the most coarse grain to the finest grain. The coarse timing involves ; user/Host computer operations and the finest relates to the interrupt ; response of the DSP which ultimately determines the resolution and jitter of ; pulses issued from the SPEAR3 device. ; ; TOP LEVEL TIMING ; ================ ; This is a diagram of the overall operation (not to scale): ; ; |~~~~~~~~~~~~~~~~~|~~~~~|--------------XXXXXXXXXXXXXXXXXXXXXX----------------- ; | | | \________ __________/ ; | | | | \/ | ; T0 T1 T2 T3 PT T0 ; ; ; T0 to T1: This time is of arbitrary length. It is the time it takes the high ; level software on the host computer to write the parameters. T1 is the time at ; which the RELOAD command is *apparently* written BY THE HOST COMPUTER. ; This is not really something that can be measured. ; ; T1 to T2: The delay from the *apparent* write to the firmware's detection of ; the RELOAD command. T2 is the first time that can be referenced to within the ; SPEAR3. ; ; T2 to T3: The time it takes the DSP code to respond with the first pusle ; following a RELOAD. This is computable and is based only on the number of ; instructions required. ; T2 to T3 is ________________________________. ; ; IMPORTANT NOTE: The device must not be written to from T1 untill the next T0. ; The green LED is on when it is safe to write to the device. Writing to the ; device within this period will lead to unpredictable behaviour since there ; are NO interrupt disable provisions. ; ; ; ; PULSE TRAIN TIMING, PART 1 ; ========================== ; All timing is dependent on the CPU clock rate. This is set at 14.7456MHz, the ; crystal oscillator frequency, which is then divided by two giving 7.35MHz. ; This means that the minimum time resolution for any event is 0.13605 usec. For ; example, there are 4 parameters the user can set which use this clock, ; and all of these take effect in the XXXXXX bit above, from T3 to the next T0, ; namely the pulse train PT. The right and left pulse trains can differ. And ; just to be clear here, a pulse train for a given side is the collection of ; all the pulse groups generated from a RELOAD command until that side has output ; the pre-specified number of groups. These parameters are: ; Y_GroupsRight ; Y_Groups_Left ; ; So expanding on PT above, these parameters are as shown: ; ; ; |<--------tR------->| ; | | ; Right ______|RRRRR______________|RRRRR_______________RRRRR_______________RRR ; |RRRRR |RRRRR RRRRR RRR ; | | ; | |<---------tL--------->| ; | | | ; Left ______|_____|LLLLL_________________|LLLLL__________________LLLLL______ ; | |LLLLL |LLLLL LLLLL ; | | ; ^_ITD_^ ; ; Where RRRRR is the output pulse group on the right side and LLLLL is the output ; pulse group on the left side. ; The time between pulses for a given side are tR and tL. ; The delay between the beginning of one side's pulse train and the other side's ; is ITD. The Right side is shown leading the left but it could be the other ; way around just as well. The IDT is set with the parameters: ; Y_DelayRight ; Number of counts to delay the right side pulse train ; Y_Delay_Left ; Number of counts to delay the left side pulse train ; ; ; ; PULSE TRAIN TIMING, PART 2 ; ========================== ; Expanding on the RRRRR and LLLLL above, the simplest pulse groups to discuss ; consist of a single electrode being stimulated as shown below. There is ; currently no provision to change the electrode number(s) once a pulse train ; has begun. While the Encoder Table can be changed prior to a PT, it is static ; for the duration of a PT. This code includes the Encoder tables beginning at ; address Y_CI24beginActiveR and Y_CI24beginActiveL. ; ; ----RRRRRRRRRRRRRRRRRR---- ; ; |<-------tR------->| ; A single | _ | _ _ ; Right side ____| | \_____________| | \______________| | \______________ ; Electrode |\_| |\_| |\_| | ; | | | ; | |-tgR-| ; | ; | ----LLLLLLLLLLLLLLLLLLLLLL---- ; | ; | |<---------tL--------->| ; A single | | _ | _ _ ; Left side ____|_____| | \_________________| | \__________________ | \__ ; Electrode | |\_| |\_| |\_| | ; | | | | ; ^_ITD_^ | | ; |-tgL-| ; ; The Time tR is set by: ; Y_RightPeriod ; and the time tL is set by: ; Y_Left_Period ; Note that the only thing the DSP can do is count, so the notion of "period" ; must be given as ; tR = Y_RightPeriod counts /( counts/0.13605 usec) ; ; The parmeters tgR and tgL ad discussed below. ; ; ; ; ; PULSE TRAIN TIMING, PART 3 ; ========================== ; Here, the notation tG refers to tgR and/or tgL, and tS refers to tR and/or tL. ; More than one electrode can be stimulated per channel per Pulse Train. This ; makes a pulse group. The selection depends entirely on the length/content of ; the Encoder Table. A longer table (basically, more electrodes) ; means a longer delay from T2 to T3 and a longer RRRRR and/or LLLLL. The min is ; a single electrode programmed with minimum values shown here: ; dc 24 ; Active electrode (MP1) ; dc 25 ; Reference elec (MP2) ; dc 0,0 ; Phase 1 duration (25 us) ; dc 5,0 ; Inter-phase gap (8 us) ; dc 0 ; Amplitude (0) ; dc 0,0 ; Phase 2 duration (25 us) ; dc 5,0 ; Inter-frame gap (8 us) ; and the maximum is all the electrodes programmed with the values shown here: ; dc 24 ; Active electrode (MP1) ; dc 25 ; Reference elec (MP2) ; dc 0,0 ; Phase 1 duration (25 us) ; dc 5,0 ; Inter-phase gap (8 us) ; dc 0 ; Amplitude (0) ; dc 0,0 ; Phase 2 duration (25 us) ; dc 5,0 ; Inter-frame gap (8 us) ; ; The diagram below shows electrodes 9, 1, 3, and 5 being used. One thing to ; note is that the order of the electrodes is dependent on the order given in ; the table and that the total width of the group must be considered to be the ; width of all the pulses in the group plus their inter-phase gaps plus their ; inter-frame gaps. Note that tG must always be smaller than tS or bad things ; will happen. That is because interrupts will arrive to issue new pulse trains ; before the previous one finishes. And as a consequence of using the Fast ; Interrput mode no interrupt masks are set to catch this. ; ; ; ; |<-------tS-------->| ; Multiple | | ; electrodes for __9135________________9135________________9135______________ ; side S ->| |<- ; tG ; ; ; IMPORTANT NOTE: If the electrode number and/or order and the Phase 1, Phase 2, ; Inter-phase gap, Inter-frame gap for each channel are not identical then, ; although each pulse train will *begin* with the desired ITD, the actual ; electrode pulses will have different delays with respect to the other side. ; ; IMPORTANT NOTE II: Re-read the important note above. ; ; ; TIMING RESOLUTION ; GENERAL ; ================= ; The timing of a group onset is interrupt driven. The timer module is in a ; mode where it needs no overhead to restart. If the interrupt timer is loaded ; with the value of 0x008F8E, for example, it will issue an interrupt every ; 5 miliseconds independent of any other event until it is turned off (by another ; part of the code which is counting pulse groups). This means that ; the times to be concerned with are: ; 1) the time to start the encoder from receiving an interrupt, ; 2) what happens with simultaneous interrupts on both sides, ; 3) the initial itd. ; These three are detailed below, but the important timing is related to the ; timer running at the cpu clk resolution, the interrupt priority scheme, and ; the CPU pipeline behaviour during interrupt. ; ; The interrput priorities with the triple timer itself are set as a block. ; There is no way to set different timers at different levels. Timer 0 is used ; for the Right channel, Timer 1 for the Left channel and Timer 2 to set the ; ITD. ; ; The fixed-priority structure within the Timer Module Interrput Priority Level ; (IPL) is: ; highest ; ... ; Timer0 Overflow ; Timer0 Compare ; Timer1 Overflow ; Timer1 Compare ; Timer1 Overflow ; Timer1 Compare ; ... ; lowest ; ; CPU pipeline on Fast Interrupt ; When the interrupt occurs the following sequence follows: ; interrupt -> ; sync to clk -> ; arbitrate -> ; put instr at head of pipe-> ; wait for other instructions to complete. ; ; In the traditional diagram, interrput instruction words i1 and i2 ; are loaded following arbitration as shown: ; Operation Instruction cycle ; --------------------------------------------- ; 1 2 3 4 5 6 7 8 9 10 11 12 ; PF1 n1 n2 i1 i2 n3 n4 ; PF2 n1 n2 i1 i2 n3 n4 ; Decode n1 n2 i1 i2 n3 n4 ; AG1 n1 n2 i1 i2 n3 n4 ; AG2 n1 n2 i1 i2 n3 n4 ; Ex1 n1 n2 i1 i2 n3 n4 ; Ex2 n1 n2 i1 i2 n3 n4 ; ; ; Testing has shown that the Fast interrupt ... ; ; ; Note: A fast interrupt is not interruptable. ; Sadly, the ITD using Timer 2 is not using the Fast Interrput Mode. ; I am thinking of fixing that. ; ; ; ; TIMING RESOLUTION ; INTERRUPT TO ENCODER START ; ========================== ; ; From interrupt to starting the encoder: ; ; -~~~~~~~~~~~~~~~~|-|-|-|-|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|-|-|-... ; I A B C | I A B ; | ; S ; I: interrupt occurs ; I to A: current instruction completes: gg to pp, ; A to B: loading two words in the pipeline, ; B to C: Executing the instruction, writing to the Encoder, ; C to S: Encoder response time. ; ; ; -----------------|-|-|-|-|--------------------------------------- ; | ; ; Do not make the itd > tp (period?) of the other channel. ; ; one channel begins till the itd of the other: ; ; ; TIMING RESOLUTION ; SIMULTANEOUS PULSE TRAIN INTERRUPTS ; =================================== ; ; Interrupt to starting the encoder as above: ; ; Right ~~~~~~~~~~~|-|-|-|-|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|-|-|-... ; I A B C | I A B ; | ; S ; ; Left ~~~~~~~~~~~|-|-|-|-|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|-|-|-... ; I A B C | I A B ; | ; S ; As Timer 0 has priority over Timer 1, simultaneous events will put ; the Right side one cycle ahead of the Left. ; ; ; ; ; TIMING RESOLUTION ; SIMULTANEOUS INTERRUPTS WITH ITD ; ================================ ; Interrupt to starting the encoder as above: ; ; Right or Left ~~~|-|-|-|-|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|-|-|-... ; I A B C | I A B ; | ; S ; Interrupt to starting the timer following initial ITD: ; ; Timer 2, ITD ~~~~|-|-|-|-|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|-|-|-... ; I A B C | I A B ; | ; S ; As Timers 0 and 1 have priority over Timer 2, simultaneous events or ; receiving a timer 0 or 1 interrupt while the Timer 2 ISR is running ; will cause ; ; ; ; ;------------------------------------------------------------------------------; ; ... TO HERE AND WILL BE REWRITTEN LATER. ;------------------------------------------------------------------------------; SpearLib IDENT 1,3 cobj '7/10/02 SpearLib' OPT FC ;fold comments OPT MU ;memory utilization listing OPT RC ;float comments ;***********************************************; ;------------------------------- ; ; Include files for DSP56302/DSP56309 ; ; include 'equ_io.a56' ; include 'intequ.a56' ; ; ;------------------------------- ; ; Spear Rev3 specfic Equates ; ; include 'Spear3eq.a56' ; ; ;-----------------------------------------------; ; ;***********************************************; ; ; ; User Program Equates & Macros ; ; ; ; Flags for: RELOAD equ 0 ; RELOADing data and starting the pulse trains. INTFLAG equ 0 ; bit for noting that an interrupt occured DONE equ 0 ; bit for noting that a given side is done. FIRSTTIME equ 1 ; noting that a given side has done the reload portion STOPNOW equ 2 ; noting that a given side shuld stop immediately. TERMINALCOUNT equ $F42400 ; This, F42400, is 16,000,000. ; ; ;------------------------------- ; ; DSP Core Frequency ; CF14_7M equ $0E0000 ; fc=14.7 MHz (Codec Sampling Freq = 14.4Kz/ch) ; ;------------------------------- ; ; ESSI equates for CODEC Initialisation ; ESSI_SCI0 equ $101801 ; fc=14.7MHz. fs = 28800Hz (14400Hz per channel) ESSI_SCI1 equ $10180F ; ; ;------------------------------- ; ; Wait States for off-chip access ; WAITSTATES equ $014861 ; fc=14.7MHz, default = 1, and ; AAR0=1, AAR1=3, AAR2=2, AAR3=2 ; ;------------------------------- ; ; Implant Type for Left and Right Side ; ; ; ; Set to 1 for CI24, or 0 for CI22 ; ; ; CI24L equ 1 ; Left Implant = CI24 CI24R equ 1 ; Right Implant = CI24 ; ;--------------------------------------- ; ; Delay macro in 50us steps ; ; make sure to adjust FIFTY_MS_COUNT ; ; if DSP Core freq is modified ; ; FIFTY_MS_COUNT equ 733 ; Fifty Micro Sec Count for 14.7MHz ; delay50us macro time ; do #time,_delay50us ; rep #FIFTY_MS_COUNT ; nop ; _delay50us ; nop ; endm ; ; ;-----------------------------------------------; ; ;***********************************************; ; ; ; Assemble time switches ; ; ; ;------------------------------- ; ; Enable CODEC output ; ; Set to 1 to Enable switch, 0 to disable ; ; ENABLEDAC equ 0 ; Codec Output Disabled ; ;-----------------------------------------------; ; ;***********************************************; ; ; ; MEMORY ALLOCATIOM ; ; ; ;***********************************************; ; ; ; Y: RAM ALLOCATION ; ; ; org y:0 ; Y_RELOAD dc 0 ; Reload flag location. Y_FLAGS dc 0 ; address of flag to note Y/Left side done, firsttime through, stop. Y_STptr dc 2000 ; Pointer to Y/Left Stimuli Table Y_ATTEN dc 9999 ; Y_SUBTRACT dc 9999 ; ; ; Sets Active El, Ref El, Amplitude of pulse N ; Y_CI24beginActive ; Encoder Table for CI24, Left Y_ActE dc 20 ; Active electrode (electrode #20) Y_RefE dc 30 ; Reference elec (MP1+2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (45us) Y_AmpAE dc 0 ; Amplitude (0) dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) ; This second section Sets Phase and IPG of pulse N (sets Active, Ref, Amp of N+1) dc 24 ; Active electrode (MP1) dc 25 ; Reference elec (MP2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (8 us) dc 0 ; Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) dc $80 ; Encoder Halt bit, loaded in Active Elec location Y_CI24endActive ; ; Y_CI24beginNull ; Power pulses only, Left. dc 20 ; Active electrode (electrode #20) dc 30 ; Reference elec (MP1+2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (45 us) dc 0 ; Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) dc $80 ; Encoder Halt bit, loaded in Active Elec location Y_CI24endNull ; ;-----------------------------------------------; ; Save save locations for registers used ; ; during interrupt service rtns ; save_x0 ds 1 ; save_y0 ds 1 ; save_aa ds 1 ; save_bb ds 1 ; Y_ATTEN24BIT ds 1 ; Y_SUBTRACT24BIT ds 1 ; Y_reserved2 ds 1 ; Y_reserved3 ds 1 ; Y_reserved4 ds 1 ; Y_reserved5 ds 1 ; Y_reserved6 ds 1 ; Y_temp7 ds 1 ; ;-----------------------------------------------; ; This location is a pointer to the beginning ; ; of the table which the code will output. It ; ; begins set to Y_StimHead, but the user can ; ; rewrite this value to any location between ; ; Y_StimHead and the end of usable Y memory. ; Y_StimBegin ds 1 ; ;-----------------------------------------------; Y_StimHead ; Beginning of the Y/Left side Stimuli Table dc $14C81E ; Eword 0 |elect #A| AMP |elec #R | dc $011000 ; Tword 0 |TTNP (Time Till Next Pulse)| dc $14B81E ; Eword 1 ... dc $010000 ; Tword 1 ... dc $14A81E ; Eword 2 ... dc $00f000 ; Tword 2 dc $14981E ; Eword 3 ... dc $00e000 ; Tword 3 dc $14881E ; Eword 4 ... dc $00d000 ; Tword 4 dc $14781E ; Eword 5 ... dc $00c000 ; Tword 5 dc $14681E ; Eword 6 ... dc $00b000 ; Tword 6 dc $14581E ; Eword ... dc $00a000 ; Tword dc $14481E ; Eword ... dc $009000 ; Tword dc $14581E ; Eword ... dc $008000 ; Tword dc $14681E ; Eword ... dc $007000 ; Tword dc $14781E ; Eword ... dc $006000 ; Tword dc $14881E ; Eword ... dc $005000 ; Tword dc $14981E ; Eword ... dc $004000 ; Tword dc $14A81E ; Eword ... dc $003000 ; Tword dc $14B81E ; Eword ... dc $002000 ; Tword dc $14C81E ; Eword ... dc $001000 ; Tword ; dc $ffffff ; Eword N |electrode = FF means end of table. dc $000000 ; Tword N unused really ;***********************************************; ; ; ;***********************************************; ; ; ; X: RAM ALLOCATION ; ; ; org x:0 ; X_BOTHDONE dc 0 ; Only have to load the encoders with the null table once per train. X_FLAGS dc 0 ; address of flag to note R/Right side done, firsttime through, stop. X_STptr dc 2000 ; Pointer to X/Right Stimuli Table X_ATTEN dc 0 ; address of flag for noting first time through following reload X_SUBTRACT dc 0 ; ; ; Sets Active El, Ref El, Amplitude of pulse N ; X_CI24beginActive ; Encoder Table for CI24, Right X_ActE dc 20 ; Active electrode (electrode #20) X_RefE dc 30 ; Reference elec (MP1+2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (45us) X_AmpAE dc 0 ; Amplitude (0) dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) ; This second section Sets Phase and IPG of pulse N (sets Active, Ref, Amp of N+1) dc 24 ; Active electrode (MP1) dc 25 ; Reference elec (MP2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (8 us) dc 0 ; Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) dc $80 ; Encoder Halt bit, loaded in Active Elec location X_CI24endActive ; ; X_CI24beginNull ; Power pulses only, Right. dc 20 ; Active electrode (electrode #20) dc 30 ; Reference elec (MP1+2) dc 0,0 ; Phase 1 duration (25 us) dc 188,0 ; Inter-phase gap (45 us) dc 0 ; Amplitude (0) -- THIS SHOULD ALWAYS BE ZERO dc 0,0 ; Phase 2 duration (25 us) dc 5,0 ; Inter-frame gap (8 us) dc $80 ; Encoder Halt bit, loaded in Active Elec location X_CI24endNull ; ;-----------------------------------------------; temp_x0 ds 1 ; temp_x1 ds 1 ; temp_y1 ds 1 ; ;-----------------------------------------------; ; Front Panel Knob parameters ; ; External Jack Input State ; ExtInputState dc 0 ; Bit0 set for ring shorted to gnd ; Bit0 cleared for ring open ;-----------------------------------------------; ; X_ATTEN24BIT ds 1 ; X_SUBTRACT24BIT ds 1 ; X_reserved2 ds 1 ; X_reserved3 ds 1 ; X_reserved4 ds 1 ; X_reserved5 ds 1 ; X_reserved6 ds 1 ; X_temp7 ds 1 ; ;-----------------------------------------------; ; This location is a pointer to the beginning ; ; of the table which the code will output. It ; ; begins set to X_StimHead, but the user can ; ; rewrite this value to any location between ; ; X_StimHead and the end of usable X memory. ; X_StimBegin ds 1 ; ;-----------------------------------------------; X_StimHead ; Beginning of the X/Right side Stimuli Table dc $14C81E ; Eword 0 |elect #A| AMP |elec #R | dc $009000 ; Tword 0 |TTNP (Time Till Next Pulse)| dc $14C81E ; Eword 1 ... dc $009000 ; Tword 1 ... dc $14C81E ; Eword 2 ... dc $009000 ; Tword 2 dc $14C81E ; Eword 3 dc $009000 ; Tword 3 dc $14C81E ; Eword 4 dc $009000 ; Tword 4 dc $14C81E ; Eword 5 dc $009000 ; Tword 5 dc $14C81E ; Eword 6 dc $009000 ; Tword 6 ; dc $ffffff ; Eword N dc $000000 ; Tword N unused really ;***********************************************; ; ;***********************************************; ; ; ; P: RAM ALLOCATION ; ; ;------------------------------- ; ; Xilinx configuration data, including headers ; ; and dummy bits. ; ; org p:$1000 ; ; ;------------------------------- ; XILDATACI24 ; include 'Ci3p.a56' ; XILDATACI24END ; dc $FFFFFF ; ; XILDATACI22 ; include 'Ci1p.a56' ; XILDATACI22END ; dc $FFFFFF ; ; ;------------------------------- ; ; CI22 Encoder Initialisation Data ; include 'Ci22init.a56' ; ; ;-----------------------------------------------; ; ; ;***********************************************; ; ; ; Hardware reset vector ; ; ; ;***********************************************; ; org p:0 ; RESET Vector jmp Initialise_system ; ; org p:I_STACK ; nop ; nop ; ; org p:I_ILL ; nop ; nop ; ; org p:I_DBG ; nop ; nop ; ; org p:I_TRAP ; nop ; nop ; ; org p:I_NMI ; nop ; nop ; ; org p:I_TIM0C ; Timer0 compare interrupt vector address move b,x0 a,y:ENCSTRT24R ; Start Encoder and a ; note that this interrupt happened. org p:I_TIM0OF ; Timer0 overflow interrupt vector address move b,x0 a,y:ENCSTRT24R ; Start Encoder and a ; note that this interrupt happened. ; org p:I_TIM1C ; Timer1 compare interrupt vector address move b,x1 a,y:ENCSTRT24L ; Start Encoder and a ; note that this interrupt happened. org p:I_TIM1OF ; Timer1 overflow interrupt vector address move b,x1 a,y:ENCSTRT24L ; Start Encoder and a ; note that this interrupt happened. ; org p:I_TIM2C ; Timer2 compare interrupt vector address jsr Timer2IntDelay ; ; org p:I_TIM2OF ; Timer2 overflow interrupt vector address jsr Timer2IntDelay ; ; org p:I_SI1RD ; jsr CodecIntt ; SC1/Codec interrupt vector ; org p:I_SI1RDE ; jsr CodecIntt ; SC1/Codec interrupt vector ; org p:I_SI1RLS ; jsr CodecIntt ; SC1/Codec interrupt vector ; ;-----------------------------------------------; ; ;***********************************************; ; Program Identification Name ; ; Null terminated ascii string (12 characeters) ; ; org p:$100 ; ; P:$100-$103 reserved for ProgramName ; ; ProgramName dcb 'exp-0020',0,0,0,0 ; 4 words (12 char) long ; ;-----------------------------------------------; ; ;***********************************************; ; ; ; Interrupt Service Routines ; ; ; ;-----------------------------------------------; org p:$104 ; ; ; Codec Recieve/Transmitt int service routine ; ; CodecIntt ; move a,y:save_aa ; Save regs move x0,y:save_x0 ; move y0,y:save_y0 ; ; movep x:M_RX1,x0 ; Get Codec Input if ENABLEDAC ; movep x0,x:M_TX1 ; send sample back to CODEC endif ; ; jclr #M_RFS,x:M_SSISR1,Right_Side ; use Frame Sync to determine which channel present ; ;Left_Side ; ; x0 = left input ; move y:save_x0,x0 ; Restore regs move y:save_y0,y0 ; move y:save_aa,a ; rti ; ; Right_Side ; ; x0 = right input ; move y:save_x0,x0 ; Restore regs move y:save_y0,y0 ; move y:save_aa,a ; rti ; ; ;-------------------------------- ; Timer2IntDelay ; bclr #M_TE,x:M_TCSR2 ; Disable timer2, but this is not move #$FFFF,r4 ; used in this code anyway. rti ; ;-----------------------------------------------; ; ; ;***********************************************; ; ; ; Main entry point ; ; ; ;***********************************************; ; Initialise_system ; ; ;------------------------------- ; ; Reset Mode regsiters ; move #-1,m0 ; linear move #-1,m1 ; move #-1,m2 ; move #-1,m3 ; move #-1,m4 ; move #-1,m5 ; move #-1,m6 ; move #-1,m7 ; ; ;-----------------------------------------------; ; Initialise DSP Core Frequency ; ; This is the PLL control register, PCTL, shown ; ; with the value 0E0000: ; ; +--------------------------------------------------+ ; |23 22 21 20 19 18 17 16 15 14 13 12 | ; +--------------------------------------------------+ ; |PD3|PD2|PD1|PD0|COD|PEN|PSTP|XTLD|XTLR|DF2|DF1|DF0| ; +--------------------------------------------------+ ; 0 0 0 0 1 1 1 0 0 0 0 0 PD3-0 all zero means a predivide factor of 1. ; +--------------------------------------------------+ ; | 11 10 9 8 7 6 5 4 3 2 1 0 | ; +--------------------------------------------------+ ; |MF11|MF10|MF9|MF8|MF7|MF6|MF5|MF4|MF3|MF2|MF1|MF0 | ; +--------------------------------------------------+ ; 0 0 0 0 0 0 0 0 0 0 0 0 All zeros means a multiplication factor of 1. ;-----------------------------------------------; movep #CF14_7M,x:M_PCTL ; Set DSP Core Freq, 14.7MHz ; Not required as ShaLo sets Fc=14.7MHz ; delay50us 1000 ; Delay for 50ms while PLL settles ; ;------------------------------- ; ; Set wait states in ext. mem, PEROM, etc ; movep #WAITSTATES,x:M_BCR ; ; ;------------------------------- ; ; Setup ESSI0 and ESSI1 (SC0, SC1) for CODEC ; ; sampling frequency ; movep #ESSI_SCI0,x:M_CRA0 ; Serial Clock movep #ESSI_SCI1,x:M_CRA1 ; Data Clock ; ;------------------------------- ; ;Flash Bottom LED 3 times to indicate user ; ;program running in Spear Rev3 ; do #6,end_flash_led ; flash the led 3 times bchg #LEDBIT,x:M_HDR ; nop ; delay50us 2000 ; 100ms delay nop ; end_flash_led ; bset #LEDBIT,x:M_HDR ; ; ; ; ; Put in initial values. ; ;-----------------------------------------------; ; bclr #RELOAD,y:Y_RELOAD ; Clear so as to not pop into doRELOAD on startup. bset #DONE,x:X_FLAGS ; Set 1 to be done. bset #DONE,y:Y_FLAGS ; Set 1 to be done. bclr #DONE,x:X_BOTHDONE ; Set 1 to be done. ; ;-----------------------------------------------; ; X_STptr and Y_STptr are pointers to the stimulus tables. These are the pointers used at run time. ; In order to allow the host SW to select sections of the table to be output, effectively treating ; the tables as multiple smaller tables, the code must read the values of X_STptr and Y_STptr ; from host SW programmable locations. The locations X_StimBegin and Y_StimBegin are where ; X_STptr and Y_STptr are loaded from. The code initializes X_STptr and Y_STptr to X_StimHead ; and Y_StimHead. ; move #>X_StimHead,a ; Initially begin at the head of the table. nop ; move a,x:X_StimBegin ; nop ; move a,x:X_STptr ; nop ; move #>Y_StimHead,a ; Initially begin at the head of the table. nop ; move a,y:Y_StimBegin ; nop ; move a,y:Y_STptr ; ;-----------------------------------------------; move #0,x0 ; move #0,x1 ; move #0,r2 ; move #0,r4 ; ;-----------------------------------------------; ; Code for attenuator and subtractor ; ; NOTE on the values. The values below correspond to the user view, not the ALU view. These will ; be converted in the right and left initialization blocks (;iR; and ;iL;) to the ALU's format ; of fractional data representation. Notes (from section 3.3 of the DSP56300 Family Manuali): ; The 56300 only knows one kind of data in the Data ALU ; ; ; ; ;-----------------------------------------------; move #0,a ; nop ; move a,x:X_SUBTRACT ; Begin subtracting Zero by default move a,y:Y_SUBTRACT ; move a,x:X_SUBTRACT24BIT ; Begin subtracting Zero by default move a,y:Y_SUBTRACT24BIT ; move #255,a ; nop ; move a,x:X_ATTEN ; Begin with a multiplication factor of 1 by default move a,y:Y_ATTEN ; move a,x:X_ATTEN24BIT ; Begin with a multiplication factor of 1 by default move a,y:Y_ATTEN24BIT ; ;-----------------------------------------------; ; TIMER MODEL TIMER MODEL TIMER MODEL TIMER MODEL TIMER MODEL TIMER MODEL ; ; Two registers COMMON to all three timers: ; M_TPLR EQU $FFFF83 ; TIMER Prescaler Load Register ; +-----------------------------------------------------------------------------+ ; |0|PS1|PS0| PL20-PL0 | ; +-+---+---+-------------------------------------------------------------------+ ; Prescaler This is the value loaded into the prescaler. ; Clock ; Source ; | 0 | 0 | Internal CLK/2. Power up default. ; | 0 | 1 | TIO0 ; | 1 | 0 | TIO1 ; | 1 | 1 | TIO2 ; ; M_TPCR EQU $FFFF82 ; TIMER Prescalar Count Register ; READ ONLY ; +-----------------------------------------------------------------------------+ ; |0|0|0| PC20-PC0 | ; +-----------------------------------------------------------------------------+ ; ; TIMERx: ; M_TCSRx EQU -------- ; TIMERx Control/Status Register ; 002a04 is: ; __________ _______ _________ ___________ _____________ _____________ ; 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 ; | | | | | | | | | | | | | | | | | | | | | | | | ; +-----------------------------------------------------------------------------+ ; |0|0|TCF|TOF|0|0|0|0||PCE|0|DO|DI|DIR|0|TRM|INV|TC3|TC2|TC1|TC0|0|TCIE|TOIE|TE| ; +-----------------------------------------------------------------------------+ ; | | | | | | | | | | | | | | | | ; | | | | | | | | | | | | | | | +-Timer Enable ; | | | | | | | | | | | | | | | Clear Timer count and ; | | | | | | | | | | | | | | | count accd to TC[3:0] ; | | | | | | | | | | | | | | +-Timer Overflow Interrupt Enable ; | | | | | | | | | | | | | +-Timer Compare Interrupt Enable. ; | | | | | | | | | | | | | If TCPR=N and TLR=M, generate an ; | | | | | | | | | | | | | interrupt after N-M+1 counts. ; | | | | | | | | | | | | | ; | | | | | | | | +---+---+---+---+-Timer Control Bits ; | | | | | | | | | 0 | 0 | 0 | 0 +-mode0. Timer and GPIO, internal clock ; | | | | | | | | | 0 | 0 | 0 | 1 +-mode1. Timer pulse, internal clock ; | | | | | | | | | - | - | - | - +-mode-. remainder not shown ; | | | | | | | | ~~~~~~~~~~~~~~~~~ ; | | | | | | | | ; | | | | | | | +-Input polarity on TIO input. See DIR. ; | | | | | | +-Timer Reload Mode. In mode0-3 et al, the counter is preloaded ; | | | | | | with TLR after TE set and first clock. If TRM, automatically ; | | | | | | If TRM set, auto reload counter when TCR==TCPR, else freerun. ; | | | | | +-O/I ; | | | | +-TIO ; | | | +-TIO ; | | +-1/0, Prescaler/(CLK/2 or external TIO) ; | +-Timer Overflow Flag ; +-Timer Compare Flag ; ; M_TLRx EQU -------- ; TIMERx Load Reg ; +-----------------------------------------------------------------------------+ ; | TLR23-TLR0 | ; +-----------------------------------------------------------------------------+ ; ; M_TCPRx EQU -------- ; TIMERx Compare Register ; +-----------------------------------------------------------------------------+ ; | TCPR23-TCPR0 | ; +-----------------------------------------------------------------------------+ ; ; M_TCRx EQU -------- ; TIMERx Count Register ; +-----------------------------------------------------------------------------+ ; | TCR23-TCR0 | ; +-----------------------------------------------------------------------------+ ; ; The CPU clock rate is 14.7MHz, or 0.06803 usec period. ; CLK/2 is 7.35MHz, or 0.13605 usec period. ; 200 Hz is a divide down of 36750, and ; 201 Hz is a divide down of 36567.16. Going for 36567 gives 201.00090, so that ; ought to do. ; ; If TCPR=N and TLR=M, generate an interrupt after N-M+1 counts. ; Count up from M to N. ; ; 7.35e6/36750 = 200Hz. 36750 is 0x08F8E. ; 7.35e6/36567 = 201Hz. 36567 is 0x08ED7. ; ; Stoph argues proper time is 0.13563368 per tick ; 250 Hz is 29491 is 0x07333 ; ; 100 Hz is 73728 is 0x12000 ;-----------------------------------------------; ; ; #$249F0 is 150,000 base 10. Subtract desired TTNP from this ;------------------------------- ; to get the Tword value. ; Initialise Timer0, Right Side ; provide a periodic interrupt movep #$000A04,x:M_TCSR0 ; disable timer0, enable compare intt movep #0,x:M_TLR0 ; reset timer0 reload register movep #$249F0,x:M_TCPR0 ; this is driven by a clk/2 prescaler ; movep #TERMINALCOUNT,x:M_TCPR0 ; this is driven by a clk/2 prescaler ; ; Initialise Timer1, Left side ; provide a periodic interrupt movep #$000A04,x:M_TCSR1 ; disable timer1, enable compare intt movep #0,x:M_TLR1 ; reset timer1 reload register movep #$249F0,x:M_TCPR1 ; this is driven by a clk/2 prescaler ; movep #TERMINALCOUNT,x:M_TCPR1 ; this is driven by a clk/2 prescaler ; movep #$000A04,x:M_TCSR2 ; disable timer2, enable compare intt ;------------------------------- ; ; Xilinx Configuration ; ; ; Configure Xilinx with Encoder configurations ; jsr ConfigureEncoders ; ; ;------------------------------- ; ; Setup AAR registers for Xilinx Chip Enable ; ; (via A14) ; movep #$100431,x:M_AAR2 ; map Left Xilinx Encoder from addr X or Y:$100000 upwards movep #$200431,x:M_AAR3 ; map Right Xilinx Encoder from addr X or Y:$200000 upwards ; ;------------------------------- ; ; Set Digital Pot Gain to minimum (-24dB), ; ; 64 Gain steps available, maximum 0dB ; ; do #80,DPotLoop ; 80 loops to be sure min gain set jsr DPotDown ; DPotLoop ; ; ;------------------------------- ; ; Initialise Encoder RAM for test stimulation ; jsr InitEncoderL ; jsr InitEncoderR ; move #0,x0 ; move #0,x1 ; ; ;------------------------------- ; ; Set Interrupt Priority, ; ; this will Unmask interrupts ; ; Interrupt Priority Levels bits in the ; ;Interrupt Priority Register P(eripheral). ; ; IPL1 IPL0 ; ; 0 0 disabled ; ; 0 1 IPL = 1 ; ; 1 0 IPL = 2 ; ; 1 1 IPL = 3 ; ; ; ; +----------------------------------------------------------+ ; | 23-10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ; |reserved|T0L1|T0L0|SCL1|SCL0|S1L1|S1L0|S0L1|S0L0|HPL1|HPL0| ; +----------------------------------------------------------+ movep #>$0001E0,x:M_IPRP ; set IPL's (SCI=2,ESSI1=1,Timer=0,ESSIO=NE,HOST=NE) ; ;------------------------------- ; jsr LedOff ; Turn Bottom LED off ;-----------------------------------------------; ; ;***********************************************; ; ; ; MAIN Main main ; ; ; ;***********************************************; ; Go around MainLoop forever .... ; ; ; ; ; ; The code in the MainLoop is about interrupt ; ; enable management. Functionally, actions ; ; depend on interrupts from timers previously ; ; set up, and set up with TTNP values from the ; ; Stimuli Tables ; ; ; ; MainLoop checks the RELOAD bit each loop. ; ; This code can only clear the RELOAD bit. ; ; The process from RELOAD set is ... ; ; Load X/Right Eword. If Active electrode == $ff, disable Right. ; Load Y/Left Eword. If Active electrode == $ff, disable Left. ; While either side is not done, load the TTNP ; ; into the timer interrupt count reg and start ; ; the timer, "prefetch" the next TTNP and wait. ; ; ; ; When both sides make thier final pass and are ; ; DONE, the code waits for the next RELAOD. ; ; ; ; ; Actually, I'm going to draw anothr picture. This code has the same control structure as the ; sprcbuxx code and since I wrote it and had to go back and decipher it by drawing out the ; flow chart I lost, I'll not lose it again. Here it is. I've put the labels on the far left ; to match the actual labels in the code: ; ; ; _______________________________________ ; / \ ; | | ; v | ; MainLoop Time to RELOAD? | ; | \ | ; N Y | ; | \ ^ ; | \ | ; | +--------------------------------+ | ; | | reinit, rewite, reload | | ; | +--------------------------------+ | ; | / | ; | / | ; | / | ; | / | ; NoReload Is the Right side done? | ; | \ ^ ; Y N | ; | \ | ; | \ | ; | +--------------------------------+ | ; | | Do right side stuff, like check| | ; | | if a Right side int happened | | ; | +--------------------------------+ | ; | / | ; | / | ; | / | ; | / | ; go_Left Is the Left side done? | ; | \ | ; Y N ^ ; | \ | ; | \ | ; | +--------------------------------+ | ; | | Do left side stuff, like check | | ; | | if a Left side int happened | | ; | +--------------------------------+ | ; | / | ; | / | ; | / | ; | / | ; go_on Is the Right side done? | ; | \ | ; N Y | ; | \ | ; | Is the Left side done? | ; | | \ ^ ; | N Y | ; | | \ | ; | | \ | ; | | +----------------------------+ | ; | | | Produce some Null Pulses | | ; | | +----------------------------+ | ; | | | | ; | | | | ; | | | | ; | | | | ; here \_____\_____\________________________________/ ; ; ;-----------------------------------------------; bclr #LEDBIT,x:M_HDR ; Turn on the LED as a "done" indicator bclr #M_TE,x:M_TCSR2 ; Disable timer2 interrupt because it is not used. bset #FIRSTTIME,x:X_FLAGS ; just to know the values bset #FIRSTTIME,y:Y_FLAGS ; just to know the values bset #E3RESETBitLeft,x:M_HDR ; Release Reset Left encoder bset #E3RESETBitRight,x:M_HDR ; Release Reset Right encoder jsr EncBusyLeft ; Wait while Left encoder is busy jsr LoadEncLeftNull ; Load Stimulus Params into Left encoder jsr EncBusyRight ; Wait while Left encoder is busy jsr LoadEncRightNull ; Load Stimulus Params into Left encoder MainLoop ; jclr #RELOAD,y:Y_RELOAD,NoRELOAD ;i; If !clr, the SW from above has written to the RELOAD bit. ;i; This will cause a reload of timer count values, a reload ;i; of the xilinx encoders, etc, bclr #RELOAD,y:Y_RELOAD ;i; Do this code once per RELOAD, so clear this flag right away. bset #LEDBIT,x:M_HDR ;i; Turn LED off. bclr #DONE,x:X_BOTHDONE ;i; Set internal flag to note done. ;i; bclr #STOPNOW,x:X_FLAGS ;i; Set 0, neither side done, as it is just starting (over). bclr #STOPNOW,y:Y_FLAGS ;i; bset #FIRSTTIME,x:X_FLAGS ;i; bset #FIRSTTIME,y:Y_FLAGS ;i; ;i; bclr #DONE,x:X_FLAGS ;i; Set 0, neither side done, as it is just starting (over). bclr #DONE,y:Y_FLAGS ;i; ;i; move #$FFFFFF,b ;i; Initialize flags. ;-------------------------; ;i; ; NOTE: Don't touch acc B ; ;i; ; E V E R ; ;i; ;-------------------------; ;i; move x:X_StimBegin,a ;i; Begin at whatever address was loaded in X_StimBegin. nop ;i; move a,x:X_STptr ;i; nop ;i; move y:Y_StimBegin,a ;i; Begin at whatever address was loaded in Y_StimBegin. nop ;i; move a,y:Y_STptr ;i; move #0,x0 ;i; flag for noting a right side, Timer0, interrupt occured. move #0,x1 ;i; flag for noting a left side, Timer1, interrupt occured. ;-----------------------------------------------;i; ;i; This section is for the first prefetch from the Stimuli ;i; tables. Load the TTNP so that the next value it loaded ;i; automatically after the timer counts down, and load the ;i; Encoder Table data so that there is no delay getting it ;i; in place when it is needed. ;i; The X-prefetch sequence ... ;i; ; setup for the X/right side following RELOAD. ;iR; move x:X_STptr,r1 ;iR; get XEWord: contents of X_STptr to r1, nop ;iR; nop ;iR; nop ;iR; nop ;iR; move x:(r1),a ;iR; and what is pointer to into acc a nop ;iR; jclr #23,a,xInitloadET ;iR; see if Active electrode is 1xxxxxxx (invalid) bset #DONE,x:X_FLAGS ;iR; then this side won't even start. jmp >dont_even_startX ;iR; xInitloadET ;iR; move a,x:X_temp7 ;iR; just to hold Eword temporarly nop ;iR; lsr #16,a ;iR; Shift the upper byte to the bottom nop ;iR; (lsr left fills with zeros). move a,x:X_ActE ;iR; Store it as the Active electrode in the ETable ;----------- ;iR; move x:X_temp7,a ;iR; Get back the Eword nop ;iR; lsr #8,a ;iR; Shift the middle byte to the bottom. nop ;iR; and #$0000FF,a ;iR; Mask off middle (and upper, while I'm at it). nop ;iR; ;-----------------------------------------------;iR; ; Code for attenuator and subtractor ;iR; ;-----------------------------------------------;iR; move a,x:X_AmpAE ;iR; Store it as the Active electrode amplitude in the ETable ;iR; ;----------- ;iR; move x:X_temp7,a ;iR; Get back the Eword nop ;iR; and #$0000FF,a ;iR; Mask off middle and upper bytes nop ;iR; move a,x:X_RefE ;iR; Store it as the reference electrode. ;----------- ;iR; move r1,a ;iR; time to increment to TTNP nop ;iR; add #1,a ;iR; acc apoints to TTNP nop ;iR; move a,r1 ;iR; now r1 points to TTNP nop ;iR; add #1,a ;iR; now a points to next Eword, and should be stored away nop ;iR; move a,x:X_STptr ;iR; nop ;iR; move x:(r1),a ;iR; Get the TTNP movep a,x:M_TLR0 ;iR; and into timer (but don't start it yet) jsr EncBusyRight ;iR; Wait while Right encoder is busy jsr LoadEncRightActive ;iR; Load Stimulus Params into Right encoder (but don't start it yet) ;iR; Destroys contents of registers r0, r1, x1 bset #FIRSTTIME,x:X_FLAGS ;iR; note that this is the first time for this pulse train. dont_even_startX ;iR; ;-----------------------------------------------;i; ; setup for the Y/left side following RELOAD. ;iL; move y:Y_STptr,r1 ;iL; get YEWord: contents of Y_STptr to r1, nop ;iL; nop ;iL; nop ;iL; nop ;iL; move y:(r1),a ;iL; and what is pointer to into acc a nop ;iL; jclr #23,a,yInitloadET ;iL; see if Active electrode is 1xxxxxxx (invalid) bset #DONE,y:Y_FLAGS ;iL; then this side won't even start. jmp >dont_even_startY ;iL; yInitloadET ;iL; move a,y:Y_temp7 ;iL; just to hold Eword temporarly nop ;iL; lsr #16,a ;iL; Shift the upper byte to the bottom nop ;iL; (lsr left fills with zeros). move a,y:Y_ActE ;iL; Store it as the Active electrode in the ETable ;----------- ;iL; move y:Y_temp7,a ;iL; Get back the Eword nop ;iL; lsr #8,a ;iL; Shift the middle byte to the bottom. nop ;iL; and #$0000FF,a ;iL; Mask off middle (and upper, while I'm at it). nop ;iL; ;-----------------------------------------------;iL; ; Code for attenuator and subtractor ;iL; ;-----------------------------------------------;iL; move a,y:Y_AmpAE ;iL; Store it as the Active electrode amplitude in the ETable ;iL; ;----------- ;iL; move y:Y_temp7,a ;iL; Get back the Eword nop ;iL; and #$0000FF,a ;iL; Mask off middle and upper bytes nop ;iL; move a,y:Y_RefE ;iL; Store it as the reference electrode. ;----------- ;iL; move r1,a ;iL; time to increment to TTNP nop ;iL; add #1,a ;iL; acc apoints to TTNP nop ;iL; move a,r1 ;iL; now r1 points to TTNP nop ;iL; add #1,a ;iL; now a points to next Eword, and should be stored away nop ;iL; move a,y:Y_STptr ;iL; nop ;iL; move y:(r1),a ;iL; Get the TTNP movep a,x:M_TLR1 ;iL; and into timer (but don't start it yet) jsr EncBusyLeft ;iL; Wait while Right encoder is busy jsr LoadEncLeftActive ;iL; Load Stimulus Params into Left encoder (but don't start it yet) ;iL; Destroys contents of registers r0, r1, x1 bset #FIRSTTIME,y:Y_FLAGS ;iL; note that this is the first time for this pulse train. bset #M_TE,x:M_TCSR1 ;iL; enable timer1, Left side interrupt dont_even_startY ;iL; jset #DONE,x:X_FLAGS,NoRELOAD ;iR; and finally, if to be done at all, bset #M_TE,x:M_TCSR0 ;iR; enable timer0, Right side interrupt ;i; ;-----------------------------------------------; NoRELOAD ; ; jset #DONE,x:X_FLAGS,go_Left ; If set then Right side has detected a 0xFF as an electrode number jset #FIRSTTIME,x:X_FLAGS,fakeXint ; ; and now should be doing Null pulses. jclr #INTFLAG,x0,go_Left ; Has the ISR set the INTFLAG bit? move #0,x0 ; ISR has indeed set the INTFLAG bit. Clear Flag. jsr EncBusyRight ; Wait while Right encoder is busy jsr LoadEncRightActive ; Load Stimulus Params into Right encoder (but don't start it yet) jclr #STOPNOW,x:X_FLAGS,dont_stop_yetR ; ; shutdown Right side, stop interrupt timer, set done. bclr #M_TE,x:M_TCSR0 ; disable timer0, as a 0xFF electrode number is detected. bset #DONE,x:X_FLAGS ; jmp >go_Left ; dont_stop_yetR ; ; The X-prefetch sequence ... fakeXint ; bclr #FIRSTTIME,x:X_FLAGS ; ; setup for the X/right side. ; move x:X_STptr,r1 ; get XEWord: contents of X_STptr to r1, nop ; nop ; nop ; nop ; move x:(r1),a ; and what is pointer to into acc a nop ; jclr #23,a,xloadET ; see if Active electrode is 1xxxxxxx (invalid) bset #STOPNOW,x:X_FLAGS ; If this is the marker to stop, and because the values are preloaded, ; the timer is running now for the last valid pulse. So the timer cannot ; be stopped at this time. There must be a flag in place to note that the timer ; should be stopped and it should be checked when the interrput is detected. ; That flag is is called X/Y_STOPNOW jmp >dont_continue_preload_X ; xloadET ; move a,x:X_temp7 ; just to hold Eword temporarly ; nop ; lsr #16,a ; Shift the upper byte to the bottom nop ; (lsr left fills with zeros). move a,x:X_ActE ; Store it as the Active electrode in the ETable ;----------- ; move x:X_temp7,a ; Get back the Eword nop ; lsr #8,a ; Shift the middle byte to the bottom. nop ; and #$0000FF,a ; Mask off middle (and upper, while I'm at it). nop ; ;-----------------------------------------------; ; Code for attenuator and subtractor ; ;-----------------------------------------------; move a,y0 ; The amplitude from the table into y0, move x:X_ATTEN24BIT,y1 ; Operate on X_ATTEN24BIT and move x:X_SUBTRACT24BIT,a ; X_SUBTRACT24BIT, setup for MAC mac y0,y1,a ; bsge a_not_negative_X ; check for negative value move #0,a ; a_not_negative_X ; nop ; move y0,x:X_AmpAE ; Store it as the Active electrode amplitude in the ETable nop ; ;----------- ; move x:X_temp7,a ; Get back the Eword nop ; and #$0000FF,a ; Mask off middle and upper bytes nop ; move a,x:X_RefE ; Store it as the reference electrode. ;----------- ; move r1,a ; time to increment to TTNP nop ; add #1,a ; acc apoints to TTNP nop ; move a,r1 ; now r1 points to TTNP nop ; add #1,a ; now a points to next Eword, and should be stored away nop ; move a,x:X_STptr ; nop ; move x:(r1),a ; Get the TTNP movep a,x:M_TLR0 ; ; Destroys contents of registers r0, r1, x1 dont_continue_preload_X ; ;-----------------------------------------------; go_Left ; ; The Y-prefetch sequence ... jset #DONE,y:Y_FLAGS,go_on ; If set then Left side has detected a 0xFF as an electrode number jset #FIRSTTIME,y:Y_FLAGS,fakeYint ; ; and now should be doing Null pulses. jclr #INTFLAG,x1,go_on ; Has the ISR set the INTFLAG bit? move #0,x1 ; ISR has indeed set the INTFLAG bit. Clear Flag. jsr EncBusyLeft ; Wait while Left encoder is busy jsr LoadEncLeftActive ; Load Stimulus Params into Left encoder (but don't start it yet) jclr #STOPNOW,y:Y_FLAGS,dont_stop_yetL ; ; shutdown Left side, stop interrupt timer, set done. bclr #M_TE,x:M_TCSR1 ; disable timer1, as a 0xFF electrode number is detected. bset #DONE,y:Y_FLAGS ; jmp >go_on ; dont_stop_yetL ; ; The X-prefetch sequence ... fakeYint ; bclr #FIRSTTIME,y:Y_FLAGS ; ; setup for the Y/left side. ; move y:Y_STptr,r1 ; get YEWord: contents of Y_STptr to r1, nop ; nop ; nop ; nop ; move y:(r1),a ; and what is pointer to into acc a nop ; jclr #23,a,yloadET ; see if Active electrode is 1xxxxxxx (invalid) bset #STOPNOW,y:Y_FLAGS ; If this is the marker to stop, and because the values are preloaded, ; the timer is running now for the last valid pulse. So the timer cannot ; be stopped at this time. There must be a flag in place to note that the timer ; should be stopped and it should be checked when the interrput is detected. ; That flag is is called X/Y_STOPNOW jmp >dont_continue_preload_Y ; yloadET ; move a,y:Y_temp7 ; just to hold Eword temporarly ; nop ; lsr #16,a ; Shift the upper byte to the bottom nop ; (lsr left fills with zeros). move a,y:Y_ActE ; Store it as the Active electrode in the ETable ;----------- ; move y:Y_temp7,a ; Get back the Eword nop ; lsr #8,a ; Shift the middle byte to the bottom. nop ; and #$0000FF,a ; Mask off middle (and upper, while I'm at it). nop ; ;-----------------------------------------------; ; Code for attenuator and subtractor ; ;-----------------------------------------------; move a,y0 ; The amplitude from the table into y0, move y:Y_ATTEN24BIT,y1 ; Operate on X_ATTEN24BIT and move x:X_SUBTRACT24BIT,a ; X_SUBTRACT24BIT, setup for MAC mac y0,y1,a ; bsge a_not_negative_Y ; check for negative value move #0,a ; a_not_negative_Y ; nop ; move y0,y:Y_AmpAE ; Store it as the Active electrode amplitude in the ETable nop ; ;----------- ; move y:Y_temp7,a ; Get back the Eword nop ; and #$0000FF,a ; Mask off middle and upper bytes nop ; move a,y:Y_RefE ; Store it as the reference electrode. ;----------- ; move r1,a ; time to increment to TTNP nop ; add #1,a ; acc apoints to TTNP nop ; move a,r1 ; now r1 points to TTNP nop ; add #1,a ; now a points to next Eword, and should be stored away nop ; move a,y:Y_STptr ; nop ; move y:(r1),a ; Get the TTNP movep a,x:M_TLR1 ; ; Destroys contents of registers r0, r1, x1 dont_continue_preload_Y ; ;-----------------------------------------------; go_on ; jclr #DONE,x:X_FLAGS,here ; That is, jump if the X/R side is NOT done yet. jclr #DONE,y:Y_FLAGS,here ; That is, jump if the Y/L side is NOT done yet. jsr EncBusyLeft ; Right and Left are both done. jsr EncBusyRight ; jset #DONE,x:X_BOTHDONE,btdt ; Load the Null stuff, once. Changed by STOPH CJL jsr LoadEncRightNull ; Load Stimulus Params into Right encoder. Destroys contents of r0, r1, x1 jsr LoadEncLeftNull ; Load Stimulus Params into Left encoder. Destroys contents of r0, r1, x1 bclr #LEDBIT,x:M_HDR ; Turn on the LED as a "done" indicator bset #DONE,x:X_BOTHDONE ; btdt ; ; Here doing Null pulses move a,y:ENCSTRT24R ; Write any value to Start Encoder move a,y:ENCSTRT24L ; Write any value to Start Encoder here ; jmp >MainLoop ; ; ; ;***********************************************; ; ; ; ;***********************************************; ; SubRoutines ; ;***********************************************; ; Cheap Debug LED blinkers ; ; ; ;------------------------------- ; ; Led subroutine (Bottom LED M) ; ; Note,top LED controlled by ShaLo ; ; FlashFast ; bclr #LEDBIT,x:M_HDR ; delay50us 200 ; bset #LEDBIT,x:M_HDR ; delay50us 200 ; bclr #LEDBIT,x:M_HDR ; delay50us 200 ; bset #LEDBIT,x:M_HDR ; delay50us 200 ; bclr #LEDBIT,x:M_HDR ; delay50us 200 ; bset #LEDBIT,x:M_HDR ; delay50us 200 ; bclr #LEDBIT,x:M_HDR ; delay50us 200 ; bset #LEDBIT,x:M_HDR ; delay50us 200 ; bclr #LEDBIT,x:M_HDR ; delay50us 200 ; bset #LEDBIT,x:M_HDR ; delay50us 200 ; rts ; ; ;------------------------------- ; FlashSlow ; bclr #LEDBIT,x:M_HDR ; delay50us 2000 ; bset #LEDBIT,x:M_HDR ; delay50us 2000 ; bclr #LEDBIT,x:M_HDR ; delay50us 2000 ; bset #LEDBIT,x:M_HDR ; delay50us 2000 ; bclr #LEDBIT,x:M_HDR ; delay50us 2000 ; bset #LEDBIT,x:M_HDR ; delay50us 2000 ; rts ; ; ;------------------------------- ; ; Front Panel SubRoutines ; ; ; ; ;------------------------------- ; ; Led subroutine (Bottom LED M) ; ; Note,top LED controlled by ShaLo ; ; LedOn ; bclr #LEDBIT,x:M_HDR ; rts ; ; LedOff ; bset #LEDBIT,x:M_HDR ; rts ; ; ; ;------------------------------- ; ; Steps Digital Pot Gain Down by one step ; DPotDown ; bset #GAIN_UP,x:M_PDRC ; keep input digital POT step-up-gain pin high bclr #GAIN_DN,x:M_PDRC ; make input digital POT step downp in gain delay50us 20 ; 1 ms delay bset #GAIN_DN,x:M_PDRC ; delay50us 20 ; 1 ms delay rts ; ; ;-----------------------------------------------; ; ;***********************************************; ; Encoder Subroutines ; ; ; ; ;------------------------------- ; ConfigureEncoders ; ; movep #(E3CLK),x:M_HDR ; preset CLK as a 1, already done by ShaLo delay50us 800 ; 40ms delay ; movep #(E3CLK|E3PDOWNLeft|E3PDOWNRight),x:M_HDR ;preset CLK as a 1, already done by ShaLo ; ; Wait 40ms ; delay50us 800 ; Wait 40ms delay ; ; Reset Xilinx device ; ; ( /RESET line already set low above ) ; ; movep #(E3CLK|E3PDOWNLeft|E3PDOWNRight|E3RESETLeft|E3RESETRight),x:M_HDR ;release reset Xilinx ; delay50us 2 ; 0.1ms delay ; ; Send an initial reset pulse with P/Done low in; ; case the Xilinx is still in a confused powerup; ; initialisation state... ; ; Pulse reset low then high again for 6ms each ; ; while holding P/Done low ; ; ; ; First take P/Done line low to initiate Xilinx ; ; configuration mode ; movep #$7FFD,x:M_HDDR ; Make P/D an output, state=0. ; Wait 6ms ; delay50us 120 ; 6ms delay ; ; Now Reset both Xilinx ; movep #(E3CLK|E3PDOWNLeft|E3PDOWNRight),x:M_HDR ; reset both Xilinx ; Wait 6ms ; delay50us 120 ; 6ms delay ; ; Finally, release Xilinx Reset ; movep #(E3CLK|E3PDOWNLeft|E3PDOWNRight|E3RESETLeft|E3RESETRight),x:M_HDR ;release reset on Xilinx again ; Wait 6ms ; delay50us 120 ; 6ms delay ; ; Make sure P/Done is low ; TestPDone1 ; brclr #E3PDONEBit,x:<$FFF000,a ; Mask out Length field or #>$00000A,a ; Adjust Length field move a1,p:(r0)+ ; Overwrite 1st word with new Length move p:(r0),a ; Get 2nd word of header and #>$000FFF,a ; Mask out Length field or #>$D70000,a ; Adjust Length field move a1,p:(r0)+n0 ; Overwrite 2nd word with new Length ; Next adjust last data byte for second encoder ; move p:(r0),a ; Get Last word (which includes 1st data byte for 2nd encoder) and #>$FFFF00,a ; Mask out bits 9-23 of Last word move p:(r1),b ; Get 2nd word of header and #>$0000FF,b ; Mask 1st data byte add b,a ; Add 1st data byte to Last word nop ; move a1,p:(r0) ; Overwrite Last word move #$FFFFFF,b ; Keep acc b non-zero rts ; ; RestoreLastByte ; ; Restore last byte of config data to FF ; ; ; ; Input params: ; ; r0 = Addr of last word for 1st encoder ; move p:(r0),a ; or #>$0000FF,a ; move a1,p:(r0) ; rts ; ; ;-----------------------------------------------; ; Initialise Encoder State and Set ; ; Encoder RAM to test stimulation parameters ; ; ; ; Destroys contents of registers r0, r1, y1 ; ; ;------------------------------- ; InitEncoderL ; ; ; Reset the state of the Encoder, ; ; usally only required after configuring Xilinx ; ; unless low battery occurs ; clr a ; move #ENCBATT24L,r0 ; move #ENCSTOP24L,r1 ; move a,x:(r0) ; Clear low batt latch nop ; move a,x:(r1) ; Stop immediately ; ; Make sure Encoder is not busy ; move #ENCSTAT24L,r0 ; jsr WaitCI24 ; Wait for Encoder finished ; ; Load stimulus data into Encoder RAM ; move #ENCRAM24L,r0 ; Address of EncoderRAM jmp LoadParamsCI24NullL ; ; rts ; ; ;------------------------------- ; InitEncoderR ; ; ; Reset the state of the Encoder ; ; usally only required after configuring Xilinx ; ; unless low battery occurs ; ; Destroys contents of registers r0, r1 ; clr a ; move #ENCBATT24R,r0 ; move #ENCSTOP24R,r1 ; move a,x:(r0) ; Clear low batt latch nop ; move a,x:(r1) ; Stop immediately ; ; Make sure Encoder is not busy ; move #ENCSTAT24R,r0 ; jsr WaitCI24 ; Wait for Encoder finished ; ; Load stimulus data into Encoder ; move #ENCRAM24R,r0 ; Address of EncoderRAM jmp LoadParamsCI24NullR ; ; ; ;-----------------------------------------------; ; Copies Test Stimulus Params into Encoder RAM ; ; LoadParamsCI24ActiveR ; ; Copy X_CI24beginActive to Encoder Ram ; move #>X_CI24beginActive,r1 ; Default stim data to encRAM do #(X_CI24endActive-X_CI24beginActive),InitCI24ActiveR ; for table length move x:(r1)+,y1 ; Get X_CI24beginActive byte move y1,x:(r0)+ ; Load into Encoder Ram InitCI24ActiveR ; rts ; ; ;------------------------------- ; LoadParamsCI24NullR ; ; Copy X_CI24beginNullR to Encoder Ram ; move #>X_CI24beginNull,r1 ; Default stim data to encRAM do #(X_CI24endNull-X_CI24beginNull),InitCI24NullR ; for table length move x:(r1)+,y1 ; Get X_CI24beginNull byte move y1,x:(r0)+ ; Load into Encoder Ram InitCI24NullR ; rts ; ; ;------------------------------- ; ; LoadParamsCI24ActiveL ; ; Copy Y_CI24beginActive to Encoder Ram ; move #>Y_CI24beginActive,r1 ; Default stim data to encRAM do #(Y_CI24endActive-Y_CI24beginActive),InitCI24ActiveL ; for table length move y:(r1)+,y1 ; Get Y_CI24beginActive byte move y1,y:(r0)+ ; Load into Encoder Ram InitCI24ActiveL ; rts ; ; ;------------------------------- ; LoadParamsCI24NullL ; ; Copy Y_CI24beginNull to Encoder Ram ; move #>Y_CI24beginNull,r1 ; Default stim data to encRAM do #(Y_CI24endNull-Y_CI24beginNull),InitCI24NullL ; for table length move y:(r1)+,y1 ; Get Y_CI24beginNull byte move y1,y:(r0)+ ; Load into Encoder Ram InitCI24NullL ; rts ; ; ;-----------------------------------------------; ; ; ;-----------------------------------------------; ; Loads Test Stimulus Params into Encoder ; ; ; ; Notes. ; ; 1. The stimulus parameters can be modified ; ; during run time but in this example they ; ; are static. ; ; 2. The stimulus parameters do not need to be ; ; written to every stimulation period if ; ; they are unchanged. ; ; 3. Not all stimulus parameters need to be ; ; written to, only those that need to be ; ; modified. EG if phase duration, IPG and ; ; IFG are constant then they do not need to ; ; be written to. ; ; 4. The less params written to Encoder RAM ; ; the sooner the Encoder can be started and ; ; thus the higher the stimulation rate ; ; ;-----------------------------------------------; ; Load stimulus data into Encoders ; ; ; ; These destroy contents of r0 ; ; ; LoadEncLeftActive ; move #ENCRAM24L,r0 ; Address of CI24L EncoderRAM jmp LoadParamsCI24ActiveL ; rts ; ; ;------------------------------- ; LoadEncRightActive ; move #ENCRAM24R,r0 ; Address of CI24R EncoderRAM jmp LoadParamsCI24ActiveR ; rts ; ; ;------------------------------- ; LoadEncLeftNull ; move #ENCRAM24L,r0 ; Address of CI24L EncoderRAM jmp LoadParamsCI24NullL ; rts ; ; ;------------------------------- ; LoadEncRightNull ; move #ENCRAM24R,r0 ; Address of CI24R EncoderRAM jmp LoadParamsCI24NullR ; rts ; ; ;------------------------------- ; EncBusyLeft ; Wait While Left Encoder is busy move #ENCSTAT24L,r0 ; jsr WaitCI24 ; Wait for Encoder finished rts ; ; ;------------------------------- ; EncBusyRight ; Wait While Right Encoder is busy move #ENCSTAT24R,r0 ; jsr WaitCI24 ; Wait for Encoder finished rts ; ; ;-----------------------------------------------; WaitCI24 ; Waits for Encoder Finished status ; ; ; Requires r0 = Address of Right or Left ; ; Encoder Status Register ; ; ; enc_busy24 ; jclr #RUNBIT,x:(r0),enc_free24 ; check status and wait till done jmp enc_busy24 ; enc_free24 ; rts ; ; ;-----------------------------------------------; ; ;-----------------------------------------------; end ; ;-----------------------------------------------;