. . . .

Verilog

 

updated:2018.05.11

Prev   Next   Site Map   Home  
Text Size
Please reload page for style change

Verilog Blood Cell Event Simulator


/* 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.




Verilog Serializer-Deserializer


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

See RSH Transport Pcb Design

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.




Verilog Complex SPI Device


// 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.




Prev   Next   Site Map   Home   Top   Valid HTML   Valid CSS