Thursday, September 17, 2015

Ticks

Last night, I decided to wire in the 1ms ticker. Of course, as soon as it wired in, the whole board stopped working. This morning I figured out that I had the INT and SCK lines swapped. So I unswapped them. I have already compiled in the tick driver so it should be grabbing the 3 bits of tick from the ticker. I verified that it is doing that when the UART interrupts occur.

I also verified that the INT pulls a pull-up low. But... I forgot that I had swapped out the 2MHz oscillator for a 4MHz oscillator. So my 1ms tick was actually 500μs. That was easy to fix. I ran home and grabbed my JTAG program and reburned the CPLD.

Now I had a 1ms tick. It wasn't counting correctly, but my code to handle the count wasn't right. Once that was corrected, it counted correctly. I can now make clocks.

However, there was an issue when I tried to program an intel hex file into flash--the tick makes the CPU too busy to keep up with communication. So, I rerouted the timer CPLD INT through a DIP switch. For now, I'll just turn off the timer when I want to program the flash. Until I can figure out a better way. I also have the 8MHz clock timer ready to burn. I may also toy with different tick amounts such as 2ms, or 5ms, or even 10ms.

Just for reference, here is the CPLD verilog:

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

`define COUNTER_TOP 11

module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    );

    reg [`COUNTER_TOP:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    wire nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;

    // Not synthesized. Just for testing
    initial begin
        counter = 0;
        ticks = 0;
    end

    // @ 2MHz 1ms, 124
    // @ 4MHz 1ms, 249
    // @ 8MHz 1ms, 499
    //assign nTick = ~(counter ==   11'b11111001111); // 124'15 causes 125'0->0'0
    assign nTick = ~(counter ==  12'b111110011111); // 124'31 causes 250'0->0'0
    //assign nTick = ~(counter == 13'b1111100111111); // 124'63 causes 500'0->0'0
    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

    always @(posedge pin_CLK) begin
        if (nTick == 1'b0) begin
            counter <= 0;
            ticks <= ticks+1;
        end
        else begin
            counter <= counter+1;
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

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

        // search for falling edge of SCK in CLK
        SCK0 <= pin_SCK;
        if (~pin_nCS) 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

endmodule

Here is tick.c:

#include "tick.h"
#include "spi.h"
#include "gpio.h"
#include "idle.h"
#include "uart.h"
#include "timer.h"
#include "interrupt.h"

Z80_IO_PORT(GPIO_OUT, 0x80);
Z80_IO_PORT(SPI_REG, 0x83);
Z80_IO_PORT(SPI_CTRL, 0x84);

uint8_t lastTick = 0;
uint32_t totalTicks = 0;

void tickInit()
{
    // GPIO-0 is UART CS#
    gpioWriteLevel(0x02, 0x02);
    gpioWriteDirection(0x02, 0x02);
    lastTick = 0;
    totalTicks = 0;
}

uint32_t getTicks()
{
    uint32_t ticks;
    di();
    ticks = totalTicks;
    ei();
    return ticks;
}

// called from the actual ISR, so this one doesn't end in reti
void tickISR()
{
    uint8_t tick;
    uint8_t out;

    //tick = spiExchange(0, SPI_SHIFT_TO_MSB, 3);
    out = GPIO_OUT;
    GPIO_OUT = out & ~(1 << 1);
    SPI_REG = 0;
    SPI_CTRL = SPI_SHIFT_TO_MSB | 3;
    __asm__("nop");
    __asm__("nop");
    tick = SPI_REG;
    tick &= 0x07;
    GPIO_OUT = out;

    if (lastTick != tick)
    {
        totalTicks += (uint8_t)((tick - lastTick) & 0x07);
        lastTick = tick;
        timerTick(totalTicks);
        releaseIdle();
    }
}

So, next I have to start working with the timers and idle. One of my short term goals was to make a clock...now that's possible. Also, timeouts for communication.

I haven't used the timer code or idle yet. Here they are--probably buggy as hell since they are totally not tested.

timer.c:

#include "timer.h"
#include "tick.h"
#include <string.h>
#include <assert.h>

#define TIMER_COUNT 2

struct TimerInfo
{
    uint32_t interval;
    uint32_t trigger;
    bool claimed;
    bool repeat;
    bool running;
    void(*callback)(int timer);
};

struct TimerInfo timers[TIMER_COUNT];



void timerInit()
{
    memset(timers, sizeof(timers), 0);
}

// called from inside interrupt
void timerTick(uint32_t totalTicks)
{
    int timerId;
    for (timerId = 0; timerId < TIMER_COUNT; timerId++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (timer->claimed)
        {
            if (timer->running)
            {
                if ((int)(totalTicks - timer->trigger) >= 0)
                {
                    // tick has reached the trigger
                    if (timer->repeat)
                    {
                        timer->trigger += timer->interval;
                    }
                    if (timer->callback)
                    {
                        timer->callback(totalTicks);
                    }
                }
            }
        }
    }
}

int timerGetAvailableTimerId()
{
    int timerId = -1;
    int i;
    for (i = 0; i < TIMER_COUNT; i++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (!timer->claimed)
        {
            timer->claimed = 1;
            timerId = i;
            break;
        }
    }
    return timerId;
}

void timerFree(int timerId)
{
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timers[timerId].claimed = 0;
}

void timerSet(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    assert(timer->claimed);
    timer->interval = interval;
    timer->repeat = repeat;
    timer->running = start;
    timer->callback = cb;
    timer->trigger = getTicks() + interval;
}

void timerGet(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    if (pTrigger)
    {
        *pTrigger = timer->trigger;
    }
    if (pInterval)
    {
        *pInterval = timer->interval;
    }
    if (pRepeat)
    {
        *pRepeat = timer->repeat;
    }
    if (pRunning)
    {
        *pRunning = timer->running;
    }
    if (pCb)
    {
        *pCb = timer->callback;
    }
}

void timerStop(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 0;
}

void timerRun(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 1;
}

void timerResetInterval(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->trigger = getTicks() + timer->interval;
}


idle.c:

#include "idle.h"
#include "interrupt.h"

volatile bool idling;

// DO NOT CALL FROM CRITICAL SECTION
void idle(void(*pIdleFn)(void* p), void* p)
{
    idling = true;
    // assert(interrupts enabled);

    while (idling)
    {
        // spinwait for tick to set idling to false
        if (pIdleFn)
        {
            pIdleFn(p);
        }
    }
}

void releaseIdle()
{
    idling = false;
}



No comments:

Post a Comment