"
module P2087A title 'TCPIP-VME Interface (A2087)' "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: [29-AUG-19] Implement writing to address and data" "buffers." "Version 3: We array the address and buffer registers in a local" "address space that the TCPIP Interface can access directly with" "an IO cycle. We add a VME status and control register, which upon" "writing sets the address strobe and the data strobes, and upon" "reading reports the state of DTACK and BERR. We implement a bus" "error timer. To present the hardware version, firmware version," "and hardware ID to the LWDAQ software, we implement VME slave" "registers that are read out when the base address is zero. We" "assume that the TCIPI Interface will capture LWDAQ writes to" "base address registers that in former TCPIP-VME interfaces" "resided in the slave VME address space. This firmware supports" "single-byte, double-byte, and quadruple-byte VME cycles, as" "well as 16-bit, 24-bit, and 32-bit address cycles, by the simple" "tactic of allowing all details of the VME cycle to be administered" "by the TCPIP Interface." "Version 4: Add CSW to test pins, improve test pins to they now" "show data going in and out of the VME crate more clearly." declarations firmware_version_num=4; test_version=0; "set to 1 to compile the test code" hardware_id=87; "A2064 hardware identifier" hardware_version_num=1; "A2087A" "TCPIP Interface (RCM6700) to VME Controller (LC4512V)" !LNK pin 21; "Ethernet link detected by TCPIP Relay" CD0..CD7 pin 51, 73, 52, 74, 57, 71, 58, 72 istype 'com,pos'; "Controller Data" CA0..CA5 pin 47, 59, 49, 60, 50, 61; "Controller Address" !CDS pin 24; "Controller Data Strobe" !CW pin 23; "Controller 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" "VME Controller (LC4512V) to VME Registers (SN74ABT646A)" 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 Controller (LC4512V) to VME Backplane" !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" BA0..BA31 node istype 'reg,keep'; "base address" BAZ node istype 'com,keep'; "base address zero" RPD0..RPD7 node istype 'reg'; "ram portal virtual data" LWORD node istype 'reg'; "Long Word (four-byte) cycle" WRITE node istype 'reg'; "Write cycle" BT0..BT7 node istype 'reg'; "Bus Timer bits" "VME Controller Local Address Space" cs_addr=40; "configuration switch location (read byte)" ar3_addr=42; "address register three location (write byte)" ar2_addr=43; "address register two location (write byte)" ar1_addr=44; "address register one location (write byte)" ar0_addr=45; "address register zero location (write byte)" acr_addr=46;"address control register location (write byte)" vscr_addr=47; "vme status and control register location (read-write byte)" dr3_addr=48; "data register three location (read-write byte)" dr2_addr=49; "data register two location (read-write byte)" dr1_addr=50; "data register one location (read-write byte)" dr0_addr=51; "data register zero location (read-write byte)" "VME Controller Backplane Address Space" id_addr=0; "hardware id location (read byte)" hv_addr=18; "hardware version location (read byte)" fv_addr=19; "firmware version location (read byte)" rp_addr=63; "ram portal address location (read byte)" "Bus Timer Constants" self_dtack_ticks=4; "25-ns ticks until local DTACK" bus_error_ticks=40; "25-ns ticks until backplane BERR" bt_done=^hff; "done state for bus timer" "Sets" control_addr=[CA5..CA0]; "control address" control_data=[CD7..CD0]; "control data" ar0=[BA7..BA0]; "vme address byte zero" ba1=[BA15..BA8]; "base address byte one" ba2=[BA23..BA16]; "base address byte two" ba3=[BA31..BA24]; "base address byte three" vrdb=[VD7..VD0]; "vme register data bus" vrab=[VA7..VA0]; "vme register address bus' rpdb=[RPD7..RPD0]; "ram portal data bus" bt=[BT7..BT0]; "bus timer" 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" "=======================================" "Base Address Zero (BAZ) is an intermediate signal that helps our logic" "determine when the base address is pointing to the local VME Controller" "or to an external VME Slave. The base address is zero when the top three" "bytes of the VME address are zero. When BAZ is asserted, readback data" "comes not from the VME Backplane, but from internal registers on the" "VME Controller." BAZ = (ba1==0) & (ba2==0) & (ba3==0); "We drive the VME Controller data lines whenever the TCPIP Interface unsasserts" "CW. These lines connect the interface to the controller, and so provide data" "on read cycles." control_data.oe = !CW; "We drive the VME Register data lines on any TCPIP Interface write." vrdb.oe = CW; "When the TCIP Interface writes to the VME Registers, we always obtain the VME Data" "Bus from the TCPIP Interface data bus lines, whose values appear on the CD0..CD7" "pins of the control data bus." vrdb=control_data.pin; "We drive the VME Register address lines on any TCPIP Interface write." vrab.oe = CW; "When the TCIP Interface writes to the VME Registers, we always obtain the VME" "Address Bus from the TCPIP Interface data bus lines, whose values appear on" "the CD0..CD7 pins of the control data bus." vrab=control_data.pin; "VME Address Registers" "=====================" "The VME Address on the VME backplane consists of four bytes, making thrity-two" "bits. All four bytes we store in SN74ABT646A address buffers (U7-U10) external" "to the VME Controller, and connected directly to the backplane. All four bytes" "we also store in the VME Controller itself so we can detect Base Address Zero" "(BAZ), identify odd and even addresses, and provide the correct VME Controller" "readback values when the TCPIP Interface is reading out of the VME Controller's" "own VME address space. The VME Address Modifier bits, !LWORD, and !WRITE are stored" "in another SN74ABT646A buffer. We retain copies of !LWORD and !WRITE to allow us" "to configure the data buffers, which themselves are another set of four SN74ABT646A" "chips. 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." "The VME Controller Local Address Space is the one that the TCPIP Interface reads" "and writes from usng CA5..CA0, CD7..CD0, CDS, and CW. It consists of sixty-four" "locations, some of which are read-only, some are write-only, and some are unused." "The VME Controller Backplane Address Space is the one that the TCPIP Interface" "reads from using VA1..VA31, VAM0..VAM5, LWORD, DS0, DS1, WRITE, and AS. This address" "space is read-only, and is active when VA8..VA31 are all zero, which is to say:" "BAZ is asserted. Instead of receiving data from one of the four VME Data Registers," "when the base address is zero the TCPIP Interface receives VME Controller internal" "data." "To access the VME Backplane, the TCPIP Interface writes four bytes to the four" "VME Address Registers, which at the same time causes the same four bytes to be" "stored in the address registers inside the VME Controller. The interface writes" "data to the VME Data Registers as required in a write cycle. The interface writes" "the correct VAM bits, LWORD, and WRITE to the VME Address Control Register, which" "at the same time causes the LWORD and WRITE bits to be stored in the VME Controller." "The interface writes an odd value to the vme status and control register to set AS," "DS0, and DS1, thus initiating a VME cycle. The interface polls the status and" "control register until its value is non-zero. On a read cycle, the interface reads" "one to four bytes from the four VME Data Registers. The interface writes again to" "the status and control register to clear AS, DS0, and DS1 to zero and end the cycle" "In the TCPIP-VME Interface (A2064), we set the base address by writing to locations" "in the VME Controller Backplane Address Space. The A2064 access to the backplane" "consisted of a single IO cycle by the TCPIP Interface (RCM2200 or RCM3200). But the" "A2087 access to the backplane consists of half a dozen IO cycles by the nterface," "which is implemented by the much faster RCM6700 controller board.. Under this new" "scheme, it is cumbersome to have the base address set by a VME backplane cycle." "Instead, we set the base address using the address register's location in VME" "Controller Local Space. But this raises a backward-compatibility problem within" "the TCPIP Interface, because the existing LWDAQ Software uses VME backplane write" "cycles to set the base address, not local write cycles. So the TCPIP Interface" "software must, for backward compatibility, recognise an attempted VME backplane" "write to one of the three base address registers, and copy the specified address" "byte into a local variable, which it will later write to the correct base address" "register during any VME backplane access." "The lowest byte of the address we store simultaneously in U8 and internal" "address register zero. Only VA1..VA7 are stored in the VME address register." "There is no A0 line on the VME Backplane. The backplane uses data strobe DS0" "to indicate access to an odd-numbered address and DS1 to indicate an even-numbered" "address, or both of them together to indicate a two-byte read." ar0.clk=!CDS; ar0.aclr=RESET; when CW & (control_addr==ar0_addr) then { ar0:=control_data.pin; ACK0=!CDS; } else { ar0:=ar0.fb; ACK0=1; } "The address byte one we store simultaneously in base address register" "one as well as in U8." ba1.clk=!CDS; ba1.aclr=RESET; when CW & (control_addr==ar1_addr) then { ba1:=control_data.pin; ACK1=!CDS; } else { ba1:=ba1.fb; ACK1=1; } "The address byte two we store simultaneously in base address register" "two as well as in U10." ba2.clk=!CDS; ba2.aclr=RESET; when CW & (control_addr==ar2_addr) then { ba2:=control_data.pin; ACK2=!CDS; } else { ba2:=ba2.fb; ACK2=1; } "The address byte three we store simultaneously in base address register" "three as well as in U9." ba3.clk=!CDS; ba3.aclr=RESET; when CW & (control_addr==ar3_addr) then { ba3:=control_data.pin; ACK3=!CDS; } else { ba3:=ba3.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." " !LWORD = CD7 (the !LWORD signal is set directly by CD7) " !WRITE = CD6 (the !WRITE signal is set directly by CD6) " AM0 = CD5 " AM1 = CD4 " AM2 = CD3 " AM3 = CD2 " AM4 = CD1 " AM5 = CD0 "The CD5..DC0 value written by the TCPIP Interface must the the" "correct VME address modifier but with the bits reversed. The VAM" "value for non-privileged 24-bit data access is 0x39. With the bits" "reversed, that's 0x27." "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==acr_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 Status and Control Register" "===============================" "On a write to the status and control register, the lowest three" "bits set the data and address strobes." [DS1,DS0,AS].clk=!CDS; [DS1,DS0,AS].aclr=RESET; when CW & (control_addr==vscr_addr) then { DS0:=CD0; DS1:=CD1; AS:=CD2; } else { DS0:=DS0.fb; DS1:=DS1.fb; AS:=AS.fb; } "On a read from the status and control register we return a" "non-zero value when the VME Backplane data acknowledge signal" "is asserted, or we have a bus error." when !CW & (control_addr==vscr_addr) then control_data=[0,0,0,0,0,0,BERR,DTACK]; "VME Data Registers" "==================" "We store values to the VME Data Registers on any write to the" "register's location in VME Controller local address space." "On such cycles, we assert the clock input of the register so" "as to store a value in the buffer that drive the VME backplane." when CW & (control_addr==dr0_addr) then { DCK0=!CDS; } else { DCK0=1; } when CW & (control_addr==dr1_addr) then { DCK1=!CDS; } else { DCK1=1; } when CW & (control_addr==dr2_addr) then { DCK2=!CDS; } else { DCK2=1; } when CW & (control_addr==dr3_addr) then { DCK3=!CDS; } else { DCK3=1; } "On VME backplane write cycles, we enable the VME data registers" "to drive the VME backplane. We enable the lower two data registers" "on all write cycles, and the upper two only on long-word write" "cycles. We set the direction of the buffers to A->B." when WRITE then { DDIR=1; DEN0=1; DEN1=1; DEN2=LWORD; DEN3=LWORD; } "On VME backplane read cycles, we set the direction of the VME" "data registers to B->A. We enable only the VME data register" "selected by the TCPIP Interface for read-out, and only during" "a TCPIP Interface read cycle." when !WRITE then { DDIR=0; when CDS & !CW & (control_addr==dr0_addr) then DEN0=1 else DEN0=0; when CDS & !CW & (control_addr==dr1_addr) then DEN1=1 else DEN1=0; when CDS & !CW & (control_addr==dr2_addr) then DEN2=1 else DEN2=0; when CDS & !CW & (control_addr==dr3_addr) then DEN3=1 else DEN3=0; } "Local Readback Registers" "========================" "We read the configuration switch from the VMA Controller" "local address space when the TCPIP Interface reboots." when !CW & (control_addr==cs_addr) then control_data=[0,0,0,0,0,0,0,!CSW]; "VME Data Registers and Backplane Readback Registers" "===================================================" "When we read from the four VME Data Registers, we are reading" "directly the values of the VME Bus data lines, and this is how" "the TCPIP Interface reads data from VME slaves. When we have" "base address zero, however, the VME Controller is supposed to" "act as its own slave and supply the VME data itself. It does" "this by hijacking the control data bus from the VME Data" "Registers whenever the TCPIP Interface attempts to read from" "those registers." "The VME Controller chooses which value to assert on the control" "data bus by looking at the ar0 address register. The readback" "registers include the hardware identifier number, which is 87 for" "the A2087." when !CW & (control_addr >= dr3_addr) & (control_addr <= dr0_addr) then { when BAZ then { when (ar0 == fv_addr) then control_data=firmware_version_num; when (ar0 == id_addr) then control_data=hardware_id; when (ar0 == hv_addr) then control_data=hardware_version_num; when (ar0 == rp_addr) then control_data=rpdb; } else { control_data=vrdb.pin; } } "The ram portal register 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." rpdb.aclr = RESET; rpdb.clk = !CDS; when !CW & (control_addr >= dr3_addr) & (control_addr <= dr0_addr) & BAZ & (ar0 == rp_addr) & !WRITE then { rpdb := rpdb + 1; } else { rpdb := rpdb.fb; } "Bus Timer" "=========" "When the TCPIP Interface is master of the VME bus, and it reads" "or writes from a VME slave, our presumption is that the slave will" "assert DTACK as soon as it has presented the read data on the" "VME data bus, or recorded the write data from the VME data bus. If" "no slave exists at the location accessed by the master, we assert" "BERR some time after one of DS0 or DS1 is asserted, a time long" "enough to allow even the slowest slave to assert DTACK. The Bus" "Timer measures the time passed since the beginning of a VME cycle." "When the base address is zero, the master is reading from itself," "so we assert DTACK after a short time." bt.clk = CK; bt.aclr = RESET; "So long as DS0 or DS1 is assserted, we count up to the done value" when !DS0 & !DS1 then { bt:=0; } else { when (bt>=0) & (bt=self_dtack_ticks) then GQ3=1 else GQ3=0; "If this is a remote access, and bt is above the bus error count, and we" "still have not seen DTACK, assert BERR by driving the base of Q5 hi." when !BAZ & (bt>=bus_error_ticks) & !DTACK then GQ5=1 else GQ3=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 forward BG3IN to BG3OUT." BG3OUT=BG3IN; "We do not assert bus request three BR3." GQ6=0; "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 (test_version!=1) then { BUSY = CDS; VACT = DS0 # DS1 # AS; TP1 = CSW $ LNK; TP2 = BAZ; TP3 = DEN0 # DEN1 # DEN2 # DEN3; TP4 = !DCK0 # !DCK1 # !DCK2 # !DCK3; TP5 = DDIR; TP6 = !ACK0 # !ACK1 # !ACK2 # !ACK3 # !ACK4; TP7 = DTACK # BERR; TP8 = CDS; } 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