-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathavrprog-usb-core.mu4
347 lines (265 loc) · 10.5 KB
/
avrprog-usb-core.mu4
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2025 David Frech. (Read the LICENSE for details.)
loading SPI over USB (AVR programming support) (core)
( Check for compatibility with device.)
.reg EPCTL0 .not .if
error" Selected device doesn't support USB."
.then
__meta
| Support for programming Atmel's 8-bit AVR family, using my JS16 as a
| USB-based programmer.
|
| Because we want to be independent of the USB byteloader code, we have to
| re-implement a lot of that code: the command dispatch table, and the
| basic handling of SETUP, IN, and OUT tokens. But code to enumerate the
| device, and the device descriptors, are *not* repeated here. We only
| implement the "vendor specific" requests required to toggle pins and talk
| SPI.
hex
40 constant buflen0 ( buffer length, and hence max packet size, for endpoint 0)
1860 equ 'usb ( USB RAM origin; buffer descriptors live here)
1880 equ 'usb-buffers ( endpoint RAM from here to 195f)
__host
: >bufaddr ( buffer - bufaddr)
dup 0f and if error" endpoint buffer must be on a 16-byte boundary" then
\eq 'usb - 2 >> ;
__meta
| Buffer descriptor control byte to match _any_ SETUP transaction, or an IN
| or OUT transaction with a DATA1 packet.
0c8 equ match-data1
( USB buffer descriptors)
1860 equ in-control
1861 equ in-count
1862 equ in-bufaddr
1863 equ out-control
1864 equ out-count
1865 equ out-bufaddr
'usb-buffers equ in-buffer
'usb-buffers buflen0 + equ out-buffer
( Useful aliases for buffer addresses - for control requests.)
out-buffer 0 + equ bmRequestType
out-buffer 1 + equ bRequest
out-buffer 2 + equ wValue
out-buffer 3 + equ wValueHi
out-buffer 4 + equ wIndex
out-buffer 5 + equ wIndexHi
out-buffer 6 + equ wLength
out-buffer 7 + equ wLengthHi
( Create a forward jump at the start of the flash sector.)
forward-jmp avr-prog-loop
.ifdef S08JS
| Pinout of SPI pins:
| 12 PTA4/nSS
| 11 PTA3/SPSCK
| 10 PTA2/MOSI
| 9 PTA1/MISO
aka PTAD PortA
aka PTADD DirA
label ResetHigh
PortA 4 bset
DirA 4 bset
rts ;c
label ResetLow
PortA 4 bclr
DirA 4 bset
rts ;c
label InitSPI
%0101_0000 # ( cpol=0, cpha=0, enable spi, master mode) SPIC1 ) mov
00 # ( 8-bit mode, /SS disable, separate pins) SPIC2 ) mov
%0001_1100 # lda PTADS ) sta ( high drive on SCK, /SS, and MOSI)
( fall thru)
.else .def S08JM16 .def S08JM60 .or .if
aka PTED PortE
aka PTEDD DirE
label ResetHigh
PortE 7 bset
DirE 7 bset
rts ;c
label ResetLow
PortE 7 bclr
DirE 7 bset
rts ;c
label InitSPI
%0101_0000 # ( cpol=0, cpha=0, enable spi, master mode) SPIC1 ) mov
00 # ( 8-bit mode, /SS disable, separate pins) SPIC2 ) mov
%1110_0000 # lda PTEDS ) sta ( high drive on SCK, /SS, and MOSI)
( fall thru)
.else error" Unsupported SPI"
.then .then
label SlowClock
| 53 # SPIBR ) mov ( 24M / 6 / 16 = 250k) rts ;c
54 # SPIBR ) mov ( 24M / 6 / 32 = 125k) rts ;c
| 55 # SPIBR ) mov ( 24M / 6 / 64 = 62.5k) rts ;c
label FastClock
50 # SPIBR ) mov ( 24M / 6 / 2 = 2M) rts ;c
label DisableSPI
SPIC1 ) clr rts ;c
label avr-write-commands ( all are control writes)
( 23) ResetLow .a dec 0!= until
( 24) ResetHigh .a dec 0!= until
( 25) SlowClock ( 250k) .a dec 0!= until
( 26) FastClock ( 2M) .a dec 0!= until
( unknown, fall thru)
label stall
EPCTL0 1 bset ( STALL) rts ;c
| The JS and JM parts - the USB parts that this code is going to run on -
| both have 16-bit-capable SPI ports, so we specify SPIDL instead of SPID
| for the SPI data register.
label SpiTx ( Sends char in A; returns recvd char in A)
begin SPIS 5 ( xmit empty) bset? until SPIDL ) sta
begin SPIS 7 ( recvr full) bset? until SPIDL ) lda rts ;c
( I put these here so they are closer to the code that uses them.)
1 zvar avr-cmd
1 zvar addr-hi
1 zvar addr-lo
1 zvar toggle
2 zvar memcount ( 16-bit count, big-endian!)
( Set endpoint count byte and toggle DATA0/1 bit.)
label ready-endpt ( HX points to EP buffer control byte, A has count)
1 ,x sta ( count)
0 ,x lda 40 # and ( data0/1) match-data1 # eor 0 ,x sta rts ;c
| wValue and following bytes have the following meaning:
| avr-cmd addr-hi addr-lo dummy/toggle
label save-command
wLength ) lda memcount 1+ ) sta
wLength 1+ ) lda memcount ) sta
wValue ) ldhx avr-cmd ) sthx
wIndex ) ldhx addr-lo ) sthx
rts ;c
label bulk-read
save-command c
( fall thru)
| We always want to queue up either a short packet or a zero-length packet
| as the last packet. In particular this means that if the last part of the
| string _exactly_ fills a packet, we queue up a zero-length to follow it,
| to make sure to signal to the host that this is the end of the data
| stage. See USB spec 8.5.3.2.
( Read data from chip and copy into IN buffer.)
( Exit with in-count in X)
label prepare-next-in
memcount ) ldhx 0!= if ( still data to read)
buflen0 # cphx u> if ( whole packet) buflen0 # ldx then
.a clr .a psh ( bufptr) .x psh ( count)
begin
.h clr .x clr
( resend command, addr, and dummy bytes)
begin avr-cmd ,x lda SpiTx c .x inc 4 # cpx 0= until
( put last byte returned into in-buffer)
1 ,s ldx in-buffer ,x sta 1 ,s inc
memcount ) ldhx -1 # aix ( decr count) memcount ) sthx
( toggle cmd byte and incr addr-lo if toggled back to 0)
toggle ) lda avr-cmd ) eor avr-cmd ) sta
toggle ) and 0= if addr-lo ) inc ( in zpage!) then
0 ,s decz? until
.a pul .x pul ( bufptr; this is in-count)
then
( in-count in x)
( fall thru)
( Enter with in-count in X)
label prepare-in-xcount
txa in-control # ldhx ready-endpt c ( fall thru) ;c
| Get ready to receive another SETUP DATA0 token, or an OUT DATA1 token.
| The host will send an empty DATA1 as a status stage.
label expect-setup-token
.a clr out-control ) sta ( fall thru) ;c
label expect-out-token ( expect OUT or SETUP, really)
buflen0 # lda out-control # ldhx ready-endpt c
CTL 5 bclr ( TSUSPEND off - resume processing)
rts ;c
| Instruction payload - 4 bytes - is in the SETUP packet, starting at
| wValue. It uses the wValue and wIndex fields. Bytes returned by the AVR
| are stored in the IN buffer for later retrieval by read-result.
label single-avr-cmd
.h clr .x clr
begin wValue ,x lda SpiTx c in-buffer ,x sta .x inc
4 # cpx 0= until
bmRequestType ) lda 80 # and ( read) prepare-in-xcount 0= until
( otherwise write, so fall thru)
| Prepare to return status - a zero-length IN transaction. Also be ready to
| accept another SETUP even if it would mean abandoning the current
| transfer. This is what the USB spec says we have to do.
label finish-control-write
( Setup IN status stage)
.x clr ( zero-length DATA1 transaction) prepare-in-xcount j ;c
label bulk-write
wLength ) ldhx finish-control-write 0!= until
save-command c
expect-setup-token j ( wait for OUT or new SETUP) ;c
| Receipt of SETUP has set out-control's data toggle to 0. Set in-control's
| as well.
label setup-token
.a clr in-control ) sta ( Reset IN endpoint to DATA0.)
( NOTE: Command numbers are all given in HEX.)
( assume vendor request)
bRequest ) lda
20 # sub ( BDM commands start at 20 hex; anything below that, we exit)
u< if ( 00 - 1f Bye) 2 # ais ( skip return from process-usb)
DisableSPI c ResetHigh j then
( 20 Single) 0!= single-avr-cmd until ( can be read or write!)
( 21 Read) .a dec 0= if bulk-read j then
( 22 Write) .a dec 0!= bulk-write until
avr-write-commands c ( these are all control writes, so ...)
finish-control-write j ;c
label setup-or-out-token
out-control ) lda 3c # and 34 # cmpne? setup-token until
( OUT token)
| If last OUT was zero-length, it was a status transaction for a
| control read, so get ready for a new SETUP.
out-count ) ldx expect-setup-token 0!= until
| Otherwise, we've got a bulk write data payload. Write bytes to target
| via SPI, by repeating single byte write commands. Be careful about
| toggling the command byte for program memory reads and writes.
.a clr .a psh ( bufptr) .x psh ( out-count)
begin
.h clr .x clr
( resend command & addr bytes)
begin avr-cmd ,x lda SpiTx c .x inc 3 # cpx 0= until
( send byte from out-buffer)
1 ,s ldx out-buffer ,x lda SpiTx c 1 ,s inc
memcount ) ldhx -1 # aix ( decr count) memcount ) sthx
( toggle cmd byte and incr addr-lo if toggled back to 0)
toggle ) lda avr-cmd ) eor avr-cmd ) sta
toggle ) and 0= if addr-lo ) inc ( in zpage!) then
0 ,s decz? until 2 # ais
( If memcount zero, we've received all the data; queue up status IN token.)
memcount ) ldhx finish-control-write 0!= until
| memcount ) ldhx 0= if finish-control-write j then
( memcount non-zero; expect more data.)
expect-out-token j ;c
| An IN token is either part of a data stage of a control read, or it is
| the status stage of a control write. Status IN's are always zero bytes
| long; sometimes the last IN of the data stage is also zero bytes long -
| see copy-to-in for details.
|
| The upshot is that if the last packet wasn't full we should expect no
| more IN transactions.
label in-token
( If last IN was a full packet, ready some more data.)
in-count ) lda buflen0 # cmp 0= if prepare-next-in j then
( Last packet was partial or zero-length; no further IN's expected.)
expect-setup-token j ;c
| XXX To convert process-usb into an interrupt handler, jumps to
| setup-or-out-token, in-token, and nak-in-expect-setup should really be
| _calls_; we should handle all the flags that are set all at once, rather
| than only one per call. Also, interrupt flags - like TOKDNEF and STALLF -
| should be cleared after being handled.
label process-usb
INTSTAT 3 bset? if ( TOKDNEF)
STAT ) lda INTSTAT 3 bset ( ack TOKDNEF)
0f8 # and setup-or-out-token 0!= until
08 # cmpne? in-token until
( not EP0; ignore) ( fall thru)
then
INTSTAT 7 bset? if ( STALLF)
EPCTL0 1 bclr ( remove EPSTALL)
INTSTAT 7 bset ( ack STALL)
expect-setup-token j ( get ready to receive SETUP)
then
rts ;c
\m avr-prog-loop resolve>> ( jump at start of Flash points here)
label main-loop
ResetHigh c ( drive /RESET high)
InitSPI c ( setup SPI interface)
finish-control-write c ( ready status IN; also prepare for SETUP)
begin process-usb c again ;c