Wednesday, August 19, 2015

Timer CPLD

Here is the verilog for the 4th CPLD that I figured out I need. It's a 1ms timer. It will pull the interrupt pin low and return to Hi-Z mode when the interrupt is cleared by reading the tick counter. The tick counter is 3 bits and can be used to ensure some degree of consistency when late servicing the tick interrupt or when using shared interrupts (which I will do). It has a very small pin footprint. Just clock, read-only SPI, chip select, and the interrupt line. This one fits into the smaller M4A5-32/32 so I'll use that.

// This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
// clock in. Scales that down to 125MHz with a 4 bit counter.
// Then it counts 125 of those in a 7 bit counter that resets on 7c.
// When the reset occurs, the pin_nINT line goes from Hi-Z to low.
//
// The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
//
// SCK and Dout are used to read the 3 bit tick counter. This is used
// to ensure that the count is kept synched. Dout is updated on the
// falling edge of SCK and the MSB is shifted first. 
//


module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    /*
    output [3:0] test_prescaler,
    output [6:0] test_counter,
    output [2:0] test_ticks,
    output [2:0] test_tickShift,
    output test_nCount,
    output test_nTick,
    output test_nInt,
    output test_nCS0,
    output test_nCS1
    */
    );

    /*
    assign test_prescaler = prescaler;
    assign test_counter = counter;
    assign test_ticks = ticks;
    assign test_tickShift = tickShift;
    assign test_nCount = nCount;
    assign test_nTick = nTick;
    assign test_nInt = nInt;
    assign test_nCS0 = nCS0;
    assign test_nCS1 = nCS1;
    */

    reg [3:0] prescaler;
    reg [6:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    reg nCount;
    reg nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;
    reg SCK1;

    initial begin
        prescaler = 0;
        counter = 0;
        ticks = 0;
    end

    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;

    always @(posedge pin_CLK) begin
        prescaler <= prescaler + 1;
        nCount <= ~(&prescaler); // 0 once every 16 pin_CLK
        nTick <= ~(counter == 7'b1111100); // 124 caused 125->0
        if (nCount == 1'b0) begin
            if (nTick == 1'b0) begin
                counter <= 0;
                ticks <= ticks+1;
            end
            else begin
                counter <= counter+1;
            end
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

        // Set interrupt on the CLK. Reset it on the falling edge of
        // pin_nCS+2 clocks
        if ((nTick | nCount) == 1'b0) begin
            nInt <= 1'b0; 
        end
        else if ((nCS1 & (~nCS0)) == 1'b1) begin
            nInt <= 1'b1; 
        end

        SCK0 <= pin_SCK;
        SCK1 <= SCK0;
        if (~pin_nCS) begin
            //if (SCK1 & ~SCK0) begin
            if (SCK0 & ~pin_SCK) begin
                tickShift[2:0] <= { tickShift[1:0], 1'b0 };
            end
        end
        else begin
            tickShift[2:0] <= ticks[2:0];
        end

    end
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

endmodule

No comments:

Post a Comment