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.
1 // This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
2 // clock in. Scales that down to 125MHz with a 4 bit counter.
3 // Then it counts 125 of those in a 7 bit counter that resets on 7c.
4 // When the reset occurs, the pin_nINT line goes from Hi-Z to low.
5 //
6 // The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
7 //
8 // SCK and Dout are used to read the 3 bit tick counter. This is used
9 // to ensure that the count is kept synched. Dout is updated on the
10 // falling edge of SCK and the MSB is shifted first.
11 //
12
13
14 module timer (
15 input pin_CLK ,
16 input pin_nCS ,
17 input pin_SCK ,
18 output pin_Dout ,
19 output pin_nINT
20 /*
21 output [3:0] test_prescaler,
22 output [6:0] test_counter,
23 output [2:0] test_ticks,
24 output [2:0] test_tickShift,
25 output test_nCount,
26 output test_nTick,
27 output test_nInt,
28 output test_nCS0,
29 output test_nCS1
30 */
31 );
32
33 /*
34 assign test_prescaler = prescaler;
35 assign test_counter = counter;
36 assign test_ticks = ticks;
37 assign test_tickShift = tickShift;
38 assign test_nCount = nCount;
39 assign test_nTick = nTick;
40 assign test_nInt = nInt;
41 assign test_nCS0 = nCS0;
42 assign test_nCS1 = nCS1;
43 */
44
45 reg [ 3 : 0 ] prescaler ;
46 reg [ 6 : 0 ] counter ;
47 reg [ 2 : 0 ] ticks ;
48 reg [ 2 : 0 ] tickShift ;
49 reg nCount ;
50 reg nTick ;
51 reg nInt ;
52 reg nCS0 ;
53 reg nCS1 ;
54 reg SCK0 ;
55 reg SCK1 ;
56
57 initial begin
58 prescaler = 0 ;
59 counter = 0 ;
60 ticks = 0 ;
61 end
62
63 assign pin_nINT = ( nInt == 1 'b1 ) ? 1 ' bz : 1 'b0 ;
64
65 always @( posedge pin_CLK ) begin
66 prescaler <= prescaler + 1 ;
67 nCount <= ~ ( & prescaler ); // 0 once every 16 pin_CLK
68 nTick <= ~ ( counter == 7 'b1111100 ); // 124 caused 125->0
69 if ( nCount == 1 'b0 ) begin
70 if ( nTick == 1 'b0 ) begin
71 counter <= 0 ;
72 ticks <= ticks + 1 ;
73 end
74 else begin
75 counter <= counter + 1 ;
76 end
77 end
78
79 nCS0 <= pin_nCS ;
80 nCS1 <= nCS0 ;
81
82 // Set interrupt on the CLK. Reset it on the falling edge of
83 // pin_nCS+2 clocks
84 if (( nTick | nCount ) == 1 'b0 ) begin
85 nInt <= 1 'b0 ;
86 end
87 else if (( nCS1 & ( ~ nCS0 )) == 1 'b1 ) begin
88 nInt <= 1 'b1 ;
89 end
90
91 SCK0 <= pin_SCK ;
92 SCK1 <= SCK0 ;
93 if ( ~ pin_nCS ) begin
94 //if (SCK1 & ~SCK0) begin
95 if ( SCK0 & ~ pin_SCK ) begin
96 tickShift [ 2 : 0 ] <= { tickShift [ 1 : 0 ], 1 'b0 };
97 end
98 end
99 else begin
100 tickShift [ 2 : 0 ] <= ticks [ 2 : 0 ];
101 end
102
103 end
104 assign pin_Dout = pin_nCS ? 1 ' bz : tickShift [ 2 ];
105
106 endmodule