-
Notifications
You must be signed in to change notification settings - Fork 3
/
CTR.spin
287 lines (221 loc) · 27.9 KB
/
CTR.spin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
''***************************************
''* Counter Modules v1.0.2 *
''* Author: Chip Gracey *
''* Copyright (c) 2006 Parallax, Inc. *
''* See end of file for terms of use. *
''***************************************
{{-----------------------REVISION HISTORY-----------------------
v1.0.1 - Updated 5/15/2006 to use TV_Terminal v1.1
v1.0.2 - Updated 11/10/2006 to fix error in Counter Mode table.
‣‣‣‣‣‣‣‣‣‣‣‣‣‣‣‣ CTRA / CTRB Description
Each COG has two counter modules. They are named CTRA and CTRB.
Each CTR module can control or monitor up to two I/O pins and perform
conditional 32-bit accumulation of its FRQ register into its PHS register on
every clock cycle.
Each CTR module also has its own phase-locked loop (PLL) which can be used to
synthesize frequencies up to 128 MHz.
With a little setup or oversight from the COG, a CTR can be used for:
frequency synthesis
frequency measurement
pulse counting
pulse measurement
multi-pin state measurement
pulse-width modulation
duty-cycle measurement
digital-to-analog conversion
analog-to-digital conversion
...and probably many other useful things
For some of these operations, the COG can "set and forget" a CTR. For others,
it may use WAITCNT to time-align CTR reads and writes within a loop, creating
the effect of a more complex state machine.
Note that for a COG clock frequency of 80 MHz, the CTR update period is a mere
12.5ns. This high speed, combined with 32-bit precision, allows for very
dynamic signal generation and measurement.
The overarching CTR design goal was to create a simple and flexible subsystem
which could perform some repetitive task on every clock cycle, thereby freeing
the COG to perform some computationally richer super-task. While the CTRs have
only 32 basic operating modes, there is no limit to how they might be used
dynamically through software. Integral to this concept is the use of the
WAITPEQ, WAITPNE, and WAITCNT instructions, which can event-align or time-align
a COG with its CTRs.
Each CTR has three registers:
CTRA / CTRB - Control register
The CTR register selects the CTR's operating mode. As soon as this register is
written, the new operating mode goes into effect. Writing a zero to CTR will
immediately disable the CTR, stopping all pin output and PHS accumulation.
FRQA / FRQB - Frequency register
FRQ holds the value that will be accumulated into the PHS register. For some
applications, FRQ may be written once, and then ignored. For others, it may be
rapidly modulated.
PHSA / PHSB - Phase register
The PHS register is the oddest of all COG registers. Not only can it be written
and read via COG instructions, but it also functions as a free-running
accumulator, summing the FRQ register into itself on potentially every clock
cycle. Any instruction writing to PHS will override any accumulation for that
clock cycle. PHS can only be read through the source operand (same as PAR, CNT,
INA, and INB). Beware that doing a read-modify-write instruction on PHS, like
"ADD PHSA,#1", will cause the last-written value to be used as the destination
operand input, rather than the current accumulation.
CTRA / CTRB registers
┌────┬─────────┬────────┬────────┬───────┬──────┬──────┐
Bits │ 31 │ 30..26 │ 25..23 │ 22..15 │ 14..9 │ 8..6 │ 5..0 │
├────┼─────────┼────────┼────────┼───────┼──────┼──────┤
Name │ ── │ CTRMODE │ PLLDIV │ ────── │ BPIN │ ──── │ APIN │
└────┴─────────┴────────┴────────┴───────┴──────┴──────┘
CTRMODE selects one of 32 operating modes for the CTR
- conveniently written (along with PLLDIV) using the MOVI instruction
PLLDIV selects a PLL output tap
- may be ignored if not used
BPIN selects a pin to be the secondary I/O
- may be ignored if not used
- %0xxxxx = Port A, %1xxxxx = Port B (future)
- conveniently written using the MOVD instruction
APIN selects a pin to be the primary I/O
- may be ignored if not used
- %0xxxxx = Port A, %1xxxxx = Port B (future)
- conveniently written using the MOVS instruction
Accumulate APIN BPIN
CTRMODE Description FRQ to PHS output* output*
┌────────┬─────────────────────────────┬────────────┬────────────┬────────────┐
│ %00000 │ Counter disabled (off) │ 0 (never) │ 0 (none) │ 0 (none) │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %00001 │ PLL internal (video mode) │ 1 (always) │ 0 │ 0 │
│ %00010 │ PLL single-ended │ 1 │ PLL │ 0 │
│ %00011 │ PLL differential │ 1 │ PLL │ !PLL │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %00100 │ NCO/PWM single-ended │ 1 │ PHS[31] │ 0 │
│ %00101 │ NCO/PWM differential │ 1 │ PHS[31] │ !PHS[31] │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %00110 │ DUTY single-ended │ 1 │ PHS-Carry │ 0 │
│ %00111 │ DUTY differential │ 1 │ PHS-Carry │ !PHS-Carry │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %01000 │ POS detector │ A¹ │ 0 │ 0 │
│ %01001 │ POS detector w/feedback │ A¹ │ 0 │ !A¹ │
│ %01010 │ POSEDGE detector │ A¹ & !A² │ 0 │ 0 │
│ %01011 │ POSEDGE detector w/feedback │ A¹ & !A² │ 0 │ !A¹ │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %01100 │ NEG detector │ !A¹ │ 0 │ 0 │
│ %01101 │ NEG detector w/feedback │ !A¹ │ 0 │ !A¹ │
│ %01110 │ NEGEDGE detector │ !A¹ & A² │ 0 │ 0 │
│ %01111 │ NEGEDGE detector w/feedback │ !A¹ & A² │ 0 │ !A¹ │
├────────┼─────────────────────────────┼────────────┼────────────┼────────────┤
│ %10000 │ LOGIC never │ 0 │ 0 │ 0 │
│ %10001 │ LOGIC !A & !B │ !A¹ & !B¹ │ 0 │ 0 │
│ %10010 │ LOGIC A & !B │ A¹ & !B¹ │ 0 │ 0 │
│ %10011 │ LOGIC !B │ !B¹ │ 0 │ 0 │
│ %10100 │ LOGIC !A & B │ !A¹ & B¹ │ 0 │ 0 │
│ %10101 │ LOGIC !A │ !A¹ │ 0 │ 0 │
│ %10110 │ LOGIC A <> B │ A¹ <> B¹ │ 0 │ 0 │
│ %10111 │ LOGIC !A | !B │ !A¹ | !B¹ │ 0 │ 0 │
│ %11000 │ LOGIC A & B │ A¹ & B¹ │ 0 │ 0 │
│ %11001 │ LOGIC A == B │ A¹ == B¹ │ 0 │ 0 │
│ %11010 │ LOGIC A │ A¹ │ 0 │ 0 │
│ %11011 │ LOGIC A | !B │ A¹ | !B¹ │ 0 │ 0 │
│ %11100 │ LOGIC B │ B¹ │ 0 │ 0 │
│ %11101 │ LOGIC !A | B │ !A¹ | B¹ │ 0 │ 0 │
│ %11110 │ LOGIC A | B │ A¹ | B¹ │ 0 │ 0 │
│ %11111 │ LOGIC always │ 1 │ 0 │ 0 │
└────────┴─────────────────────────────┴────────────┴────────────┴────────────┘
* must set corresponding DIR bit to affect pin
A¹ = APIN input delayed by 1 clock
A² = APIN input delayed by 2 clocks
B¹ = BPIN input delayed by 1 clock
┌────────┬────────────┐
│ PLLDIV │ Output │ The PLL modes (%00001..%00011) cause FRQ-to-PHS
├────────┼────────────┤ accumulation to occur every clock cycle. This
│ %000 │ VCO ÷ 128 │ creates a numerically-controlled oscillator (NCO)
├────────┼────────────┤ in PHS[31], which feeds the CTR PLL's reference
│ %001 │ VCO ÷ 64 │ input. The PLL will multiply this frequency by 16
├────────┼────────────┤ using its voltage-controlled oscillator (VCO).
│ %010 │ VCO ÷ 32 │
├────────┼────────────┤ For stable operation, it is recommended that the
│ %011 │ VCO ÷ 16 │ VCO frequency be kept within 64 MHz to 128 MHz.
├────────┼────────────┤ This translates to an NCO frequency of 4 MHz to
│ %100 │ VCO ÷ 8 │ 8 MHz.
├────────┼────────────┤
│ %101 │ VCO ÷ 4 │ The PLLDIV field of the CTR register selects
├────────┼────────────┤ which power-of-two division of the VCO frequency
│ %110 │ VCO ÷ 2 │ will be used as the final PLL output. This
├────────┼────────────┤ affords a PLL range of 500 KHz to 128 MHz.
│ %111 │ VCO ÷ 1 │
└────────┴────────────┘
}}
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
OBJ
term : "TV_Terminal"
VAR
long ctr, frq
PUB Go | freq
'' Synthesize frequencies on pin 0 and pin 1
'' also show FRQ values on tv
term.start(12) 'start tv terminal
SynthFreq(0, 1_000_000) 'determine ctr and frq for pin 0
CTRA := ctr 'set CTRA
FRQA := frq 'set FRQA
DIRA[0]~~ 'make pin output
term.str(string("CTRA = ")) 'show CTRA value
term.hex(ctr,8)
term.str(string(" FRQA = ")) 'show FRQA value
term.hex(frq,8)
SynthFreq(1, 1_000_001) 'determine ctr and frq for pin1
CTRB := ctr 'set CTRB
FRQB := frq 'set FRQB
DIRA[1]~~ 'make pin output
term.str(string(13, "CTRB = ")) 'show CTRB value
term.hex(ctr,8)
term.str(string(" FRQB = ")) 'show FRQB value
term.hex(frq,8)
repeat 'loop forever to keep cog alive
waitcnt(0) 'waitcnt to reduce power
PRI SynthFreq(Pin, Freq) | s, d
'' Determine CTR settings for synthesis of 0..128 MHz in 1 Hz steps
''
'' in: Pin = pin to output frequency on
'' Freq = actual Hz to synthesize
''
'' out: ctr and frq hold CTRA/CTRB and FRQA/FRQB values
''
'' Uses NCO mode %00100 for 0..499_999 Hz
'' Uses PLL mode %00010 for 500_000..128_000_000 Hz
''
Freq := Freq #> 0 <# 128_000_000 'limit frequency range
if Freq < 500_000 'if 0 to 499_999 Hz,
ctr := constant(%00100 << 26) '..set NCO mode
s := 1 '..shift = 1
else 'if 500_000 to 128_000_000 Hz,
ctr := constant(%00010 << 26) '..set PLL mode
d := >|((Freq - 1) / 1_000_000) 'determine PLLDIV
s := 4 - d 'determine shift
ctr |= d << 23 'set PLLDIV
frq := fraction(Freq, CLKFREQ, s) 'Compute FRQA/FRQB value
ctr |= Pin 'set PINA to complete CTRA/CTRB value
PRI fraction(a, b, shift) : f
if shift > 0 'if shift, pre-shift a or b left
a <<= shift 'to maintain significant bits while
if shift < 0 'insuring proper result
b <<= -shift
repeat 32 'perform long division of a/b
f <<= 1
if a => b
a -= b
f++
a <<= 1
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}