MODULE P3028 TITLE 'P3028A' "NOTE: For all versions, make sure you have the global input constraint" "set to HOLD rather than PULLUP or PULLDOWN. Set the input constraint in the" "constraint editor, and when compiling this code, instruct the compiler to" "merge the constraints in this file (pin numbers) with the constraints from" "the constraint editor (input hold)." "Version 1 [19-SEP-13] Based upon P3019A02. We change pin numbers to suit the" "layout of the A3028. We modify the auxilliary channel code to support two main" "channels at the same sample rate, or one channel either X or Y." "Version 2 [31-DEC-13] Change the ring oscillator calibration so as to guarantee" "the mark-space ratio is 50%, sacrificing frequency resolition." "Version 3 [24-FEB-14] Correct the channel selection on the ADC so that X is the" "first channel ID and Y is the second channel ID." "Version 4 [21-MAR-14] Add conditional compilation clauses to support compiling" "for the new A302801C layout, which has some changes to the logic chip pinout." "We set CENTERED_BATTERY to 0 for the A302801A and A302801B layouts, and to 1 for" "the A302801C layout." "Version 5 [01-JAN-15] Add version-letter compilation codes to deal with higher" "sample rates as well as single and dual channel transmitters. We remove the" "off-center battery option as it is obsolete." "[08-JAN-15] We now clock the frequency register with a special VCK signal, which" "is delayed by two FCK periods from TCK. Previously, we allowed slightly less" "than one FCK period, which proved to be insufficient in some circuits" "[26-MAR-15] Add support for A3028C and A3028H." "Version 6 [08-APR-15] With VERSION = 4 or 5, frequency_low = 7, and x_id = 11 we" "have F2 generated incorrectly from FCK out of the chip. We introduce combinatorial" "node FHI, which determines when the output frequency should be HI or LO. We use" "FHI to set the frequency register bits, which we clock of VCK as before. This" "seems to solve the logic race condition that existed when we were trying to run the" "frequency register off its own outputs." "Version 7 [16-JUL-15] Add another gate to the ring oscillator, bringing the total" "number of gates in the ring to three. This slows down the fast clock (FCK) enough" "to allow the clock divider do operate without glitches. The TCK period now increases" "almost linearly with fck_divisor. The new values of fck_divisor will be lower than" "the previous values." "[29-JUL-15] With frequency_low = 7 and version = 1 current consumption is 200 uA" "instead of 80 uA. The TP1 output is noisy. We replace FHI with BIT, after the scheme" "we implemented in the A3030D firmware, and later we xor BIT with TCK to generate the" "output frequency DAC value. When we re-name the FHI signal, we find that it was" "declared in V06 firmware as an output pin at no fixed location. We make it a node and" "re-compile. The TP1 output now looks good and current consumption is 80 uA. We go back" "to V06 and convert FHI into a node. With frequenc_low = 7 current consumption is 80 uA." "But we note that the TP1 output has glitches. In our V07 firmware, where we generate" "the frequency DAC value with the VCK clock, the TP1 output has no glitches. We find" "that we must keep the BIT node or else the F1..F4 calculation becomes so large with" "x_id = 7 that it cannot settle in time for the VCK edge." "Version 8 [11-SEP-15] Add support for some new versions for reliability tests." "Version 9 [03-NOV-15] Now take fck_divisor and set up ring oscillator length" "and the divisor for the transmit clock counter so as to give finer resolution" "in obtaining TCK period." "Version 10 [17-NOV-15] We add support for the A3028AV5 circuit, which has a 160-Hz" "low-pass filter on the bottom-side amplifier, and an 80-Hz low-pass filter on the" "top-side amplifier. We can now make an 80-Hz single-channel or 160-Hz single-channel" "device with the same circuit, provided we can use the Y input alone when we want to" "make an 80-Hz device." "Version 11 [29-JAN-16] Extend ring oscillator to a maximum of 13-gates so as to" "provide division by 26 for TCK period." declarations "Configuration Parameters" "========================" "Version | Number | Description" "------------------------------" " A 2 dual-channel 512 SPS for mice" " B 1 single-channel 512 SPS for mice, using X" " C 5 single-channel 256 SPS for mice, using X" " D 2 dual-channel 512 SPS for rats" " E 1 single-channel 512 SPS for rats, using X" " F 4 dual-channel 1024 SPS for mice" " G 3 single-channel 1024 SPS for mice, using X" " H 6 dual-channel 256 SPS for mice" " Q 2 dual-channel 512 SPS for rats, protected" " R 1 single-channel 512 SPS for rats, protected, using X" " T 7 dual-channel 128 SPS for rats" " C 8 single-channel 256 SPS for mice, using Y." "Set the transmitter version number" VERSION = 6; "Channel number for x input, which may be the channel number for the y input" "as well, if the x input is disabled in for this transmitter version." x_id = 10; "Calibration Parameters" "======================" "Fast Clock Divisor, use to set TCK period in range 195-215 ns. Supported" "range for fck_divisor is 8 to 30." fck_divisor = 25; "Frequency Low, use to center transmit spectrum in range 913-918 MHz." frequency_low=8; "Parameters" "==========" "Version-Dependent Parameters Set Automatically" @IF (VERSION == 1) { tck_divisor=64; "Total Sample Rate 512 SPS enable_x=1; "X Input Enabled" enable_y=0; "Y Input Disabled" scatter_xmit=8; "Transmit Scatter is +-8" } @IF (VERSION == 2) { tck_divisor=32; "Total Sample Rate 1024 SPS enable_x=1; "X Input Enabled" enable_y=1; "Y Input Enabled" scatter_xmit=8; "Transmit Scatter is +-8" } @IF (VERSION == 3) { tck_divisor=32; "Total Sample Rate 1024 SPS" enable_x=1; "X Input Enabled" enable_y=0; "Y Input Disabled" scatter_xmit=8; "Transmit Scatter is +-8" } @IF (VERSION == 4) { tck_divisor=16; "Total Sample Rate 2048 SPS" enable_x=1; "X Input Enabled" enable_y=1; "Y Input Enabled" scatter_xmit=4; "Transmit Scatter is +-4" } @IF (VERSION == 5) { tck_divisor=128; "Total Sample Rate 256 SPS" enable_x=1; "X Input Enabled" enable_y=0; "Y Input Disabled" scatter_xmit=8; "Transmit Scatter is +-4" } @IF (VERSION == 6) { tck_divisor=64; "Total Sample Rate 512 SPS" enable_x=1; "X Input Enabled" enable_y=1; "Y Input Enabled" scatter_xmit=8; "Transmit Scatter is +-4" } @IF (VERSION == 7) { tck_divisor=128; "Total Sample Rate 256 SPS" enable_x=1; "X Input Enabled" enable_y=1; "Y Input Enabled" scatter_xmit=8; "Transmit Scatter is +-4" } @IF (VERSION == 8) { tck_divisor=128; "Total Sample Rate 256 SPS" enable_x=0; "X Input Disabled" enable_y=1; "Y Input Enabled" scatter_xmit=8; "Transmit Scatter is +-4" } "Set the clock divisor according to fck_divisor. We will also configure" "the ring oscillator to suit fck_divisor. The TCK period is a two gate" "delays multiplied by ring_length and tcd_divisor. For 7.5-ns chips, two" "internal gate delays are roughly 9.3 ns. The ring length must be between" "3 and 11. A ring length of 2 is too fast, and leads to glitches and counter" "failure. A ring length greater than 11 is not supported by the number of" "gates declared. The tcd_divisor must be between 3 and 15. It cannot be less" "than 3 because we need at least two divider states to arrange the timing of" "transmission. It cannot be more than 15 because we implement the divider with" "a four-bit counter." @IF (fck_divisor <= 8) {tcd_divisor = 2; ring_length = 4;} @IF (fck_divisor == 9) {tcd_divisor = 3; ring_length = 3;} @IF (fck_divisor == 10) {tcd_divisor = 2; ring_length = 5;} @IF (fck_divisor == 11) {tcd_divisor = 2; ring_length = 5;} @IF (fck_divisor == 12) {tcd_divisor = 4; ring_length = 3;} @IF (fck_divisor == 13) {tcd_divisor = 4; ring_length = 3;} @IF (fck_divisor == 14) {tcd_divisor = 2; ring_length = 7;} @IF (fck_divisor == 15) {tcd_divisor = 5; ring_length = 3;} @IF (fck_divisor == 16) {tcd_divisor = 4; ring_length = 4;} @IF (fck_divisor == 17) {tcd_divisor = 4; ring_length = 4;} @IF (fck_divisor == 18) {tcd_divisor = 6; ring_length = 3;} @IF (fck_divisor == 19) {tcd_divisor = 6; ring_length = 3;} @IF (fck_divisor == 20) {tcd_divisor = 5; ring_length = 4;} @IF (fck_divisor == 21) {tcd_divisor = 7; ring_length = 3;} @IF (fck_divisor == 22) {tcd_divisor = 2; ring_length = 11;} @IF (fck_divisor == 23) {tcd_divisor = 8; ring_length = 3;} @IF (fck_divisor == 24) {tcd_divisor = 8; ring_length = 3;} @IF (fck_divisor == 25) {tcd_divisor = 5; ring_length = 5;} @IF (fck_divisor == 26) {tcd_divisor = 2; ring_length = 13;} @IF (fck_divisor == 27) {tcd_divisor = 9; ring_length = 3;} @IF (fck_divisor == 28) {tcd_divisor = 7; ring_length = 4;} @IF (fck_divisor == 29) {tcd_divisor = 7; ring_length = 4;} @IF (fck_divisor >= 30) {tcd_divisor = 6; ring_length = 5;} "Other Parameters" frequency_step=2; "HI frequency - LO frequency" enable_rf=1; "Turns on RF oscillator during transmission" @IF (enable_x == 1) {y_id = x_id + 1;} "Using X and Y" @IF (enable_x == 0) {y_id = x_id;} "Using only Y" "Channel ID" I3..I0 node istype 'com'; "Transmitter ID nodes" id = [I3..I0]; "Inputs and Outputs" "==================" CK pin K9; "Clock From 32-kHz Oscillator" F4..F0 pin C10,D10,E10,G10,H10 istype 'reg'; "DAC for frequency" !SHDN pin A1 istype 'com'; "Shutdown Control for Transmitter" TP1 pin A7 istype 'com'; "Test Point" TP2 pin F1 istype 'com'; "Test Point" TP3 pin G1 istype 'com'; "Test Point" CONV pin H1 istype 'com'; "Convert for ADC" SDO pin K4; "Serial Data Out for ADC" SCK pin K5 istype 'com'; "Serial Clock for ADC" SDI pin H4 istype 'com,pos'; "Serial Data In for ADC" "Nodes" "=====" FCK node istype 'com,keep'; "Fast Clock" TCK node istype 'reg,keep'; "Transmission Clock" ECK node istype 'reg,keep'; "End Clock" VCK node istype 'reg,keep'; "VCO Clock" T0..T8 node istype 'reg,keep'; "Timer Running off 32-kHz" R1..R12 node istype 'com,keep'; "Ring Oscillator Bit" TXS0..TXS5 node istype 'reg,pos'; "Transmitter State" ACTIVE node istype 'reg,keep'; "Active period of 32-kHz" TXD node istype 'com,keep'; "Transmitter Done" TCD3..TCD0 node istype 'reg,keep'; "Transmit Clock Divider" TCDZ node istype 'reg,keep'; "Transmit Clock Divider Zero" ADC0..ADC3 node istype 'reg,keep'; "ADC Bits" TTS0..TTS3 node istype 'reg,keep'; "Transmit Time Shift" SDOS node istype 'reg,keep'; "SDO Synchronized" RSC3..RSC0 node istype 'reg'; "Reference Signal Counter" YSEL node istype 'reg'; "Select Channel Y" BIT node istype 'com,keep'; "The output bit value" "Sets" "====" time = [T8..T0]; "Transmit Cycle Timer" tcd = [TCD3..TCD0]; "Transmit Clock Divider" txs = [TXS5..TXS0]; "Transmitter State" adc_bits = [ADC3..ADC0]; "ADC Bits" xmit_time_shift = [TTS3..TTS0]; "Transmit Time Shift" active_time = [0,0,0,0,0,TTS3..TTS0]; "Active Time" frequency = [F4..F0]; "Frequency Voltage" ref_signal_count = [RSC3..RSC0]; "Reference Signal Counter" "Constants" "=========" num_sync_bits=11; "Number of synchronizing bits at transmission start." num_id_bits = 4; "Number of ID bits" num_start_bits = 1; "Transmitted zero to mark data start" num_stop_bits = 2; "Not transmitted, for txs termination" num_data_bits = 16; "Number of ADC data bits" num_xmit_bits = "Number of transmission bit periods" num_sync_bits + num_start_bits + num_id_bits + num_data_bits + num_id_bits; txs_done = "Final state of txs machine" num_xmit_bits + num_stop_bits; first_sync_bit = 1; first_start_bit = first_sync_bit + num_sync_bits; first_id_bit = first_start_bit + num_start_bits; first_data_bit = first_id_bit + num_id_bits; first_iid_bit = first_data_bit + num_data_bits; start_sck = "The txs state for first SCK falling edge" first_data_bit - 1; end_sck = "The txs state for last SCK falling edge" start_sck + num_data_bits - 1; equations "The transmit cycle timer runs off the 32-kHz clock and" "counts up to tck_divisor-1." time.clk=CK; when (time==tck_divisor-1) then { time:=0; } else { time:=time+1; } "The ECK clock sets the active_time register at the" "end of a cycle of tck_divisor CK periods." ECK.clk=CK; ECK:=(time==tck_divisor-2); "When ACTIVE is asserted, we begin a burst transmission." "When it is unasserted, we reset the burst transmission" "state machine. We must make sure that ACTIVE remains true" "for long enough for the burst transmission to complete." ACTIVE.clk=CK; ACTIVE:= (time==active_time); "TXD is true when the transmitter completes its burst" "transmission." TXD=(txs==txs_done); "We alternate between input channels with YSEL, which we" "toggle every time we are done with a transmission." YSEL.clk=TXD; when enable_x & enable_y then YSEL:=!YSEL; when enable_x & !enable_y then YSEL:=0; when !enable_x & enable_y then YSEL:=1; when !enable_x & !enable_y then YSEL:=0; "We change the channel ID depending upon whether we are" "transmitting X or Y." when YSEL then id = y_id else id = x_id; "The ring oscillator turns on when ACTIVE and remains" "on until TXD. Each gate in the ring adds 2 ns to the" "delay around the ring. The period of the oscillation is" "4 ns multiplied by the number of gates." @IF (ring_length == 2) { [FCK,R1]=[R1,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 3) { [FCK,R1..R2]=[R1..R2,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 4) { [FCK,R1..R3]=[R1..R3,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 5) { [FCK,R1..R4]=[R1..R4,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 6) { [FCK,R1..R5]=[R1..R5,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 7) { [FCK,R1..R6]=[R1..R6,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 8) { [FCK,R1..R7]=[R1..R7,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 9) { [FCK,R1..R8]=[R1..R8,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 10) { [FCK,R1..R9]=[R1..R9,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 11) { [FCK,R1..R10]=[R1..R10,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 12) { [FCK,R1..R11]=[R1..R11,!FCK & ACTIVE & !TXD]; } @IF (ring_length == 13) { [FCK,R1..R12]=[R1..R12,!FCK & ACTIVE & !TXD]; } "The transmit clock divider runs off FCK and divides it down" "to 5 MHz by correct choice of fck_divisor during transmitter" "calibration." tcd.aclr=!ACTIVE; tcd.clk=FCK; when (tcd==tcd_divisor-1) then { tcd:=0; } else { tcd:=tcd+1; } "We detect the transmit clock divider being zero with TCDZ." TCDZ.aclr=!ACTIVE; TCDZ.clk=FCK; TCDZ:=(tcd==0); "The transmit clock should be close to or a little less than 5 MHz," "with a mark-space ratio of exactly 50%." TCK.aclr=!ACTIVE; TCK.clk=TCDZ; TCK:=!TCK; "The transmitter state machine steps through all its" "states when ACTIVE is asserted, and then stops in its" "final state, waiting for !ACTIVE, which will reset it" "to zero." txs.aclr=!ACTIVE; txs.clk=TCK; when (txs==txs_done) then txs:=txs else txs:=txs+1; "Transmit sixteen ADC bits." when (txs>0) & (txs=1)&(txs =first_data_bit)&(txs =start_sck) & (txs<=end_sck) & !TCK; "We configure the ADC for single-ended input by setting" "SDI to 1 on the first rising edge of SCK after CONV goes" "low." when txs==start_sck then SDI=1; "We select ADC channel that will be digitized at the end" "of the CONV pulse with the value of SDI on the second" "rising edge of SCK after CONV goes low. With SDI = 0, the" "next sample from the ADC will be of X. But note that this" "selection does not affect the sample being read out by SCK" "during the CONV low pulse. The YSEL signal tells us that" "Y is being read out now, and !YSEL means we are reading out" "X. So when we are alternating between the two, we set SDI" "to !YSEL, because we will then select X for the channel after" "Y, and Y after X. But when we have only one channel enabled," "we either always select X or always select Y." when enable_x & enable_y then { when txs==start_sck+1 then SDI=!YSEL; } when enable_x & !enable_y then { when txs==start_sck+1 then SDI=0; } when !enable_x & enable_y then { when txs==start_sck+1 then SDI=1; } when !enable_x & !enable_y then { when txs==start_sck+1 then SDI=0; } "Test Points" TP1=(frequency==frequency_low+frequency_step); END