"
"
module P2060

title 'P2060'

"(C) Ben Wiener 2011"
"(C) Kevan Hashemi 2011"
"(C) Murray Lamb 2011"

declarations

"Version 4"

"[19-SEP-11] We run the pulse timing off a new 100 Hz clock."
"All our counters, other than the pulse clock counter, are"
"nine bits or less, and the pulse counter can be made out of"
"T-type flip-flops. Thus we save roughly fifty registers and"
"we simplify the counter code."

"[14-SEP-11] The controller now has three outputs to drive"
"the x-ray head: SETUP, ACTIVATE, and KV. The dental x-ray"
"source needs SETUP to be asserted at least half a second"
"before ACTIVATE. We believe the SETUP signal starts heating"
"up the cathode. If we assert SETUP at the same time as ACTIVATE,"
"the source fails after a few dozen shots. Thus we cannot use the"
"LWDAQ flash job to time our x-ray exposures, because any exposure,"
"no matter how short, must be preceeded by the SETUP period. We now"
"control the head by writing a single device command, the top eight"
"bits (DC16..DC9) of which give us a multiple of 10 ms that sets the"
"ACTIVATE period. The DC1 bit sets KV directly, and selects 70 keV"
"for the current activation when HI and 60 keV when LO. At the end"
"of each activation period, SETUP remains asserted for 0.25 s as well,"
"which is something we observed in the manufacturer's power supply."

"Version 3"

"[02-JUN-11] We now pulse the x-ray source when either"
"DC1 or DC2 are set. When we receive DC1, we set KV to"
"zero to select 60 keV. When we receive DC2, we set KV"
"to one to select 70 keV. The KV setting remains in place"
"until the next command is received."

"Version 2"

"[01-JUN-11] Establish the count-up and count-down to"
"provide guaranteed limit to the duty cycle of the x-ray"
"pulse."

"Constants"
pulse_clock_period=400000; "CK Frequency / PC Frequency"
max_time=300; "Maximum x-ray pulse duration, PC periods"
rest_ratio=32; "Min ratio of rest to active for x-ray source"
setup_wait=60; "In PC periods"
setup_end=25; "In PC periods"

"Pins"
A pin 12;"LVDS input"
B pin 64 istype 'com';"LVDS output"
!RESET pin 37;"RESET"           
CK pin 38;"40 MHz clock input"
WAKE pin 66 istype 'com';"WAKE"
LB pin 65 istype 'com';"Loop Back, enables LVDS driver"
TP1..TP4 pin 69,70,71,72 istype 'com';"
LED16..LED1 pin 41..44,47..50,53..56,58..61 istype 'com';
IO4..IO10 pin 6,8,9,10,11,14,15 istype 'com';
SETUP pin 3 istype 'com'; "X-Ray Setup for Pulse"
ACTIVATE pin 4 istype 'com'; "X-Ray Pulse On"
KV pin 5 istype 'com'; "Beam Energy 70 kV"

"Nodes"               
SA node istype 'reg';"Synchornized A."
DA node istype 'com';"Delayed SA"
DDA node istype 'com';"Delayed Delayed SA"
ACTIVE node istype 'reg';"transmission active"
D1..D9 node istype 'reg';"delay pipeline."
DRS0..DRS2 node istype 'reg';"Command Receiver State"
CS node istype 'com';"Command Strobe"
NCS node istype 'com';"New Command Strobe"
AS node istype 'com';"Address Strobe"
NAS node istype 'com';"New Address Strobe"
DS node istype 'com';"Data Strobe"
ER,Q1..Q16 node istype 'reg';"Receiver Bits"
DA0..DA15 node istype 'reg';"Device Address Bits"
DC1..DC16 node istype 'reg';"Device Command Bits"

"Sets"
drs=[DRS2..DRS0];"Command Receiver State"

equations


"Serial Input Decoding"
"---------------------"

"We synchronize the incoming LVDS logic signal to"
"our 40-MHz clock."
SA:=A;
SA.clk=CK;

"We put SA through a pipeline of registers clocked"
"with CK so that we can generate the delayed timing"
"pulses from the rising edge of SA."
[D1..D9]:= [SA,D1..D8];
[D1..D9].clk= CK;
[D1..D9].aclr=RESET;

"Delayed SA provides is asserted for one CK period 125 ns"
"after any rising edge of SA. We use DA and SA to obtain"
"the LWDAQ data bit. If SA is high with DA, the data bit"
"is one."
DA = D4 & !D5;

"Delayed Delayed SA is asserted for one CK period 250 ns"
"after any rising edge of SA. We use DDA and SA to obtain"
"the LWDAQ ACTIVE bit. If SA is low with DDA, ACTIVE is"
"true."
DDA = D8 & !D9;

"We assert ACTIVE whenever the driver is transmitting"
"a command data bit."
ACTIVE.clk=CK;
ACTIVE.aclr=RESET;
when !SA & DDA then ACTIVE:=1;
when SA & DDA then ACTIVE:=0;
when !DDA then ACTIVE:=ACTIVE;

"We clock the receiver shift register and the entry"
"register with CK, and we clear them on RESET."
[ER,Q1..Q16].clk=CK;
[ER,Q1..Q16].aclr=RESET;

"We shift the receiver bits whenever we have DA"
"asserted. We clock the current value of SA into"
"the entry register (ER), and shift all the other"
"bits over by one. At the beginning of a LWDAQ"
"transmission, ER contains a 1 if the transmission"
"is a command, and 0 if it is an address. At the"
"end of a transmission, ER contains a 1, and Q1"
"to Q16 contain the transmitted sixteen-bit word,"
"either address or command."
when DA then [ER,Q1..Q16]:=[SA,ER,Q1..Q15];
else [ER,Q1..Q16]:=[ER,Q1..Q16].fb;


"Data Receiver"
"-------------"

"Data Receiver states."
declarations

rest=0;
command_receive=1;
clock_command_register=2;
address_receive=3;
clock_address_register=4;
new_command_strobe=5;
new_address_strobe=6;

equations

drs.clk=CK;
drs.aclr=RESET;

state_diagram drs;
  "Stay in the rest state until we receive ACTIVE."
  "When ACTIVE, we proceed with command receive"
  "if the data bit in ER is one, otherwise an address"
  "receive."
  state rest:
    if ACTIVE then {
      if ER then command_receive
      else address_receive;
   } else rest;

  "We stay in command_receive until !ACTIVE."
  state command_receive:
    if !ACTIVE then clock_command_register
    else command_receive;

  "As we pass through clock_command_register we"
  "indicate that it is time to clock the receiver"
  "bits into the command register."
  state clock_command_register:goto new_command_strobe;

  "As we pass through new_command_strobe we start"
  "any action that should be taken when the new command"
  "arrives. The variable NCS is true when drs is in"
  "the new_command_strobe state."
  state new_command_strobe:goto rest;

  "We stay in address_receive until !ACTIVE."
  state address_receive:
    if !ACTIVE then clock_address_register;
    else address_receive ;

  "As we pass through clock_address_register we"
  "indicate that it is time to clock the receiver"
  "bits into the address register."    
  state clock_address_register:goto new_address_strobe;   

  "As we pass through new_address_strobe we start"
  "any action that should be taken when the new address"
  "arrives. The variable NAS is true when drs is in"
  "the new_address_strobe state."
  state new_address_strobe:goto rest;
equations


"Command Strobe is asserted for one CK period at the"
"end of a command transmission from the LWDAQ driver."
CS = (drs==clock_command_register);

"New Command Strobe follows CS when the new command"
"is already established in the DC registers."
NCS = (drs==new_command_strobe);

"Address Strobe is asserted for one CK period at the"
"end of an address transmission from the LWDAQ driver."
AS = (drs==clock_address_register);

"New Address Strobe follows AS when the new address"
"is already established in the DA registers."
NAS = (drs==new_address_strobe);

"Data Strobe is asserted for one CK period after a"
"solitary low pulse from the LWDAQ driver. The driver"
"uses solitary low pulses that endure for 125 ns and"
"are followed by a > 375 ns high pulse to initiate"
"serial transmission of data from LWDAQ devices of type"
"data_device, like this one."
DS = (SA & DDA & (drs==rest));

"We clock the receiver bits into the command register"
"on CS."
[DC1..DC16].clk=CK;
[DC1..DC16].aclr=RESET;
when CS then [DC1..DC16]:=[Q1..Q16]
else [DC1..DC16]:=[DC1..DC16];

"We clock the receiver bits into the address register"
"on AS."
[DA0..DA15].clk=CK;
[DA0..DA15].aclr=RESET;
when AS then [DA0..DA15]:=[Q1..Q16]
else [DA0..DA15]:=[DA0..DA15].fb;

"This board is always awake, so we don't use DC8."
WAKE=1;

"This board always loops back, so we don't use DC7.."
LB = 1;

"Loop back A to B for loopback job."
B=A;



"Pulse Clock"
"==========="

declarations
PCC0..PCC18 node istype 'reg'; "Pulse Clock Counter"
PCK node istype 'reg'; "Pulse Clock"
PCR node istype 'reg'; "Pulse Clock Reset"
pcc=[PCC18..PCC0];
equations

"The pulse clock produces a 100 Hz square wave, for a 10-ms timing"
"resolution. We use it to clock the x-ray controller and its associated"
"timers. Because it is always counting, we can declare it as a simple"
"eighteen-bit counter and trust that the compiler will know how to"
"turn it into a compact set of T-type registers. We reset the clock"
"whenever it reaches pulse clock period, and generate a pulse clock"
"pulse at that time also."
pcc.clk = CK;
pcc.aclr = PCR;
pcc:=pcc+1;
PCR.clk = CK;
PCR := (pcc ==  pulse_clock_period);
PCK.clk = CK;
PCK := (pcc <= pulse_clock_period / 2);

"Safety Counter"
"=============="

declarations
RCC0..RCC7 node istype 'reg'; "Resting Clock Counter"
RCK node istype 'reg';"Resting Clock"
SCCK node istype 'com';"Safety Counter Clock"
rcc=[RCC7..RCC0];
SC0..SC8 node istype 'reg';"Safety Counter"
sc=[SC8..SC0];
ABORT node istype 'com'; "Abort Activation"
equations

"The resting clock substitutes for PCK to count down the safety"
"counter when the x-ray source is resting."
rcc.clk=PCK;
when rcc==rest_ratio then rcc:=0 else rcc:=rcc+1;
RCK:=(rcc==0);
RCK.clk=PCK;

"The safety counter clock is either PCK or RCK. We count up fast when the"
"x-ray source is active and count down slow when the source is resting."
when ACTIVATE then SCCK=PCK else SCCK=RCK;

"The safety counter is clocked by SCCK"
sc.clk=SCCK;
sc.aclr=RESET;

"When the x-ray source is powered, we count up"
"by incrementing the safety counter by 1."
when ACTIVATE then {
  sc:=sc+1
} else {
"When the x-ray source is turned off, we count down"
"to zero by decrementing the counter by 1."
  when sc!=0 then {
    sc:=sc-1;
  } else {
    sc:=0;
  }
}

"We assert ABORT when the safety counter is greater than"
"the off-time. We use stop to abort the activation of the"
"x-ray source by the x-ray controller."
ABORT = sc>max_time;


"X-Ray Pulse Generator"
"====================="


declarations
XT0..XT8 node istype 'reg';"X-Ray Timer"
XTR node istype 'reg'; "X-Ray Timer Reset"
xt=[XT8..XT0];
EW,EA,ET node istype 'com,keep'; "End Flags"
XCS3..XCS0 node istype 'reg'; "X-Ray Controller State"
xcs=[XCS3..XCS0];
SStart = 0;
SSetup_Wait = 1;
SReset_1 = 2;
SActivate = 3;
SReset_2 = 4;
SStop = 5;
pulse_length = [0,DC16..DC9];
equations

"End Warmup lets the controller know when the cathode warm-up is done."
"This should be around half a second after we assert SETUP."
EW = (xt == setup_wait);

"End Activate lets the controllerknow that the pulse time requested"
"by the user has finished."
EA = (xt == pulse_length);

"End Termination lets the controller know that the post-pulse termination"
"period, during which we keep the cathode hot, is finished."
ET = (xt == setup_end);

"State machine provides warm-up and termination periods for the SETUP"
"output, and inserts the ACTIVATE pulse in the middle. If the safety"
"counter cuts short the pulse, the x-ray control recognises this with"
"the ABORT signal."
xcs.clk=CK;
xcs.aclr=RESET;
state_diagram xcs
state SStart: if (pulse_length!=0) & NCS then SSetup_Wait else SStart;
state SSetup_Wait: if EW then SReset_1 else SSetup_Wait;
state SReset_1: goto SActivate;
state SActivate: if EA # ABORT then SReset_2 else SActivate;
state SReset_2: goto SStop;
state SStop: if ET then SStart else SStop;
equations

"The x-ray controller timing is performed with the help of the"
"x-ray timer, which we clock with the pulse clock."
xt.clk=PCK;
xt.aclr=XTR;
xt:=xt+1;
XTR := (xcs==SStart) # (xcs==SReset_1) # (xcs==SReset_2);
XTR.clk = !CK;


"Outputs"
"======="

"The SETUP output is always asserted during an exposure, regardless"
"of the safety counter.
SETUP = (xcs!=SStart);

"The ACTIVATE output turns on the x-ray high-voltage power."
ACTIVATE = (xcs==SActivate);

"We set KV with DC1. When zero, the X-ray voltage will"
"be 60 keV, when 1 it will be 70 keV."
KV = DC1;

"Test indicators."
LED1 = SETUP;
LED2 = ACTIVATE;
LED3 = KV;
LED4 = PCK;
LED5 = NCS;
LED6 = 0;
LED7 = 0;
[LED8..LED16]=[SC8..SC0];
TP1 = (xcs==SStart);
TP2 = (xcs==SSetup_Wait);
TP3 = (xcs==SActivate);
TP4 = (xcs==SStop);


end