"
module P2087A

title 'TCPIP-VME Interface (A2087)'

"Compilation Notes: Be sure to merge the constraints in"
"this file with those in the constraint file A2064F.lci."
"The constraint file tells the compiler to set all I/O to"
"3.3V CMOS, and to use slow slew rate. The slow slew rate"
"causes less interference along the long tracks of the A2087"
"printed circuit board."

"Version 1: [05-AUG-19] Based on P2064F01. This version defines"
"input and output pins, implements a local ram portal with dummy"
"readback data, provides a test configuration where front panel"
"lamps flash, provides hardware and firmware version readback,"
"and ethernet activity lights. No VME read or write implemented.."

"Version 2: [21-AUG-19] Implement writing to address and data"
"buffers. When writing to the address control register, re-arrange"
"the CD0-CD5 lines so that we can write the modifier without"
"flipping the bit order in the controller software. The AM0-AM5"
"lines are connected by the A2087A circuit board to VA5-VA0, which"
"was an oversight. We do not want to propagate this hardware"
"error into the software, so we correct it in firmware. We can now"
"write to all four data buffers, all four address buffers, the"
"address control buffer, and control DS0, DS1, and AS by writing"
"to an internal VME control register. When we read back the VME"
"status register, we get the value of DTACK. The code makes no"
"use of the hardware clock. It operates entirely under the control"
"of the RCM6700 IO signals. We have not yet implemented a bus timeout"
"counter."

declarations
firmware_version_num=2;
test_version=0; "set to 1 to compile the test code"
hardware_id=87; "A2064 hardware identifier"
hardware_version_num=1; "A2087A"

"Connections to the RCM6700"
!LNK pin 21; "Ethernet link from RCM"
CD0..CD7 pin 51, 73, 52, 74, 57, 71, 58, 72 istype 'com'; "Control Data"
CA0..CA5 pin 47, 59, 49, 60, 50, 61; "Control Address"
!CDS pin 24; "control data strobe, unsynchronized"
!CW pin 23; "control write"

"Switches"
!CSW pin 20; "configuration switch"
!RESET pin 5 istype 'com'; "RESET switch input and RESET output"
HV0..HV2 pin 16, 17, 18; "Hardware Version"

"Indicators"
TP1..TP8 pin 7, 8, 9, 10, 11, 12, 14, 15 istype 'com,pos';
BUSY pin 6 istype 'com';
VACT pin 19 istype 'com'; "VME Activity"
EGRN pin 39 istype 'com'; "Ethernet Green, for power"
EYLW pin 38 istype 'com'; "Ethernet Yellow, for activity"

"Clock Inputs and Outputs"
CLK0OUT pin 36 istype 'com';
CLK0IN pin 156;
CLK2OUT pin 70 istype 'com';
CLK2IN pin 68;
CKOUT pin 37 istype 'reg';
CK pin 154;
FCK pin 66; "fast clock input, 80 MHz"

"Internal VME Bus"
VA0..VA7 pin 102, 103, 104, 105, 106, 107, 108, 109 istype 'com'; "vme address bus"
!AEN0..!AEN4 pin 120, 123, 138, 85, 135 istype 'com'; "address latch output enable"
ACK0..ACK4 pin 122, 124, 136, 87, 137 istype 'com'; "address latch clock"
ADIR pin 86; "address latch direction"
ADIRN pin 146; "address latch direction negated"
VD0..VD7 pin 93, 94, 95, 96, 97, 98, 99, 100 istype 'com'; "vme data bus"
!DEN0..!DEN3 pin 151, 162, 82, 80 istype 'com'; "data latch output enable"
DCK0..DCK3 pin 152, 161, 84, 81 istype 'com'; "data latch clock"
DDIR pin 83 istype 'com'; "data latch direction"

"VME Control Lines"
!DS0,!DS1 pin 142, 141 istype 'reg'; "vme data strobe outputs"
!VDS0,!VDS1 pin 159, 160; "vme data strobe inputs"
!DTACK pin 170; "vme data acknowledge"
GQ3 pin 139 istype 'com, pos'; "assert data acknowledge"
!AS pin 145 istype 'reg'; "vme address strobe output"
!VAS pin 158; "vme address strobe input"
!IACKIN pin 127; "vme interrupt acknowledge daisychain input"
!IACKOUT pin 126 istype 'com'; "vme interrupt acknowledge daisychain output"
!IACK pin 164; "interrupt acknowledge cycle input"
GQ2 pin 125 istype 'com, pos'; "assert IACK"
!IRQ7 pin 118; "interrupt request seven"
GQ1 pin 121 istype 'com, pos'; "assert IRQ7"
!BBSY pin 169; "vme bus busy"
GQ7 pin 163 istype 'com, pos'; "assert BBSY"
!BG3IN pin 147; "vme bus grant three input"
!BG3OUT pin 150 istype 'com, pos'; "vme bus grant three output"
!BR3 pin 171; "vme bus request three"
GQ6 pin 140 istype 'com, pos'; "assert BR3"
!BERR pin 165; "vme bus error'
GQ5 pin 149 istype 'com, pos'; "assert BERR"
!SYSRST pin 168; "vme system reset"
GQ4 pin 148 istype 'com, pos'; "assert SYSRST"

"Nodes"
DVA8..DVA31 node istype 'reg,keep'; "driver vme address"
!LDA node istype 'com,keep'; "local data access"
BAZ node istype 'com,keep'; "base address zero"
RCMOE node istype 'com,keep'; "rcm data output enable"
RPD0..RPD7 node istype 'reg'; "ram portal virtual data"
LRSEL node istype 'com,keep'; "local ram select"
LWORD node istype 'reg'; "Long Word (four-byte) cycle"
WRITE node istype 'reg'; "Write cycle"

"Constants"
id_addr=0;  "hardware id location (readback byte)"
hv_addr=18; "hardware version number location (readback byte)"
fv_addr=19; "firmware version number location (readback byte)"
cs_addr=40; "configuration switch address (readback byte)"
dva_addr=42;"driver vme address location (write four bytes)"
actrl_addr=46;"address control register location (write byte)"
vctrl_addr=47; "vme control register (write byte)"
vsts_addr=47; "vme status register (read byte)"
vd_addr=48; "vme data address"
ram_portal_addr=63; "ram portal address location (read byte)"

"Sets"
control_addr=[CA5..CA0]; "control address"
control_data=[CD7..CD0]; "control data"
dva1=[DVA15..DVA8]; "second byte of driver vme address"
dva2=[DVA23..DVA16]; "third byte of driver vme address"
dva3=[DVA31..DVA24]; "fourth byte of driver vme address"
vme_data=[VD7..VD0]; "internal vme data bus"
vme_addr=[VA7..VA0]; "internal vme address bus'
ram_portal_data=[RPD7..RPD0]; "ram portal virtual data"

equations


"Clocks"
"======"

"We divide FCK from 80MHz to 40 MHz. We use CKOUT, which is connected"
"externally to the CK input. So CK s 40 MHz and symmetric."
CKOUT.clk = FCK;
CKOUT := !CKOUT;


"TCPIP Relay to VME Controller Interface"
"======================================="

"We drive the relay's data lines on any control interface read."
RCMOE = !CW & CDS;
control_data.oe = RCMOE;

"Base Address Zero (BAZ) helps our logic compiler. The base address"
"of the target LWDAQ Driver is zero when the top three bytes of the"
"VME address are zero."
BAZ = (dva1==0) & (dva2==0) & (dva3==0);

"We access local data when the BAZ is asserted, or when we read from"
"registers that the VME Controller always provides regardless of"
"base address."
LDA = BAZ 
  # (control_addr==cs_addr)
  # (control_addr==vsts_addr);

"The control_data is equal to the vme_data when the TCPIP relay is"
"reading a remote byte from the VME Controller. When the TCPIP"
"relay is reading a local byte from the VME Controller, the control"
"data will be provided by the VME Controller itself. We clock VSEL"
"on the falling edge of CK because CDS and LDA are clocked on the"
"rising edge."
when !LDA then control_data=vme_data.pin;

"We drive the VME data lines on any Control Interface write. The"
"VME data is always equal to the control data."
vme_data.oe = CW;
vme_data=control_data.pin;

"We drive the VME address lines during a control write. The VME"
"address is equal to the control bus except when we are writing"
"to the address control register, in which case we correct an"
"error in the ordering of the address modifier bits by re-arranging"
"the control data bits."
vme_addr.oe = CW;
when (control_addr!=actrl_addr) then {
  vme_addr=control_data.pin;
} else {
  vme_addr=[CD7,CD6,CD0,CD1,CD2,CD3,CD4,CD5].pin;
}

"We access the local ram portal when we read from the ram"
"portal address with the local base address."
LRSEL = !CW & LDA & (control_addr==ram_portal_addr);
when LRSEL then control_data=ram_portal_data;


"VME Address Registers"
"====================="

"The driver vme address consists of four bytes. All four bytes we store in"
"the SN74ABT646 address buffers (U7-U10) whenever the controller writes to"
"them. The top three bytes are also held in registers within the VME Controller"
"(U1) so that we can distinguish between local register access, such as reading"
"the firmware version from the VME Controller itself, and remote access, such"
"as reading the firware version from a VME Driver in the VME crate."

"The lowest byte of the address is stored only in U4. The VME byte ordering"
"is little-endian, so we adhere to this ordering in the VME Controller address"
"space. The highest address is the least significant byte."
when CW & (control_addr==dva_addr+3) then {
  ACK0=!CDS;
} else {
  ACK0=1;
}

"The address byte one we store in a register as well as in U3."
dva1.clk=!CDS;
dva1.aclr=RESET;
when CW & (control_addr==dva_addr+2) then {
  dva1:=control_data.pin;
  ACK1=!CDS;
} else {
  dva1:=dva1.fb;
  ACK1=1;
}

"The address byte two we store in a register as well as in U6."
dva2.clk=!CDS;
dva2.aclr=RESET;
when CW & (control_addr==dva_addr+1) then {
  dva2:=control_data.pin;
  ACK2=!CDS;
} else {
  dva2:=dva2.fb;
  ACK2=1;
}

"The address byte three we store in a register as well as in U5."
dva3.clk=!CDS;
dva3.aclr=RESET;
when CW & (control_addr==dva_addr+0) then {
  dva3:=control_data.pin;
  ACK3=!CDS;
} else {
  dva3:=dva3.fb;
  ACK3=1;
}

"The address control register presents the VME address modifier to"
"the VME bus, as well as the !WRITE and !LWORD control signals. The"
"A2087A layout has the AM5 bit connected to VA0, while AM0 is connected"
"to VA5. We would like to set AM5 with CD5 and AM0 with AM0, so we have"
"rearranged the correspondance between VA and CD for address control"
"access. After re-arrangement we have:"

" !LWORD = CD7 (the !LWORD signal is set directly by CD7)
" !WRITE = CD6 (the !WRITE signal is set directly by CD6)
" AM5    = CD5
" AM4    = CD4
" AM3    = CD3
" AM2    = CD2
" AM1    = CD1
" AM0    = CD0

"We store the LWORD and WRITE bits separately so we can use them to"
"control which data buffer outputs to enable."
LWORD.clk=!CDS;
WRITE.clk=!CDS;
LWORD.aclr=RESET;
WRITE.aclr=RESET;
when CW & (control_addr==actrl_addr) then {
  LWORD := !CD7;
  WRITE := !CD6;
  ACK4 = !CDS;
} else {
  LWORD := LWORD.fb;
  WRITE := WRITE.fb;
  ACK4 = 1;
}

"The four VME Address buffers U3-U6 always drive the VME address"
"bus. This version of the VME-TCPIP Interface is always the master"
"The direction of the buffers is side A to side B, and the side B"
"receives the values stored in the A-side registers."
ADIR=1; "set buffer direction A->B"
ADIRN=!ADIR; "negated version of ADIR for use by DS0, DS1, and AS"
AEN0=1; "always drive A0-A7"
AEN1=1; "always drive A8-A15"
AEN2=1; "always drive A16-A23"
AEN3=1; "always drive A24-A31"
AEN4=1; "always drive AM5-AM0. !WRITE, !LWORD"


"VME Data Buffers"
"================"

when CW & (control_addr==vd_addr+3) then {
  DCK0=!CDS;
} else {
  DCK0=1;
}

when CW & (control_addr==vd_addr+2) then {
  DCK1=!CDS;
} else {
  DCK1=1;
}

when CW & (control_addr==vd_addr+1) then {
  DCK2=!CDS;
} else {
  DCK2=1;
}

when CW & (control_addr==vd_addr+0) then {
  DCK3=!CDS;
} else {
  DCK3=1;
}

[DS1,DS0,AS].clk=!CDS;
[DS1,DS0,AS].aclr=RESET;
when CW & (control_addr==vctrl_addr) then {
  DS0:=CD0;
  DS1:=CD1;
  AS:=CD2;
} else {
  DS0:=DS0.fb;
  DS1:=DS1.fb;
  AS:=AS.fb;
}


"We enable the VME data buffers on write cycles, and set the"
"direction of the buffers to A->B. We enable the lower two"
"data buffers on all write cycles, and the upper two only on"
"long-word write cycles."
when WRITE then {
  DDIR=1;
  DEN0=1;
  DEN1=1;
  DEN2=LWORD;
  DEN3=LWORD;
} else {
"We enable only the selected VME data buffers on read cycles, but"
"only when they are selected by the control address and strobed by"
"the TCPIP relay with CDS. The VME data buffer direction is set to"
"B->A."
  DDIR=0;
  when CDS & !CW & (control_addr==vd_addr+3) then DEN0=1 else DEN0=0;
  when CDS & !CW & (control_addr==vd_addr+2) then DEN1=1 else DEN1=0;
  when CDS & !CW & (control_addr==vd_addr+1) then DEN2=1 else DEN2=0;
  when CDS & !CW & (control_addr==vd_addr+0) then DEN3=1 else DEN3=0;
}


"Interrupt Handling"
"=================="

"We do not assert IACK."
GQ2=0;

"We do not request an interrupt, so we don't assert IRQ7.'
GQ1=0;

"We forward IACKIN to IACKOUT."
IACKOUT=IACKIN;


"Bus Arbitration"
"==============="

"We assume we are the only master. Assert BBSY."
GQ7=1;

"We will implement bus error later. For now, don't assert it.'
GQ5=0;

"We forward BG3IN to BG3OUT."
BG3OUT=BG3IN;

"We do not assert bus request three BR3."
GQ6=0;

"We do not assert DTACK, but leave that to slave devices."
GQ3=0;


"Readback Registers"
"================="

"We read the configuration switch from the local address"
"space during any reboot of the TCPIP relay. The LDA flag"
"will always be asserted when the controll address points"
"to this register."
when LDA & !CW & (control_addr==cs_addr) then 
  control_data=[0,0,0,0,0,0,0,!CSW];

"We read the vme status register from the VME Controller"
"regardless of the base address. The LDA flag will always"
"be asserted when the controll address points to this register."
when LDA & !CW & (control_addr==vsts_addr) then
  control_data=[0,0,0,0,0,0,0,DTACK];

"The firmware verion, hardware id, and hardware version"
"will be read from the VME Controller only if the base"
when LDA & !CW & (control_addr==fv_addr) then 
  control_data=firmware_version_num;
when LDA & !CW & (control_addr==id_addr) then
  control_data=hardware_id;
when LDA & !CW & (control_addr==hv_addr) then
  control_data=hardware_version_num;


"The local ram portal provides virtual data for"
"a local ram portal. Every time we read from the"
"local ram portal at address ram_portal_addr, the"
"data increments by one."
ram_portal_data.aclr = RESET;
ram_portal_data.clk = !CDS;
when LRSEL then {
  ram_portal_data := ram_portal_data + 1;
} else {
  ram_portal_data := ram_portal_data.fb;
}

 
"LEDs"
"===="

"We always turn on the green RJ-45 socket light to show power."
EGRN=1;

"We flash the yellow RJ-45 light with activity."
EYLW=LNK;

"The LEDs provide diagnostic signals. The test point"
"LEDs have pins on the board you can use with a scope"
"probe."
when (!CSW # !RESET) & (test_version!=1) then {
  BUSY = CDS;
  VACT = DS0 # DS1
  TP1 = CSW;
  TP2 = VD0;
  TP3 = CD0;
  TP4 = CW;
  TP5 = CDS;
  TP6 = DS0 # DS1;
  TP7 = CDS & (control_addr==ram_portal_addr);
  TP8 = DTACK;
} 

declarations
T24..T0 node istype 'reg'; "timer"
timer=[T24..T0]; "timer"
countbits=[T24..T21]; "top timer bits"
equations

"The test firmware creates a moving spot of light"
"on the front panel LEDs. The spot changes direction"
"when you press the configuration switch, and stops"
"when you press reset."

"Because this timer just counts up, we can define it"
"as one counter and trust that the compiler will figure"
"out that it can implement the counter with T-type"
"flip-flops and no fancy logic."
timer.clk=CK;
timer.aclr=RESET;
timer:=timer+1;

"We use the counter top four bits to drive the LEDs."
when !CSW & (test_version==1) then {
  BUSY=(countbits==13);
  VACT=(countbits==12);
  TP1=(countbits==11);
  TP2=(countbits==10);
  TP3=(countbits==9);
  TP4=(countbits==8);
  TP5=(countbits==7);
  TP6=(countbits==6);
  TP7=(countbits==5);
  TP8=(countbits==4);
}

"We change direction when we press the CSW switch."
when CSW & (test_version==1) then {
  BUSY=(countbits==6);
  VACT=(countbits==7);
  TP1=(countbits==8);
  TP2=(countbits==9);
  TP3=(countbits==10);
  TP4=(countbits==11);
  TP5=(countbits==12);
  TP6=(countbits==13);
  TP7=(countbits==14);
  TP8=(countbits==15);
}

end