;*************************************************************************
; 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
;
;
;
;- Objectives
;
- Memory Map
;
- Driving SPRTBL
;
- 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 ;
;-----------------------------------------------;