"
module P2080A

title 'P2080A'

"Version 1:"

"[15-DEC-16] Start with P2082A03 and change pin numbers. The LEDs may be operated"
"in one of four modes, as set by DC2..DC1. When the mode is zero, the LEDs are all"
"off. When mode is one, a single LED is set to full power as selected by DC16..DC9."
"When mode is two, a single LED is set to three-quarter power as selected by the"
"bits DC16..DC9. When mode is three, all LEDs are turned on to ten percent power."

"Version 2:"

"[03-FEB-17] Add new graduations of intensity and eliminate the all-LED command for"
"now."

"Version 3:

"[15-FEB-17] Now have power levels 0-10 with uniform increase in duty cycle."

"Version 4:"

"[03-APR-17] We fix vulnerability to commands with bottom nibble equal to zero."

"Version 5:"

"[07-FEB-18] We disable power levels higher than 7 to avoid damaging LEDs in strong"
"magnetic fields."

"Version 6:"

"[27-MAY-18] Assign source element numbers to the S1-S36 control lines so as to"
"harmonize them with the grid naming system on the A2080B chassis. We now have A1-A8"
"are element numbers 1-8, B1-B8 are element numbers 17-24, C1-C10 are 33-42, D1-10 are"
"49-58. This leaves element number zero as a dummy no-flash element."

"[01-AUG-24] Fix outdated comments."

"Version 7:"

"[02-AUG-24] Change power modes 1-6 to provide lower power. Mode 1 asserts the"
"switching signal for only 18 ns, when the mosfet takes 100 ns to turn on fully."
"This mode provides a dim output that is useful in applications where the"
"higher power settings are too bright."

"[16-AUG-24] Slow down switching frequency for power mode = 1 so as to decrease"
"brightness even further."


declarations

"Constants"
da_delay = 7; "CK periods to DA"
dda_delay = 16; "CK periods to DDA"
run_time = dda_delay+3; "CK periods to run"

"Inputs"
A pin 64; "LVDS Input"

"Outputs"
B pin 69 istype 'com'; "LVDS Output"
LB pin 72 istype 'com'; "Loop Back"			
WAKE pin 48 istype 'com'; "Wake"
TP1..TP3 pin 59, 60, 54 istype 'com';
S1..S36 pin 58, 55, 53, 61, 16, 17, 19, 15, 21, 22,
  28, 20, 30, 31, 34, 29, 41, 44, 47, 36, 
  66, 87, 84, 81, 91, 100, 97, 94, 11, 8, 
  9, 10, 6, 3, 4, 5 istype 'com';

"Command Receiver Nodes"
SA node istype 'reg'; "Synchronized A"
DSA node istype 'reg'; "Delayed SA"
DA node istype 'reg'; "Delayed A Rising Edge"
DDA node istype 'reg'; "Delayed DA"
AA node istype 'reg'; "Address Active"
CA node istype 'reg'; "Command Active"
ER,Q1..Q16 node istype 'reg'; "Receiver Bits"
LT4..LT0 node istype 'reg'; "LWDAQ Timer"
lt = [LT4..LT0];
DS node istype 'com'; "Data Strobe"
DC1..DC16 node istype 'reg';"Device Command Bits"
DA0..DA15 node istype 'reg';"Device Address Bits"

"Ring Oscillator Notes"
RO1,RO2 node istype 'com,keep'; "Ring Oscillator"
CK node istype 'reg,keep'; "Clock"
RUN node istype 'reg,keep';
equations 


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

"The RUN flag controls the ring oscillator. When the ring"
"oscillator runs, it causes lt to increment. When lt reaches"
"a threshold, we clear the RUN flag, which stops the ring"
"oscillator."
RUN.aclr = (lt == run_time);
RUN := 1;
RUN.clk = A;

"Here we generate the clock we use in the LWDAQ receiver with"
"a two-gate ring oscillator. The oscillator runs at around"
"140 MHz, which we divide by two to obtain our symmetric CK"
"signal at 70 MHz."
RO1 = RO2;
RO2 = !RO1 & RUN;
CK.clk = RO1;
CK:=!CK;

"We synchronize A with DCK, and provide a delayed"
"version of A that allows us to detect edges."
[SA,DSA].clk = CK;
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 = CK;
lt.aclr = !RUN;
lt := lt+1;
DA.clk = !CK;
DA := (lt==da_delay);
DDA.clk = !CK;
DDA := (lt==dda_delay);

"The command or address bits enter a sixteen-bit shift register."
[ER,Q1..Q16].clk = DA;
[ER,Q1..Q16] := [SA,ER,Q1..Q15];

"Address Active, or AA, provides a pulse that begins with DDA"
"on the start bit of an address transmission, and ends with DDA"
"on the stop bit of an address transmission. We clock the receiver"
"bits into the address register on a rising edge of AA."
AA.clk = DDA;
AA := (!AA & !CA & !SA & !ER) # (AA & !SA);
[DA0..DA15].clk = !AA;
[DA0..DA15] := [Q1..Q16];

"Command Active, or CA, provides a pulse that begins with DDA"
"on the start bit of a command transmission, and ends with DDA"
"on the stop bit of a command transmission. We clock the receiver"
"bits into the command register on a rising edge of CA."
CA.clk = DDA;
CA := (!AA & !CA & !SA & ER) # (CA & !SA);
[DC1..DC16].clk = !CA;
[DC1..DC16] := [Q1..Q16];

"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;


"Command Bit Allocation"
"----------------------"

"WAKE bit."
WAKE = DC8;

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

"When the loopback bit is set, we send B back to A. When we"
"implement a data device, we will allow B to take on values"
"generated by a data transmitter state machine. For now, we"
"just set B = A always."
when LB then {
  B = A
} else {
  B = A
}

declarations
mode = [DC4..DC1];
select = [DC16..DC9];
equations


"Switching Signal Clock"
"----------------------"

"We need a 150-kHz switching signal with roughly 7% duty cycle for"
"our A2080A/B buck converters. We start with a ring oscillator and"
"divide down to 150 kHz. We use the divider count to set the duty"
"cycle according to the power mode, which is given by DC1..DC4. Our"
"value for end_7 sets the switching signal to give 7% duty cycle."
"The buck converter mosfet turns on in 100 ns and turns off in 200ns,"
"so the time for which we assert our switching signal will be less"
"than the duty cycle of the buck converter. Here we have 150 kHz with"
"period 6.7 us derived from a ring oscillator of 54 MHz. We specify"
"the assertion of the switching signal in units of 54-MHz periods,"
"which are 18 ns. We want 7% duty cycle, so the mosfet should be on"
"for 0.47 us. Subtract 100 ns to get the switching signal time, and"
"we have 0.37 us, which is 21 periods of 54 MHz. Hence end_7 below"
"is given as 21. And this value does indeed provide 7% duty cycle."

"For very low power output, we slow down the switching signal from"
"150 kHz to 26 kHz, and generate the shortest possible pulse on the"
"switching output."

"It takes 100 ns for the mosfet to turn on fully, so applying our"
"switching signal for less than 100 ns provides us with much lower"
"power output. The lower power modes are designed to provide"
"dim lighting for applications where the injector is too bright."

declarations
SR0..SR4 node istype 'com,keep'; "Switching Ring"
SD0..SD10 node istype 'reg'; "Switching Divider"
sd = [SD10..SD0];
end_0 = 0;
end_1 = 1;
end_2 = 2;
end_3 = 3;
end_4 = 5;
end_5 = 11;
end_6 = 15;
end_7 = 21;
period_end = 360;
SCK node istype 'com,keep'; "Switching Clock"
equations

"The ring oscillator consists of five gates. It runs at about"
"55 MHz."
SR4 = SR3 & (mode != 0);
SR3 = SR2;
SR2 = SR1;
SR1 = SR0;
SR0 = !SR4;

"Divide the ring oscillator to make 150 kHz."
sd.aclr = CA;
sd.clk = SR2;
when (mode == 1) then {
  sd := sd + 1;
} else {
  when (sd == period_end) then {
    sd := 0;
  } else {
    sd := sd + 1;
  }
}

"The end constants give us the increasing duty cycle of our switching"
"waveform, from mode one to seven."
SCK = (sd > 0)
  & ((sd <= end_1) & (mode == 1)
  # (sd <= end_2) & (mode == 2)
  # (sd <= end_3) & (mode == 3)
  # (sd <= end_4) & (mode == 4)
  # (sd <= end_5) & (mode == 5)
  # (sd <= end_6) & (mode == 6)
  # (sd <= end_7) & (mode == 7));


"Switching Signals"
"-----------------"

"We have the following mapping between the S1-S36 control lines, which are named"
"on the schematic and circuit board, and element numbers given by the top byte"
"of the device command. This mapping is constrained by the chassis of the A2080B"
"and is indended to be versatile enough to be used in future multi-source devices."
S1 = (select == 49) & SCK;
S2 = (select == 50) & SCK;
S3 = (select == 34) & SCK;
S4 = (select == 33) & SCK;
S5 = (select == 51) & SCK;
S6 = (select == 52) & SCK;
S7 = (select == 36) & SCK;
S8 = (select == 35) & SCK;
S9 = (select == 53) & SCK;
S10 = (select == 54) & SCK;
S11 = (select == 38) & SCK;
S12 = (select == 37) & SCK;
S13 = (select == 55) & SCK;
S14 = (select == 56) & SCK;
S15 = (select == 40) & SCK;
S16 = (select == 39) & SCK;
S17 = (select == 57) & SCK;
S18 = (select == 58) & SCK;
S19 = (select == 42) & SCK;
S20 = (select == 41) & SCK;
S21 = (select == 17) & SCK;
S22 = (select == 18) & SCK;
S23 = (select == 2) & SCK;
S24 = (select == 1) & SCK;
S25 = (select == 19) & SCK;
S26 = (select == 20) & SCK;
S27 = (select == 4) & SCK;
S28 = (select == 3) & SCK;
S29 = (select == 21) & SCK;
S30 = (select == 22) & SCK;
S31 = (select == 6) & SCK;
S32 = (select == 5) & SCK;
S33 = (select == 23) & SCK;
S34 = (select == 24) & SCK;
S35 = (select == 8) & SCK;
S36 = (select == 7) & SCK;


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

TP1 = sd == 0;
TP2 = SCK;
TP3 = CA;

end