. | . | . | . | David McCracken |
Verilogupdated:2018.05.11 |
/* Cellsim.v * Cell event simulator generates a programmable number of pulses with a given * width and event period primarily for testing APU gather coincidence. Change * EventPeriod, PulseWidth, and EventCount definitions, recompile, and reload * FPGA to change parameters. * Target device XC9572XL Name FPGA pin Circuit Location Purpose/use clk1 15 F1M9/GCK1, U1p1 40 MHz FPGA clock. clk5Mhz 22 F3M2, JP6p3 clk1 / 8 for basic width and clock. arm 19 F1M15, JP6p1 Arm the generator by pulling low (LED goes off). trig 20 F1M17, JP6p2 Trigger the generator by pulling low (if armed). gen 32 F3M4, LED9, JP8p1 Goes low, turning on LED, when pattern gen starts. dout 23 F1M12, JP6p4 Cell pattern data out. Connect to APU channel+ (e.g. JCH1p1). Use J3p2 for ground for arm, trig, and APU channel- (e.g. JCH1p2). ....................................................................................*/ module cellsim( clk1, arm, trig, gen, clk5Mhz, dout, armout ); input clk1, arm, trig; output gen, clk5Mhz, dout, armout; reg clk5Mhz, dout; reg [1:0] clkdiv; reg [7:0] eventTmr; reg [7:0] eventCtr; reg rdyFlag; wire divclk; /* EventPeriod and EventWidth are in number of 200 nsec periods provided by * clk5Mhz, e.g. 5 = 1 usec. EventPeriod includes EventWidth. PulseWidth = 10 for 2 us. EventPeriod (clks) Event Period (us) Conversion Time (us) 25 5 3 30 6 4 35 7 5 40 8 6 45 9 7 50 10 8 55 11 9 60 12 10 65 13 11 70 14 12 75 15 13 80 16 14 85 17 15 */ `define PulseWidth 10 `define EventPeriod 50 `define EventCount 16 /* gen is a debounced switch. Pull arm and trig high through 10K. Ground arm * to make gen high, turning off the LED. When gen is high (LED off) the pulse * generator is armed and ready. Ground trig to make gen low, turning on the * LED and starting the pulse stream generator. */ assign gen = ~arm & trig | gen & trig; assign divclk = clkdiv == 3 & clk5Mhz; assign armout = ~arm; always @( posedge clk1 ) begin clkdiv <= clkdiv + 1; if( clkdiv == 3 ) clk5Mhz <= ~clk5Mhz; else clk5Mhz <= clk5Mhz; if( gen ) begin eventCtr <= 0; eventTmr <= 0; dout <= 0; end else if( divclk && eventCtr < `EventCount ) begin if( eventTmr == `EventPeriod ) begin eventCtr <= eventCtr + 1; eventTmr <= 0; end else begin if( eventTmr < `PulseWidth ) dout <= 1; else dout <= 0; eventTmr <= eventTmr + 1; end end end endmodule
To test a new large flow cytometer (blood analyzer) FPGA design against timing requirements, a number of unique electronic event combinations needed to be simulated. We had an existing cell simulator, the "ecell" but it could not test the effect of time between events because the events are spaced further apart than the time required to convert and store all channels. The only external stimulus test facility we had tested the effect of the duration of a single event.
None of the previous tests verified the FPGA's handling of multiple rapidly occurring events characteristic of the real application. For this an external pattern generator is required. The minimum pattern needed to be a specified number of pulses with a specified pulse width and spacing. More complicated patterns, including varying pulse height, width, and spacing, could provide additional test coverage but not significantly more than the minimum. However, a function generator like the DS345 used for the event width stimulus was not adequate even for the minimum because it produced either a single pulse, a continuous stream of pulses with a specified mark and space ratio, or a specified number of symmetrical cycles. The possibility of using two such instruments with a specified number of cycles from one triggering pulses of the second was investigated. This approach failed to meet the timing requirements. The test is not adequate unless the events can be programmed to occur faster than the conversion and storage time of one channel, i.e. approximately five usec period. The maximum synthesized periodic waveform frequency of several instruments similar to the DS345 appeared to be much lower than this (there is some confusion because the manuals for these instruments claim higher frequencies but only explain how to produce much lower ones).
I concluded that, while it may be ultimately possible to make a pair of these instruments produce the required patterns it would be easier to program one PLD to generate the pattern. The Insight CPLD 95XL demo board was used for this. Its Xilinx XC9572XL contains more than enough resources to generate any one pattern but not enough to be programmable. To produce the test vectors, the PLD was reprogrammed for each timing variation. Using the Xilinx ISE WebPack software, changing the time and count of the stimulus can be done in less than three minutes. The pattern is triggered by the user grounding one PLD input. Because pattern reconfiguration and triggering are not automated, testing is limited to the most important items that are not covered by other testing. For example, although the Verilog code (cellsim.v) used for the PLD defines the pulse width in a manner that makes it as easy to change as event period, a constant 2 us width is used throughout the test. Most of the effects of varying event width are adequately tested by the previously described width test using the DS345. The number of simulated events is also constant at 16, affording sufficient resolution of dropped list counts due to conversion coincidence without generating such a wide range of results that automated analysis would be needed. Only gather channel counts of 1, 2, 3, 4, and 5 are tested and the channel-count association is not varied: when one channel is converted, it is channel 1; when two are converted, they are channels 1 and 2; etc. With an automated test facility, more variations would be tested.
The demo board's on-board 40 MHz clock is divided down to 5 MHz used as the fundamental clock source for the pulse width and event period counters. The 2-usec pulse width is generated by counting 10 clocks. The event period encompasses both this time and the time for converting and storing list data. Since the minimum ADC conversion time is 2 us, the period with one channel selected would have to be at least 4 usec. Very precise timing is not required and the period is varied only by full microseconds (5 clocks). The smallest event period tested is 5 us, allowing 3 us for conversion and writing into memory.
The output of the PLD is connected to the channel 1 input at JCH1 pin 1. Bench A was tested using the ICD32 macro streama.icd, which sets up the bench to trigger on channel 1 (mm.w 500050 1) and to write list data to 0x200000 (mm.l 50005E 200000). It also sets the event width to 2 without recording width as a parameter (mm.w 500056 2) and displays list memory and the FPGA's bench A registers (mdf6 200000 and mdf3 500050). The user enters the channel conversion list using the ICD32 command mm.w 500058 1, 3, 7, F, or 1F and then triggers the pattern generator. Testing revealed that in all cases the amount of list data written into memory was as expected and consistent with the list pointer register. The cell count register always reported exactly 16 even when list data was less due to conversion coincidence. Both of these are characteristics to be tested here but require no further result reporting. After this, all that remains of test results is the relationship between the conversion time allowed by the event period, the number of channels converted, and the number of list data events.
sersig.v `define MaxSig 8 // Count of signals - 1 (note 0-based indexing). `define MaxStateBit 3 // 4 bits supports up to 16 signals. module sersig( clk1, sig, sdat, pload, xclk, invclk, unused ); input clk1; input [`MaxSig:0] sig; output sdat; output pload; reg pload; input xclk; // Connect to same source as clk1. output invclk; output [27:`MaxSig] unused; reg [`MaxSig:0] dat; reg [`MaxStateBit:0] cnt; initial begin // Simulation assistance. cnt = `MaxSig; pload = 0; dat = 0; end assign unused = 0; not( invclk, xclk ); buf( sdat, dat[`MaxSig]); always @(posedge clk1) if( cnt == `MaxSig ) cnt <= 0; else cnt <= cnt + 1; always @(posedge clk1) begin /* At trailing edge of last state, load the input/shift registers. At all other times shift the loaded data. */ if( cnt == `MaxSig ) dat <= sig; else dat <= dat << 1; case( cnt ) `MaxSig: pload <= 1; `MaxSig / 2: pload <= 0; endcase end endmodule sersigt.v (test bench) `include "sersig.v" module sersigt; reg osc; reg [`MaxSig:0] sig; wire sdat; wire pload; wire invclk; wire [27:`MaxSig] unused; reg [`MaxStateBit:0] cnt; sersig rsh( clk1, sig, sdat, pload, xclk, invclk, unused ); initial begin osc = 0; sig = 'b101001110; cnt = `MaxSig; end always #1 osc = ~osc; buf( xclk, osc ); buf( clk1, osc ); always @(posedge clk1) if( cnt == `MaxSig ) cnt <= 0; else cnt <= cnt + 1; initial begin $monitor( $time,,, "clk1 = %b, cnt = %d, pload = %b, sdat = %b", clk1, cnt, pload, sdat ); #(`MaxSig * 2 + 2) sig = 'b100011010; #(`MaxSig * 2 + 4) $finish; end endmodule
The Xilinx XC9536XL PLD selected for this application requires 3.3V Vcc but accepts and generates TTL-compatible signals, a requirement for interfacing with the sensor signals and 26C31. Icc ranges from 20 to 50 mA, which makes it easy to generate the 3.3V from 5V using a resistor (33 ohm, 1/4 W) and zener diode (3.3V 1/2 W).
A clock is needed for the bit rate and serializer state machine. This does not have to be accurate or stable so I tried to build an RC oscillator in the PLD. I tried several oscillator and multivibrator topologies and, although some did oscillate, none yielded a clock that the PLD was able to use. One multivibrator design yielded a decent looking clock but when used as a clock input, the PLD apparently doubled the frequency. When I asked Xilinx about this, the FAE said that he too could not make a usable oscillator in the part. Since an external clock would be needed, I chose a single-component 2 MHz crystal oscillator instead making an RC oscillator from discrete components that might be difficult to source (e.g. 74HC14). A silicon oscillator, such as Maxim/Dallas DS1077L, could also be used.
As currently designed, the transport PCB has nine sensors. We will probably have only eight as we do not need the rail guide sensor. This sensor is located at the end of the rack picker and tells when a rack is engaged. This is an expensive assembly with a moving cable and is not necessary. The reflective rack sensor at the tray (Tray Section Carrier Detector) tells whether the picker has lifted the rack while the sensor at the belt (Carrier Positioner Carrier Detect) tells whether the rack was dropped in moving from the tray to the belt. If the picker fails to engage the rack at the tray, we will not hunt for the rack so the rail guide sensor would only serve to allow us to report a mechanical failure to the user one second sooner than without it.
To facilitate using the serializer in other applications, the PLD's Verilog code parameterizes the number of inputs using defines for the number of signals (MaxSig) and the number of bits in the control state machine (MaxStateBit). Although we won't use be using the rail guide sensor, provision was made for it for the benefit of Architect by defining MaxSig as 8 (the nine signals are 0 through 8). MaxStateBit was defined as 3, which means that there are 4 state bits and, therefore, as many as 16 inputs could be supported. The state machine is clocked by the external oscillator, which also drives an independent inverter (realized in the PLD). The inverted clock is used as the transmitted bit clock. Both the PLD and the receiver clock on the positive edge so they are in anti-phase, avoiding timing problems.
The operation of the serializer is straightforward. The serial output continuously shifts with no delay for loading inputs or for the receiver's parallel shift out. The signals are shifted out most significant (sig8) first. At state MaxSig, the serial output data, sdat, has the value of input sig0. On the rising clock edge, pload, the receiver's parallel load signal goes from low to high, causing a broadside load of the receiver's serial shift registers to its parallel outputs. Also on this clock edge the serializer state changes to 0 and all of the input signals are latched. Since sdat is combinatorially driven from the latched MSB, it reflects this value after a gate delay. The change in sdat coincident with pload does not result in a race condition because the receiver's serial shift registers were clocked on the previous invclk. At state 0, sdat shows the MSB (sig8). After one/half clock delay, the inverted clock, invclk, causes the receiver to shift in this bit. At the next clock, the serializer's input registers are shifted left (dat << 1) causing sig7 to appear at sdat. This process repeats until at state MaxSig / 2 pload goes low, arming it for the next parallel receiver loading. For any even number of signals, pload is symmetrical, which may simplify phase-locking to it in case we decide to implement the two-signal receiver.
With only nine input signals, the PLD is under-utilized. Unused input pins have to be pulled to Vcc or DGND and unassigned pins default to input. To keep unused pins physically uncommitted, all of them are automatically made outputs by the declaration output [27:`MaxSig] unused; The name "unused" has no intrinsic meaning but is simply the name arbitrarily chosen for the vector.
The Verilog test bench, sersigt, is wired to all of the inputs and outputs of the rsh instance of sersig. It also duplicates sersig's internal state counter to display the state in waveforms. For most simulators this is not really necessary, as they can show the internal workings of all simulated components. For simulation, the signal vector (sig0 through sig8) is preloaded with the pattern 101001110. After generating exactly enough clocks to shift this word, the vector is loaded with the pattern 100011010, which is then clocked out. Microsoft Word document sersim.doc [sersim] contains a bitmap of the simulation waveform produced by ModelSim.
Low level timing is very loose. The only possibility of criticality is in the delay of sdat relative to invclk. A 74HC595's input data setup time is 24 nsec. (@<85C). The worst case combined delay of a 26C31 transmitter and 26C32 receiver is 56 nsec. (including both propagation delays and differential signal rise time). Sdat is delayed in the PLD by two gate delays, the sequential shift and the combinatorial out, which is maximum 16 nsec. Therefore, the total worst case time from clock to sdat required valid at the receiver is 96 nsec. Sdat shifts at the PLD on the clock but at the receiver on invclk, which reduces the 500 nsec. (2 Mhz) period to 250 nsec. Thus, even if invclk experienced no delay, there would be a 154 nsec. margin.
// rpmpld.v // Right Panel Module (RPM) PLD // Target device is an Xilinx XC95144XL CPLD `timescale 1 ns /100 ps // For release. `define CLOCKPULSE 2000 `define SVTIMEOUT 500 // For testbench. // `define CLOCKPULSE 10 // `define SVTIMEOUT 2 module rpmpld( PRST, NPCS, SCLK, MOSI, MISO_LB, // scan chain signals MOSI_LB, MISO, SVValvesEnabled, // Enable <0,1> Shear valve, else SOL1 and/or SOL2. SVNewEnabled, // Enable new (on-board driver) or old style shear valve. YV0_En, // (JPLDI4) Enable Yvalve 0, else MOSI4->JPLDOp7, MOSI5->JPLDOp6. YV1_En, /* (JPLDI1) Select 2MHz (APU2) or 4MHz (APU4 or MSM) SCK * used for timing (e.g. of fluid strobe). This input was originally intended * for selecting YV1 control or simple output. That use could be restored by * using the extra MOSI bit (13) to select the clock speed. Jumper off selects * 4MHz, on 2MHz. In either case, MOSI6->JPLDOp9, MOSI7->JPLDOp8. */ StatusBoardInvert, SVCurrentSense, // SV over surrent signal, Not used. SV_ON, SV_DIR, // shear valve drive signals SV_CWAckOld, SV_CCWAckOld, // feedback signals from shear valve SV_CWAckNew, SV_CCWAckNew, // feedback signals from new shear valve YV0_OC, YV0_CWH, YV0_CCWH, // Y valve over current, limit switches YV0_CW, YV0_CCW, // Y valve drive signals YV1_OC, YV1_CWH, YV1_CCWH, YV1_CW, YV1_CCW, SENSOR1, SENSOR2, SENSOR3, SENSOR4, // strobed fluid sensor inputs SENSOR5, SENSOR6, SENSOR7, SENSOR8, CHARGE_SENSORS, // fluid sensor stobe signal StatusBoard, // Status board signals. JT1, JT2, JT3, JT4, JT5, // JTEST <- PLD pins 60, 59, 58, 39, 40. SOL25_ON, SOL25_HI, GATED_SCLK ); // Variable declaration input PRST, NPCS, SCLK, MOSI, MISO_LB; input [1:0] SVValvesEnabled; input SVNewEnabled; input StatusBoardInvert; input [1:0] SVCurrentSense; input [1:0] SV_CWAckOld; input [1:0] SV_CCWAckOld; input [1:0] SV_CWAckNew; input [1:0] SV_CCWAckNew; input YV0_OC, YV1_OC, YV0_En, YV1_En; input YV0_CWH, YV1_CWH, YV0_CCWH, YV1_CCWH; input SENSOR1, SENSOR2, SENSOR3, SENSOR4; input SENSOR5, SENSOR6, SENSOR7, SENSOR8; output CHARGE_SENSORS; output MOSI_LB, MISO; output [1:0] SV_ON; output [1:0] SV_DIR; output YV0_CW, YV1_CW, YV0_CCW, YV1_CCW; output GATED_SCLK; output [12:9] StatusBoard; // Status board signals. output SOL25_ON, SOL25_HI; output JT1, JT2, JT3, JT4, JT5; // JTEST <- PLD pins 60, 59, 58, 39, 40. reg [1:0] SVOn; reg [1:0] SV_CW_Req; reg YV0_On, YV1_On, YV0_CW_REQ, YV1_CW_REQ; reg LAST_MOSI_b8, STROBE_SENSORS; reg LATCHED_SENSOR1, LATCHED_SENSOR2, LATCHED_SENSOR3, LATCHED_SENSOR4; reg LATCHED_SENSOR5, LATCHED_SENSOR6, LATCHED_SENSOR7, LATCHED_SENSOR8; reg [15:0] mosi_shift_registers; // scan chain output reg reg [15:0] miso_shift_registers; // scan chain input reg reg [5:0] timer; reg TX1; reg OneKTimerPulse; // 1 KHz timer pulse. reg [11:0] OneKTimer; // 1 KHz timer counter. reg [10:0] SV0Timer; // ~2 Second Timer based on 1K pulse. reg [10:0] SV1Timer; // ~2 Second Timer based on 1K pulse. reg [1:0] SVOnOff; reg [1:0] SVDir; reg [1:0] SVTimeExpired; reg [12:9] StatBoard; // Status board signals. reg [1:0] SVStartTimer; reg [15:13] GenOut; integer i; always @ (posedge NPCS) begin // transfer data from scan chain to outputs SV_CW_Req[0] = mosi_shift_registers[0]; SVOn[0] = mosi_shift_registers[1]; SV_CW_Req[1] = mosi_shift_registers[2]; SVOn[1] = mosi_shift_registers[3]; YV0_On = mosi_shift_registers[4]; YV0_CW_REQ = mosi_shift_registers[5]; YV1_On = mosi_shift_registers[6]; YV1_CW_REQ = mosi_shift_registers[7]; StatBoard[12:9] = mosi_shift_registers[12:9]; GenOut[15:13] = mosi_shift_registers[15:13]; end always @ (posedge SCLK) begin if (PRST) begin // reset asserted // clear the scan chain registers mosi_shift_registers = 'h 0000; miso_shift_registers = 'h 0000; // initialize other registers STROBE_SENSORS = 0; LATCHED_SENSOR1 = 0; LATCHED_SENSOR2 = 0; LATCHED_SENSOR3 = 0; LATCHED_SENSOR4 = 0; LATCHED_SENSOR5 = 0; LATCHED_SENSOR6 = 0; LATCHED_SENSOR7 = 0; LATCHED_SENSOR8 = 0; LAST_MOSI_b8 = 0; timer = 0; OneKTimer = 12'd0; OneKTimerPulse = 0; end else begin // PRST is low, reset not asserted // 1KHz timer. OneKTimer = OneKTimer + 1; if (OneKTimer >= `CLOCKPULSE) begin OneKTimer = 12'd0; OneKTimerPulse = ~OneKTimerPulse; end else ; // Timer for sensor strobe. if (timer > 1) begin timer = timer - 1; STROBE_SENSORS = 1; end else begin if (timer ==1) begin timer = 0; LATCHED_SENSOR1 = SENSOR1; LATCHED_SENSOR2 = SENSOR2; LATCHED_SENSOR3 = SENSOR3; LATCHED_SENSOR4 = SENSOR4; LATCHED_SENSOR5 = SENSOR5; LATCHED_SENSOR6 = SENSOR6; LATCHED_SENSOR7 = SENSOR7; LATCHED_SENSOR8 = SENSOR8; end else // timer == 0 STROBE_SENSORS = 0; end if (NPCS) begin if (LAST_MOSI_b8 != mosi_shift_registers[8]) begin LAST_MOSI_b8 = mosi_shift_registers[8]; if (LAST_MOSI_b8 && timer == 0) // Start pulse on low to high transition of bit8 if( YV1_En ) timer = 40; // 10 usec pulse @ 4MHz SCK else timer = 20; // 10 usec pulse @ 2MHz SCK else ; end else ; // clock input data to shift register for (i=0; i<2; i=i+1) begin if (SVNewEnabled) begin // New shear valve. if (SVTimeExpired[i]) begin if (SV_CWAckNew[i] || SV_CCWAckNew[i]) begin // Sensor made with stall. miso_shift_registers[i*2] = SV_CWAckNew[i]; miso_shift_registers[i*2+1] = SV_CCWAckNew[i]; end else begin // Sensor not made but motor stalled. miso_shift_registers[i*2] = 1; miso_shift_registers[i*2+1] = 1; end end else begin miso_shift_registers[i*2] = 0; miso_shift_registers[i*2+1] = 0; end end else begin // Old shera valve and siple I/O. miso_shift_registers[i*2] = SV_CWAckOld[i]; miso_shift_registers[i*2+1] = SV_CCWAckOld[i]; end end miso_shift_registers[4] = YV0_CWH; miso_shift_registers[5] = YV0_CCWH; miso_shift_registers[6] = YV1_CWH; miso_shift_registers[7] = YV1_CCWH; miso_shift_registers[8] = LATCHED_SENSOR1; miso_shift_registers[9] = LATCHED_SENSOR2; miso_shift_registers[10] = LATCHED_SENSOR3; miso_shift_registers[11] = LATCHED_SENSOR4; miso_shift_registers[12] = LATCHED_SENSOR5; miso_shift_registers[13] = LATCHED_SENSOR6; miso_shift_registers[14] = LATCHED_SENSOR7; miso_shift_registers[15] = LATCHED_SENSOR8; end else begin // NPCS is low //shift MISO scan chain data miso_shift_registers[15:1] = miso_shift_registers[14:0]; miso_shift_registers[0] = MISO_LB; //shift MOSI scan chain data mosi_shift_registers[15:1] = mosi_shift_registers[14:0]; mosi_shift_registers[0] = MOSI; end end // PRST is low, reset not asserted end // (posedge SCLK) always @ (negedge SCLK) begin if(!NPCS) TX1 = mosi_shift_registers[15]; // shift data out on falling sclk end always @ (negedge OneKTimerPulse) begin // SV0 timer. if (!SVStartTimer[0]) begin SV0Timer = 11'd0; SVTimeExpired[0] = 0; end else begin if (!SVTimeExpired[0]) SV0Timer = SV0Timer + 1; else ; // Check the timer. if (SV0Timer > `SVTIMEOUT) begin SVTimeExpired[0] = 1; end else ; end // SV1 timer. if (!SVStartTimer[1]) begin SV1Timer = 11'd0; SVTimeExpired[1] = 0; end else begin if (!SVTimeExpired[1]) SV1Timer = SV1Timer + 1; else ; // Check the timer. if (SV1Timer > `SVTIMEOUT) begin SVTimeExpired[1] = 1; end else ; end end always @ (SVValvesEnabled or SVNewEnabled or SV_CW_Req or SVOn or SVTimeExpired or SV_CWAckNew or SV_CCWAckNew) begin // Shear Valve. for (i=0; i<2; i=i+1) begin if (!SVValvesEnabled[i]) begin if (!SVNewEnabled) begin // Direction request and On/Off reverted on the // original design for old SV. SVOnOff[i] = SV_CW_Req[i]; SVDir[i] = SVOn[i]; end else begin // It is the new SV. if (SVOn[i]) begin if (SV_CW_Req[i]) begin if (!SV_CWAckNew[i]) SVStartTimer[i] = 0; else SVStartTimer[i] = 1; end else begin if (!SV_CCWAckNew[i]) SVStartTimer[i] = 0; else SVStartTimer[i] = 1; end if (SVStartTimer[i]) begin if (SVTimeExpired[i]) SVOnOff[i] = 0; else SVOnOff[i] = 1; end else begin SVOnOff[i] = 1; end end else begin SVOnOff[i] = 0; SVStartTimer[i] = 0; end SVDir[i] = SV_CW_Req[i]; end end else begin // SV used as I/O. SVOnOff[i] = SVOn[i]; SVDir[i] = SV_CW_Req[i]; end end end // // Output Assignments // assign CHARGE_SENSORS = STROBE_SENSORS; // strobed fluid sensor output assign MISO = miso_shift_registers[15]; // scan chain upstream output assign MOSI_LB = TX1; // scan chain downstream output assign GATED_SCLK = SCLK || NPCS; // Shear valve outputs assign SV_ON[0] = (SVOnOff[0] && !PRST); assign SV_DIR[0]= (SVDir[0] && !PRST); assign SV_ON[1] = (SVOnOff[1] && !PRST); assign SV_DIR[1]= (SVDir[1] && !PRST); // Y valve outputs assign YV0_CW = (YV0_En && YV0_On && YV0_CW_REQ && !YV0_OC && YV0_CWH && !PRST) || (!YV0_En && YV0_CW_REQ && !PRST); assign YV0_CCW = (YV0_En && YV0_On && !YV0_CW_REQ && !YV0_OC && YV0_CCWH && !PRST) || (!YV0_En && YV0_On && !PRST); /* YValve1 is permanently disabled in order to use YV1_En for selecting the strobed fluid sensor divider. The YV1 outputs are simple scan chain. assign YV1_CW = (YV1_En && YV1_On && YV1_CW_REQ && !YV1_OC && YV1_CWH && !PRST) || (!YV1_En && YV1_CW_REQ && !PRST); assign YV1_CCW = (YV1_En && YV1_On && !YV1_CW_REQ && !YV1_OC && YV1_CCWH && !PRST) || (!YV1_En && YV1_On && !PRST); */ assign YV1_CW = YV1_CW_REQ && !PRST; assign YV1_CCW = YV1_On && !PRST; // Status board outputs assign StatusBoard[12] = (((!StatusBoardInvert && StatBoard[12]) || ( StatusBoardInvert && !StatBoard[12])) && !PRST); assign StatusBoard[11] = (((!StatusBoardInvert && StatBoard[11]) || ( StatusBoardInvert && !StatBoard[11])) && !PRST); assign StatusBoard[10] = (((!StatusBoardInvert && StatBoard[10]) || ( StatusBoardInvert && !StatBoard[10])) && !PRST); assign StatusBoard[9] = (((!StatusBoardInvert && StatBoard[9]) || ( StatusBoardInvert && !StatBoard[9])) && !PRST); // Generic outputs assign SOL25_HI = (GenOut[14] && !PRST); assign SOL25_ON = (GenOut[15] && !PRST); // JT1 is permanently assigned MOSI13 and could be used for application. // JT2-5 are assigned internal signals as needed for testing. assign JT1 = (GenOut[13] && !PRST); assign JT2 = STROBE_SENSORS; // For verifying 10 usec strobe width. Note JPLDI1 use. endmodule
In a complex multifunction machine we had partitioned the hardware into several stable sub-units and one unstable unit to take left-overs-- essentially a dump. By intent, the board controlling this unit was frquently being redesigned during development and for variations of the basic machine.
Although this did effectively stabilize the other sub-units, reiterating this one board was getting costly so I partitioned it, concentrating the unstable functions into a single complex PLD with various jumper-selectable functions and I/O connections. In this way, the same hardware could be used for a specialized function requiring the PLD to provide direct local control or simple I/O. In some cases, such as shear valve control, the same facilities could control various versions of the shear valve or simple I/O, all by external jumper selection. New control capabilities could be created by reprogramming the PLD instead of making a new board.
The unit is controlled by a central CPU located in another part of the machine. The only communication is by an SPI loop. The board is not an endpoint but is in the middle of the loop, with the CPU's output (MOSI) flowing downstream through the board (and this PLD) and system input signals flowing (MISO) upstream.