"
module P3032A

title 'P3032A'

"Animal Location Tracker (A3032X) Firmware"
"========================================="

"Version 1, Based on P302701A Version 7, designed to work with Octal Data Receiver firmware"
"P302701A08. Provides reception of notifications and storage of their four-byte contents"
"in RAM. We can download data just as if the A3032A were a Data Receiver."

"Version 2, We replace the data for transmitter messages with the digitized power measurement"
"from coil number eight, so the SCT trace in the Recorder Instrument, when downloading from"
"the ALT, is a plot of power received versus time."

"Version 3, We fix the notification receiver so that it uses two synchronized versions of Y"
"to achieve reliable reception, one version clocked on the rising edge of DCK, the other on"
"the falling edge. We ignore incoming data and clock transmissions with id number 255, because"
"these are the result of garbled incoming notifications from A3037E firmware version 8. We add"
"a Power Measurement Valid flag to force power measurements to expire before they can be used"
"twice."

"Version 4. Remove PMV flag because it does not help get rid of bad message, and it will make"
"it impossible for us to identify collisions between many devices received at the same time."
"Add code for all fifteen antenna amplifiers. We implement amplifiers 8 and 9."

"Version 5. When we store a measurement, we store the ID, sample value from the data"
"notification, the timestamp of the data notification, followed by fifteen bytes of power"
"measurements and a one-byte firmware version. When we store a clock message, we store a"
"set of background power values for the detector coils, which analysis can subtract from the"
"measured power values to get the power received from external sources."

"Version 6. We abandon the power calibration of each coil because it did not produce any"
"observable improvement in tracking. We encode the coordinates of each coil in its"
"corresponding byte in clock messages. The top nibble of each coordinate byte gives the"
"x-coordinate of the coil, and the bottom nibble is the y-coordinate. The units are coil"
"grid spacings. The first coil is always (0,0) so we encode the grid spacing in cm in place"
"of the coordinates. The final byte of sixteen in the payload is the encoding version, 1, to"
"identify this coil location encoding. The firmware version of the tracker takes the"
"place of the data receiver firmware version in the clock messages."

"Version 7. We have not been using the coordinates recorded in clock messages, and we find"
"that for implanted transmitters the received power is so weak that the background powers"
"are significant. So we now implement dynamic background power calibration using the payload"
"of the clock message."

"Version 8. Restore power calibration of each coil in the firmware as we did in Version 5."
"We find that interference renders the dynamic power calibration worse than ineffective: it"
"causes spurious errors of up to 20 cm in transmitter location. The original firmware-based"
"calibration of the amplifiers and power converter are useful when the coil power is weak,"
"as in recordings from mice standing up in their cages."


declarations
  firmware_version = ^d8;

  "Power calibration for A3023A/C 23-AUG-17:"
  "20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0"
  p1_calib = 20;
  p2_calib = 20;
  p3_calib = 20;
  p4_calib = 20;
  p5_calib = 20;
  p6_calib = 20;
  p7_calib = 20;
  p8_calib = 20;
  p9_calib = 20;
  p10_calib = 20;
  p11_calib = 20;
  p12_calib = 20;
  p13_calib = 20;
  p14_calib = 20;
  p15_calib = 20;

  fake_transmit_data = 0;
  fake_write_data = 0;
  incoming_notification = 10;
  clock_transmission = 11;
  data_transmission = 12;
  clock_id = 0;
equations

"Pins"
declarations
A pin 112; "LVDS input"
B pin 117 istype 'com'; "LVDS output"
LB pin 115 istype 'com'; "Loop Back"
!RESET pin 39; "Hardware Reset Input"
DCK pin 154; "Data Clock, 40 MHz"
!RW pin 84 istype 'com'; "RAM Write"
!RCE pin 50 istype 'reg'; "RAM Chip Enable"
!ROE pin 62 istype 'com'; "RAM Output Enable"
RA18..RA0 pin 58, 59, 60, 61, 72, 73, 74, 75, 76, 77, 80, 
  81, 82, 83, 51, 52, 53, 54, 57 istype 'com'; "RAM Address"
ram_addr = [RA18..RA0];
RD7..RD0 pin 63, 64, 70, 71, 85, 86, 48, 49 istype 'com'; "RAM Data"
ram_data = [RD7..RD0];
UPLOAD pin 94 istype 'reg'; "Upload Indicator"
EMPTY pin 37 istype 'reg'; "Empty Indicator"
RECEIVE pin 93 istype 'reg'; "Receive Indicator"
TP1,TP2,TP3 pin 10, 20, 30 istype 'com'; "Test Points"
X pin 109;
XEN pin 107 istype 'com';
Y pin 103 istype 'com';
!FS pin 5 istype 'reg'; "FRAM Select"
FQ pin 6; "FRAM Serial Output"
FC pin 23 istype 'reg'; "FRAM Serial Clock"
FD pin 24 istype 'com'; "FRAM Serial Input"
!CS1 pin 120 istype 'com'; "ADC Chip Select 1"
SC1 pin 124 istype 'com'; "ADC Serial Clock 1"
SD1 pin 122; "ADC Serial Data 1"
!CS2 pin 99 istype 'com'; "ADC Chip Select 2"
SC2 pin 106 istype 'com'; "ADC Serial Clock 2"
SD2 pin 102; "ADC Serial Data 2"
!CS3 pin 36 istype 'com'; "ADC Chip Select 3"
SC3 pin 33 istype 'com'; "ADC Serial Clock 3"
SD3 pin 34; "ADC Serial Data 3"
!CS4 pin 126 istype 'com'; "ADC Chip Select 4"
SC4 pin 136 istype 'com'; "ADC Serial Clock 4"
SD4 pin 135; "ADC Serial Data 4"
!CS5 pin 127 istype 'com'; "ADC Chip Select 5"
SC5 pin 95 istype 'com'; "ADC Serial Clock 5"
SD5 pin 98; "ADC Serial Data 5"
!CS6 pin 32 istype 'com'; "ADC Chip Select 6"
SC6 pin 25 istype 'com'; "ADC Serial Clock 6"
SD6 pin 26; "ADC Serial Data 6"
!CS7 pin 140 istype 'com'; "ADC Chip Select 7"
SC7 pin 145 istype 'com'; "ADC Serial Clock 7"
SD7 pin 141; "ADC Serial Data 6"
!CS8 pin 137 istype 'com'; "ADC Chip Select 8"
SC8 pin 139 istype 'com'; "ADC Serial Clock 8"
SD8 pin 138; "ADC Serial Data 8"
!CS9 pin 174 istype 'com'; "ADC Chip Select 9"
SC9 pin 172 istype 'com'; "ADC Serial Clock 9"
SD9 pin 173; "ADC Serial Data 9"
!CS10 pin 149 istype 'com'; "ADC Chip Select 10"
SC10 pin 152 istype 'com'; "ADC Serial Clock 10"
SD10 pin 150; "ADC Serial Data 10"
!CS11 pin 146 istype 'com'; "ADC Chip Select 11"
SC11 pin 148 istype 'com'; "ADC Serial Clock 11"
SD11 pin 147; "ADC Serial Data 11"
!CS12 pin 171 istype 'com'; "ADC Chip Select 12"
SC12 pin 169 istype 'com'; "ADC Serial Clock 12"
SD12 pin 170; "ADC Serial Data 12"
!CS13 pin 158 istype 'com'; "ADC Chip Select 13"
SC13 pin 160 istype 'com'; "ADC Serial Clock 13"
SD13 pin 159; "ADC Serial Data 13"
!CS14 pin 163 istype 'com'; "ADC Chip Select 14"
SC14 pin 161 istype 'com'; "ADC Serial Clock 14"
SD14 pin 162; "ADC Serial Data 14"
!CS15 pin 168 istype 'com'; "ADC Chip Select 15"
SC15 pin 164 istype 'com'; "ADC Serial Clock 15"
SD15 pin 165; "ADC Serial Data 15"
equations


"Global Nodes"
declarations
ATR node istype 'com'; "Address and Timestamp Reset"
RST node istype 'com'; "Global Reset"
MRDY node istype 'com'; "Message Ready"
MDS node istype 'com,pos'; "Message Data Strobe" 
MD0..MD7 node istype 'reg'; "Message Data"
message_data = [MD7..MD0];
NDST node istype 'com,pos,keep'; "Notification Data Stored"
equations


"Reset Signals"
"-------------"


"The RESET signal from U11 goes low for 300 ms after power-up and whenever"
"any switch or logic output pulls it low. The ATR signal goes hi when we"
"receive a reset command from the LWDAQ. We assert RST when either of"
"these is asserted."

RST = RESET # ATR;


"LWDAQ Command and Address Decoding"
"----------------------------------"

"This LWDAQ receiver uses the 40-MHz data clock to generate"
"the DA and DDA signals. We synchronise the incoming serial"
"logic signal, A, with the data clock."

declarations
SA node istype 'reg'; "Synchronized A"
DSA node istype 'reg'; "Delayed SA"
DA node istype 'com,keep'; "Delayed A Rising Edge"
DDA node istype 'com,keep'; "Delayed DA"
AA node istype 'reg'; "Address Active"
DAA node istype 'reg'; "Delayed AA"
CA node istype 'reg'; "Command Active"
DCA node istype 'reg'; "Delayed CA"
ER,Q1..Q16 node istype 'reg'; "Receiver Bits"
LT3..LT0 node istype 'reg'; "LWDAQ Timer"
lt = [LT3..LT0];
AS node istype 'reg'; "Address Strobe"
CS node istype 'reg'; "Command Strobe"
DS node istype 'reg'; "Data Strobe"
DC1..DC16 node istype 'reg';"Device Command Bits"
DA0..DA15 node istype 'reg';"Device Address Bits"
WAKE node istype 'com'; "Wake"
DTX node istype 'com'; "Device Transmit"
ISTS node istype 'com'; "Immediate Sample Transmit Select"
equations

"We synchronize A with DCK, and provide a delayed"
"version of A that allows us to detect edges."
[SA,DSA].clk = DCK;
[SA,DSA].aclr = RESET;
SA := A;
DSA := SA;

"This timer allows us to generate the Delayed A (DA)"
"and Double-Delayed A (DDA) signals for serial reception."
lt.clk = DCK;
lt.aclr = RESET;
when lt==0 then {
  when SA & !DSA then lt:=1
  else lt:=0;
} else {
  when lt==9 then lt:=0
  else lt:=lt+1;
}
DA = (lt==4);
DDA = (lt==9);

"We use DCK to clock the receiver registers, and RESET to"
"clear them."
[ER,Q1..Q16,AA,DAA,AS,CA,DCA,CS,DS,DC1..DC16,DA0..DA15].clk = DCK;
[ER,Q1..Q16,AA,DAA,AS,CA,DCA,CS,DS,DC1..DC16,DA0..DA15].aclr = RESET;

"We move bits along the shift register on DA."
when DA then [ER,Q1..Q16] := [SA,ER,Q1..Q15];
else [ER,Q1..Q16] := [ER,Q1..Q16];

"Address Active provides a pulse that begins with DDA"
"on the start bit of an address transmission, and ends"
"with the stop bit of an address transmission. Delayed"
"AA allows us to create Address Strobe, or AS. Address"
"Strobe provides a pulse at the end of an address"
"reception. We clock the receiver bits into the address"
"register on a rusing edge of AS."
when DDA then AA := (!AA & !CA & !SA & !ER) # (AA & !SA)
else AA := AA;
DAA := AA;
AS := DAA & !AA;
when AS then [DA0..DA15] := [Q1..Q16]
else [DA0..DA15] := [DA0..DA15];

"Command Active provides a pulse that begins with DDA"
"on the start bit of a command transmission, and ends"
"with the stop bit of a command transmission. Delayed"
"CA allows us to create Command Strobe, or CS. Command"
"strobe provides a pulse at the end of each command"
"reception. We clock the receiver bits into the command"
"register on a rusing edge of CS."
when DDA then CA := (!AA & !CA & !SA & ER) # (CA & !SA)
else CA := CA;
DCA := CA;
CS := DCA & !CA;
when CS then [DC1..DC16] := [Q1..Q16]
else [DC1..DC16] := [DC1..DC16];

"Data Strobe identifies a solitary low pulse on A. A"
"solitary low pulse, combined with DTX, indicates that"
"the drivers is expecting this device to upload eight"
"bits of data."
DS := DDA & SA & !AA & !CA;

"Address and Timestamp Reset"
ATR = DC1 # RESET;

"Device Transmit Bit"
DTX = DC5;

"We loop back A to B so long as DTX is not set."
when !DTX then B = A;

"We enable the return LVDS driver when DC7 is set."
LB = DC7;

"WAKE bit."
WAKE = DC8;

"Immediate Sample Transmit Select"
ISTS = DC2;



"Slow Clock"
"----------"

"The slow clock is 2.4 kHz. We use it to provide a receive and upload indicator"

declarations
  SCK0..SCK13 node istype 'reg';
  SCK node istype 'com';
  sck_count = [SCK13..SCK0];
equations

sck_count.clk = DCK;
sck_count := sck_count + 1;
SCK = SCK13;



"Data Transmitter"
"----------------"

"The transmitter sends bytes back to the driver. It waits for"
"DS combined with DTX (DC5). When it receives DS and DTX, it"
"waits for Transmission Byte Load (TBL). The transmitter uses"
"DCK to time its serial transmission to the driver. It transmits"
"a leading zero followed by the eight bits of the transmission"
"byte (tb)."

declarations
TS4..TS0 node istype 'reg'; "Transmit State"
TBD7..TBD0 node istype 'reg'; "Transmission Bits"
ts=[TS4..TS0];"Transmission State"
tbd=[TBD7..TBD0]; "Transmission Byte Data"
TBL node istype 'com,pos,keep'; "Transmitter Byte Load"
TBO node istype 'com,keep'; "Transmitter Bit Out"
equations

"The Transmit Byte, tb, holds a byte for transmission to"
"the LWDAQ driver."
tbd.clk = DCK;
tbd.aclr = RESET;

"The Transmitter State, ts, controls serial transmission"
"to the LWDAQ driver of the Transmit Byte."
"We assert Transmit Byte Load, TBL, elsewhere in the code"
"when we want to clock ram_data.pin into the Transmit Byte."
ts.clk = DCK;
ts.aclr = RESET;
state_diagram ts;
  state 0:if DS & DTX then 1 else 0;
  state 1:
    if !DTX then 0;
    if DTX & TBL then 2;
    if DTX & !TBL then 1;
  state 2:goto 3;"Start Bit"
  state 3:goto 4;"Start Bit"
  state 4:goto 5;"TBD7"
  state 5:goto 6;"TBD7"
  state 6:goto 7;
  state 7:goto 8;
  state 8:goto 9;
  state 9:goto 10;
  state 10:goto 11;
  state 11:goto 12;
  state 12:goto 13;
  state 13:goto 14;
  state 14:goto 15;
  state 15:goto 16;
  state 16:goto 17;
  state 17:goto 18;
  state 18:goto 19;"TBD0"
  state 19:goto 20;"TBD0"
  state 20:goto 0;"Stop Bit"
equations;

"TBO is the output of the bit transmitter. It passes through"
"the LVDS return and so along the cables to the driver."
TBO = (
    (ts==0)  & 1   "Idle Bit 1"
  # (ts==1)  & 1   "Idle Bit 1"
  # (ts==2)  & 0   
  # (ts==3)  & 0
  # (ts==4)  & TBD7
  # (ts==5)  & TBD7
  # (ts==6)  & TBD6
  # (ts==7)  & TBD6
  # (ts==8)  & TBD5
  # (ts==9)  & TBD5
  # (ts==10) & TBD4
  # (ts==11) & TBD4
  # (ts==12) & TBD3
  # (ts==13) & TBD3
  # (ts==14) & TBD2
  # (ts==15) & TBD2
  # (ts==16) & TBD1
  # (ts==17) & TBD1
  # (ts==18) & TBD0
  # (ts==19) & TBD0
  # (ts==20) & 1   "Stop Bit 1"
);

"We return TBO to the driver when DTX is set."
when DTX then B = TBO;

"We load the transmitter byte from the RAM data bus. When"
"some other part of the firmware asserts TBL."
when !fake_transmit_data then {
  when TBL then tbd:=ram_data.pin;
  else tbd:=tbd;
}

"We can generate fake data to check for errors in the"
"upload to the LWDAQ driver, as opposed to errors in"
"RF reception."
when fake_transmit_data then {
  TBL = 1;
  when (ts==20) then {tbd:=tbd+1;} else {tbd:=tbd;}
}


"Address Counters"
"----------------"

"We have two address counters, one for storage and one for transmission."
"The Message Router transmits bytes from RAM to the LWDAQ Driver and"
"stores bytes to RAM from the Message Detector."

declarations
SA18..SA0 node istype 'reg'; "Store Address"
store_addr = [SA18..SA0];
sab0=[SA7..SA0]; "Store Address byte zero"
sab1=[SA15..SA8]; "Store address byte one"
sab2=[SA18..SA16]; "Store address byte two"
SAC0,SAC1 node istype 'com'; "Store Address Carry"
SAI node istype 'com,pos,keep'; "Store Address Increment"
TA18..TA0 node istype 'reg'; "Transmit Address"
transmit_addr = [TA18..TA0];
tab0=[TA7..TA0]; "Transmit address byte zero"
tab1=[TA15..TA8]; "Transmit address byte one"
tab2=[TA18..TA16]; "Transmit address byte two"
TAC0,TAC1 node istype 'com,keep'; "Transmit Address Carry"
TAI node istype 'com,pos,keep'; "Transmit Address Increment"
!AE0 node istype 'com,keep'; "Addresses Equal Byte 0"
!AE1 node istype 'com,keep'; "Addresses Equal Byte 1"
!AE2 node istype 'com,keep'; "Addresses Equal Byte 2"
AE node istype 'com'; "Addresses Equal"
equations

"The store address is the RAM address at which we store"
"data."
store_addr.clk = DCK;
store_addr.aclr = ATR;
SAC0 = (sab0==^hFF);
SAC1 = (sab1==^hFF);
when SAI then {
  sab0 := sab0+1;
  when SAC0 then sab1 := sab1+1;
  else sab1 := sab1;
  when SAC1 & SAC0 then sab2 := sab2+1;
  else sab2 := sab2;
} else {
  store_addr := store_addr;
}

"The transmit address is the RAM address from which"
"we read data to transmit."
transmit_addr.clk = DCK;
transmit_addr.aclr = ATR;
TAC0 = (tab0==^hFF);
TAC1 = (tab1==^hFF);
when TAI then {
  tab0 := tab0+1;
  when TAC0 then tab1 := tab1+1;
  else tab1 := tab1;
  when TAC1 & TAC0 then tab2 := tab2+1;
  else tab2 := tab2;
} else {
  transmit_addr := transmit_addr;
}

"Address Equal is true when the store address equals the"
"transmit address."
AE0 = (sab0 == tab0);
AE1 = (sab1 == tab1);
AE2 = (sab2 == tab2);
AE = AE0 & AE1 & AE2;


"RAM Controller"
"--------------"

"The RAM Controller arbitrates between storing messages from the"
"Message Detector and transmitting mesages to the LWDAQ Driver."
"It gives priority to bytes waiting to be stored. When it sees"
"MRDY it stores the byte presented by the Message Detector on the"
"message data. When it sees the transmitter state machine waiting"
"to transmit a byte, and provided MRDY is not asserted, the RAM"
"Controller retrieves a byte from RAM and passes it to the byte"
"transmitter. Note that the RAM Controller deals only in bytes. It"
"does not know about the four-byte structure of messages. It just"
"keeps reading bytes from the Message Detector until there are no"
"more bytes to read."

declarations
RDOE node istype 'com,keep'; "RAM Data Output Enable (from this chip)"
RCS2..RCS0 node istype 'reg'; "Ram Controller State"
rcs=[RCS2..RCS0];
FWD0..FWD7 node istype 'reg,pos'; "Fake Write Data"
fwd=[FWD7..FWD0];
equations

"The Fake Write Data is what we write to RAM when we want"
"to test if writing to RAM is reliable. We use the LWDAQ"
"Terminal Instrument to read blocks of data from the Data"
"Receiver and look at them on the screen to wath for glitches."
fwd.clk = DCK;
fwd.aclr = RESET;
when SAI then fwd:=fwd+1 else fwd:=fwd;

"The RAM Controller responds to new messages in preference"
"to a request to upload data over the LWDAQ interface."
"If the RAM buffer is empty, which we mark with AE,"
"address equal, the RAM Controller waits until there"
"is another byte to transmit before it loads the transmit"
"buffer with the byte and permits transmission."
rcs.clk = DCK;
rcs.aclr = RESET;
state_diagram rcs;
  state 0:
    if MRDY then 1 
    else if (ts==1) & !AE then 5
    else 0;
  state 1:goto 2;   "store byte"
  state 2:goto 3;   "increment storage address"
  state 3:goto 4;   "wait for MRDY to settle"
  state 4:goto 0;   "wait for MRDY to settle"
  state 5:goto 6;   "load transmit byte from ram"
  state 6:goto 0;   "increment transmit address"
equations


ram_data.oe = RDOE;
RCE.clk = DCK;

when (rcs==0) then {  "rest state"
  ram_addr = store_addr
  ram_data = 0;
  RW = 0;
  ROE = 0;
  RCE := 0;
  RDOE = 1;
} 
when (rcs==1) then { "store byte from Message Detector"
  ram_addr = store_addr;
  when !fake_write_data then {
    ram_data = message_data; 
  } else {
    ram_data=fwd;
  }
  RW = 1;
  ROE = 0;
  RCE := 1;
  RDOE = 1;
}
when (rcs==2) then {  "increment storage address"
  ram_addr = store_addr;
  when !fake_write_data then {
    ram_data = message_data;
  } else {
    ram_data=fwd;
  }
  RW = 1;
  ROE = 0;
  RCE := 0;
  RDOE = 1;
  SAI = 1;
  MDS = 1;
}
when (rcs==3) then {  "wait for MRDY to settle"
  ram_addr = store_addr;
  ram_data=0;
  RW = 1;
  ROE = 0;
  RCE := 0;
  RDOE = 1;
}
when (rcs==4) then {  "wait for MRDY to settle"
  ram_addr = store_addr;
  ram_data=0;
  RW = 1;
  ROE = 0;
  RCE := 0;
  RDOE = 1;
}
when (rcs==5) then {
  ram_addr = transmit_addr; 
  ram_data = 0; "load transmit byte from ram"
  RW = 0;
  ROE = 1;
  RCE := 1;
  RDOE = 0;
}
when (rcs==6) then {
  ram_addr = transmit_addr;
  ram_data = 0; "increment transmit address"
  RW = 0;
  ROE = 1;
  RCE := 0;
  TAI = 1;
  TBL = 1;
  RDOE = 0;
}


"Notification Receiver"
"---------------------"

declarations
  YSP, YSN node istype 'reg'; "Y Synchronized Positive/Negative Edge"
  NRS0..NRS6 node istype 'reg'; "Notification Receiver State"
  nrs = [NRS6..NRS0];
  nrs_tx_end = 76;
  nrs_in_end = 10;
  nrs_id_ready = 25;
  NIR0..NIR3 node istype 'reg'; "Notification Instruction Register"
  nir = [NIR3.. NIR0];
  NID0..NID7 node istype 'reg'; "Notification Identification Number"
  nid = [NID7..NID0];
  NMD0..NMD15 node istype 'reg'; "Notification Message Data"
  nmd = [NMD15..NMD0];
  NTS0..NTS7 node istype 'reg'; "Notification Time Stamp"
  nts = [NTS7..NTS0];
  INR node istype 'com,keep'; "Incoming Notification Received"
equations

"We synchronize Y, the incoming notification signal, with the rising and"
"falling edges of DCK. We need both signals to get reliable reception of the"
"thirty-two bits of notification data."
YSP.clk = DCK;
YSP := Y;
YSN.clk = !DCK;
YSN := Y;

"The Notification Receiver watches for a LO on Y and then commences serial"
"reception of thirty-six bits: a four-bit instruction, eight-bit ID, sixteen-"
"bit data, and eight-bit timestamp. In the case of the incoming notification"
"instruction, the rest of the bits after the instruction will all be ones."
"But for clock and data transmissions, they will contain useful data."
nrs.clk = DCK;
nrs.aclr = RESET;
when (nrs == 0) then {
  when !YSP then nrs := 1
  else nrs := 0;
}
when (nrs >= 1) & (nrs <= nrs_tx_end - 2) then {
  when (nir == incoming_notification) & (nrs == nrs_in_end) then nrs := 0
  else nrs := nrs + 1;
}
when (nrs == nrs_tx_end - 1) then {
  "Detecting the erroneous nid == 255 allows this firmware to work with"
  "the one Octal Data Receiver that we shipped with firmware P302701A08."
  when (nid == 255) then {
    nrs := 0
  } else {
    when (nir == clock_transmission) 
        # (nir == data_transmission) then {
      nrs := nrs_tx_end
    } else {
      nrs := 0;
    }
  }
}
when (nrs == nrs_tx_end) then {
  when NDST then nrs := 0
  else nrs := nrs_tx_end;
}

"The Notification Instruction Register contains the four-bit instruction"
"sent with every notification."
nir.clk = DCK;
when (nrs == 0) then {
  nir := 0;
} else when (nrs == 2) # (nrs == 4) # (nrs == 6) # (nrs == 8) then {
  nir := [NIR2..NIR0,YSN];
} else {
  nir := nir;
}

"The Notification Identification Number contains the eight-bit channel"
"identification number sent with every clock and data transmission."
nid.clk = DCK;
when (nrs == 0) then {
  nid := 0;
} else when (nrs == 10) # (nrs == 12) # (nrs == 14) # (nrs == 16) 
    # (nrs == 18) # (nrs == 20) # (nrs == 22) # (nrs == 24) then {
  nid := [NID6..NID0,YSN];
} else {
  nid := nid;
}

"The Notification Message Data contains the sixteen-bit contents of"
"each clock and data transmission. For a clock message, the contents"
"are the top two bytes of the three-byte timestamp. For a data message"
"the contents are a digitized voltage or some other digital data from"
"a transmitter."
nmd.clk = DCK;
when (nrs == 0) then {
  nmd := 0;
} else when (nrs == 26) # (nrs == 28) # (nrs == 30) # (nrs == 32) 
    # (nrs == 34) # (nrs == 36) # (nrs == 38) # (nrs == 40) 
    # (nrs == 42) # (nrs == 44) # (nrs == 46) # (nrs == 48) 
    # (nrs == 50) # (nrs == 52) # (nrs == 54) # (nrs == 56) then {
  nmd := [NMD14..NMD0,YSN];
} else {
  nmd := nmd;
}

"The Notification Time Stamp contains the eight-bit timestamp that"
"arrives with every message, and is the lowest eight bits of the three"
"byte message timestamp."
nts.clk = DCK;
when (nrs == 0) then {
  nts := 0;
} else when (nrs == 58) # (nrs == 60) # (nrs == 62) # (nrs == 64) 
    # (nrs == 66) # (nrs == 68) # (nrs == 70) # (nrs == 72) then {
  nts := [NTS6..NTS0,YSN];
} else {
  nts := nts;
}

"We use the Incoming Notification Received flag to initiate digitization"
"of power measurements. The notification can arise either from an incoming"
"notification instruction or from the arrival of a clock message."
INR = (nrs == nrs_in_end) & (nir == incoming_notification)
  # (nrs == nrs_id_ready) & (nid == clock_id) & (nir == clock_transmission);


"Power Measurement Digitizer"
"---------------------------"

declarations
  PDS0..PDS4 node istype 'reg'; "Power Digitizer State"
  pds = [PDS4..PDS0];
  pds_end = 25;
  pds_bit_zero = 9;
  pds_bit_seven = pds_bit_zero + 15;
  ACK node istype 'reg'; "ADC Clock"
  PD1D0..PD1D7 node istype 'reg'; "Power Detector 1 Data"
  pd1 = [PD1D7..PD1D0];
  PD2D0..PD2D7 node istype 'reg'; "Power Detector 2 Data"
  pd2 = [PD2D7..PD2D0];
  PD3D0..PD3D7 node istype 'reg'; "Power Detector 3 Data"
  pd3 = [PD3D7..PD3D0];
  PD4D0..PD4D7 node istype 'reg'; "Power Detector 4 Data"
  pd4 = [PD4D7..PD4D0];
  PD5D0..PD5D7 node istype 'reg'; "Power Detector 5 Data"
  pd5 = [PD5D7..PD5D0];
  PD6D0..PD6D7 node istype 'reg'; "Power Detector 6 Data"
  pd6 = [PD6D7..PD6D0];
  PD7D0..PD7D7 node istype 'reg'; "Power Detector 7 Data"
  pd7 = [PD7D7..PD7D0];
  PD8D0..PD8D7 node istype 'reg'; "Power Detector 8 Data"
  pd8 = [PD8D7..PD8D0];
  PD9D0..PD9D7 node istype 'reg'; "Power Detector 9 Data"
  pd9 = [PD9D7..PD9D0];
  PD10D0..PD10D7 node istype 'reg'; "Power Detector 10 Data"
  pd10 = [PD10D7..PD10D0];
  PD11D0..PD11D7 node istype 'reg'; "Power Detector 11 Data"
  pd11 = [PD11D7..PD11D0];
  PD12D0..PD12D7 node istype 'reg'; "Power Detector 12 Data"
  pd12 = [PD12D7..PD12D0];
  PD13D0..PD13D7 node istype 'reg'; "Power Detector 13 Data"
  pd13 = [PD13D7..PD13D0];
  PD14D0..PD14D7 node istype 'reg'; "Power Detector 14 Data"
  pd14 = [PD14D7..PD14D0];
  PD15D0..PD15D7 node istype 'reg'; "Power Detector 15 Data"
  pd15 = [PD15D7..PD15D0];
  ARS node istype 'com,pos,keep'; "ADC Read Shift"
  PMS node istype 'com,pos,keep'; "Power Measurement Shift"
equations

pds.aclr = RESET;
pds.clk = DCK;
when (pds == 0) then {
  when INR then pds := 1
  else pds := 0;
}
when (pds > 0) & (pds < pds_end) then {
  pds := pds + 1
}
when (pds >= pds_end) then {
  pds := 0;
}

[CS1..CS15] = (pds > 0);

ACK.clk = DCK;
ACK := (pds == 0) # PDS0;
[SC1..SC15] = ACK;

ARS = (pds >= pds_bit_zero) & (pds <= pds_bit_seven);

pd1.aclr = RESET;
pd1.clk = !DCK;
when ARS & ACK then {
  pd1 := [PD1D6..PD1D0,SD1];
} else {
  when PMS then pd1 := pd2
  else pd1 := pd1;
}

pd2.aclr = RESET;
pd2.clk = !DCK;
when ARS & ACK then {
  pd2 := [PD2D6..PD2D0,SD2];
} else {
  when PMS then pd2 := pd3
  else pd2 := pd2;
}

pd3.aclr = RESET;
pd3.clk = !DCK;
when ARS & ACK then {
  pd3 := [PD3D6..PD3D0,SD3];
} else {
  when PMS then pd3 := pd4
  else pd3 := pd3;
}

pd4.aclr = RESET;
pd4.clk = !DCK;
when ARS & ACK then {
  pd4 := [PD4D6..PD4D0,SD4];
} else {
  when PMS then pd4 := pd5
  else pd4 := pd4;
}

pd5.aclr = RESET;
pd5.clk = !DCK;
when ARS & ACK then {
  pd5 := [PD5D6..PD5D0,SD5];
} else {
  when PMS then pd5 := pd6
  else pd5 := pd5;
}

pd6.aclr = RESET;
pd6.clk = !DCK;
when ARS & ACK then {
  pd6 := [PD6D6..PD6D0,SD6];
} else {
  when PMS then pd6 := pd7
  else pd6 := pd6;
}

pd7.aclr = RESET;
pd7.clk = !DCK;
when ARS & ACK then {
  pd7 := [PD7D6..PD7D0,SD7];
} else {
  when PMS then pd7 := pd8
  else pd7 := pd7;
}

pd8.aclr = RESET;
pd8.clk = !DCK;
when ARS & ACK then {
  pd8 := [PD8D6..PD8D0,SD8];
} else {
  when PMS then pd8 := pd9
  else pd8 := pd8;
}

pd9.aclr = RESET;
pd9.clk = !DCK;
when ARS & ACK then {
  pd9 := [PD9D6..PD9D0,SD9];
} else {
  when PMS then pd9 := pd10
  else pd9 := pd9;
}

pd10.aclr = RESET;
pd10.clk = !DCK;
when ARS & ACK then {
  pd10 := [PD10D6..PD10D0,SD10];
} else {
  when PMS then pd10 := pd11
  else pd10 := pd10;
}

pd11.aclr = RESET;
pd11.clk = !DCK;
when ARS & ACK then {
  pd11 := [PD11D6..PD11D0,SD11];
} else {
  when PMS then pd11 := pd12
  else pd11 := pd11;
}

pd12.aclr = RESET;
pd12.clk = !DCK;
when ARS & ACK then {
  pd12 := [PD12D6..PD12D0,SD12];
} else {
  when PMS then pd12 := pd13
  else pd12 := pd12;
}

pd13.aclr = RESET;
pd13.clk = !DCK;
when ARS & ACK then {
  pd13 := [PD13D6..PD13D0,SD13];
} else {
  when PMS then pd13 := pd14
  else pd13 := pd13;
}

pd14.aclr = RESET;
pd14.clk = !DCK;
when ARS & ACK then {
  pd14 := [PD14D6..PD14D0,SD14];
} else {
  when PMS then pd14 := pd15
  else pd14 := pd14;
}

pd15.aclr = RESET;
pd15.clk = !DCK;
when ARS & ACK then {
  pd15 := [PD15D6..PD15D0,SD15];
} else {
  when PMS then pd15 := firmware_version
  else pd15 := pd15;
}


"Message Data"
"------------"

declarations
  MSS0..MSS4 node istype 'reg'; "Message Storage State"
  mss = [MSS4..MSS0];
equations

mss.clk = DCK;
mss.aclr = RESET;
when (mss == 0) then {
  when (nrs == nrs_tx_end) then mss := 1 else mss := 0;
}
when (mss > 0) & (mss < 21) then {
  when MDS then mss := mss + 1 else mss := mss;
}
when (mss >= 21) then mss := 0;

MRDY = (mss != 0);

message_data.clk = !DCK;

"We set the message data byte to zero in state zero. We never"
"use this value zero, but we know the value of message data"
"before we begin to store a tracker record."
when (mss == 0) then message_data := 0;

"The first byte of a message is its channel number."
when (mss == 1) then message_data := nid;

"The next two bytes are the high and low bytes of the sixteen-bit"
"data word, which could be an ADC sample, or the upper two bytes"
"of a clock value."
when (mss == 2) then message_data := [NMD15..NMD8];
when (mss == 3) then message_data := [NMD7..NMD0];

"The fourth byte is a timestamp when the message is a data message,"
"but it's the firmware version if the message is a clock message. A"
"clock message always occurs on a timestamp value of zero, so we don't"
"bother writing zero to memory. We write something useful. This firmware"
"version is that of the tracker firmware. The receiver firmware version"
"is, for clock messages, saved in nts, the timestamp byte of the incoming"
"clock message. We will store that later, after the final coil power"
"calibration value."
when (mss == 4) then {
  when (nir == data_transmission) then {
    message_data := nts;
  }
  when (nir != data_transmission) then {
    message_data := firmware_version;
  }
}

"The payload of the message consists of these sixteen bytes. They are the"
"tracker power measurements for a data record. These measurements are shifted"
"through pd1 and so we set the message data to the same pd1 repeatedly. For"
"clock messages we store the coil background power calibration and, as the"
"last byte, the firmware version of the receiver controlling the tracker."
when (mss > 4) & (mss < 21) then {
  when (nir == data_transmission) then {
    message_data := pd1;
  }
  when (nir != data_transmission) then {
    when (mss == 5) then message_data := p1_calib;
    when (mss == 6) then message_data := p2_calib;
    when (mss == 7) then message_data := p3_calib;
    when (mss == 8) then message_data := p4_calib;
    when (mss == 9) then message_data := p5_calib;
    when (mss == 10) then message_data := p6_calib;
    when (mss == 11) then message_data := p7_calib;
    when (mss == 12) then message_data := p8_calib;
    when (mss == 13) then message_data := p9_calib;
    when (mss == 14) then message_data := p10_calib;
    when (mss == 15) then message_data := p11_calib;
    when (mss == 16) then message_data := p12_calib;
    when (mss == 17) then message_data := p13_calib;
    when (mss == 18) then message_data := p14_calib;
    when (mss == 19) then message_data := p15_calib;
    when (mss == 20) then message_data := nts;
  }
}

"We make sure we know what the message data will be for other states."
when (mss >= 21) then message_data := 0;

"We assert Notification Data Stored when we have all the bytes in memory."
NDST = (mss == 21);

"We assert Power Measurement Shift after each power byte storage, so as"
"to shift power values through pd1."
PMS = (mss > 4) & (mss < 21) & MDS;


"Indicators Lamps"
"----------------"

declarations
  PMSL node istype 'reg'; "PMS Latched"
equations

UPLOAD.ap = DS;
UPLOAD.clk = SCK;
UPLOAD := 0;

EMPTY.ap = AE;
EMPTY.clk = SCK;
EMPTY := 0;

PMSL.ap = PMS;
PMSL.clk = SCK;
PMSL := 0;

RECEIVE.aclr = RESET;
RECEIVE.clk = SCK;
RECEIVE := PMSL;


"FRAM Controller"
"---------------"

"The FRAM Controller will allow the ALT to save configuration constants."



"Digital Input-Output"
"--------------------"

"The Digital I/O connections are brought through the Message Detector."
"Here we generate output signals that will be routed through the Message"
"Detector, or accept input signals from the Message Detector."

XEN = 0;
X = 0;


"Test Points"
"-----------"


TP1 = (ts > 0);
TP2 = TBL;
TP3 = Y;

end