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