-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathhimem_flash.spin2
320 lines (286 loc) · 8.61 KB
/
himem_flash.spin2
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
''
'' loader for high memory
'' flash version
''
'' this will be loaded to $FC000 and run from there,
'' so hub addresses need to be relocatable
''
'' The main loader will call the original entry point at
'' $FC000, which should do any initialization and return
'' a pointer to a mailbox to use in ptra
''
'' This initialization should generally consist of firing
'' up a COG which will handle the writes asynchonously
'' (so the main code can continue to handle serial I/O).
''
'' When the main loader needs to write data, it will set up
'' the mailbox like:
'' +0 = hub address (with highest bit set to indicate we are running)
'' +4 = flash address
'' +8 = size in bytes (currently always <=1K)
''
'' when the writer is finished, it should overwrite the
'' first mailbox word with either 0 (for OK) or a pointer to an
'' error message to send back:
'' +0 = 0 or positive error message
''
'' when the main loader is completely finished, it will set
'' the hub address to -1: this indicates to the writer that it should
'' shut down
''
'' The initialization code is allowed to use pa, pb, ptra, and ptrb
'' as well as registers $1d0-$1e0
''
'' The actual low level flash writing code is based on SpiFlash.spin2, by
'' Nicolas Benezan and Evan Hillas
''
con
spi_cs = 61
spi_ck = 60
spi_di = 59 ' P2 -> flash
spi_do = 58 ' flash -> P2
modeCLK = P_TRANSITION | P_OE | P_INVERT_OUTPUT ' SPI_CK
modeTXD = P_SYNC_TX | P_INVERT_B | P_OE | ((spi_ck - spi_di) & %111)<<24 ' + P_SYNC_IO + P_FILT0_AB
modeRXD = P_SYNC_RX |((spi_ck - spi_do) & %111)<<24
Page_Size = 256
Write_Disable = $04
Write_Enable = $06
Erase_4k = $20
Erase_64k = $D8
Write_Page = $02
Read_Data = $03
Read_Status = $05
Reset_Enable = $66
Reset_Device = $99
Program_Resume = $7a
PD_Release = $ab
dat
orgh
enter
mov pa, #16
loc pb, #code
loc ptra, #mailbox
setq ptra
coginit pa, pb
ret
mailbox
long 0[4]
' error messages must follow mailbox
err_badaddr
byte "address $XXXXXXXX must be a multiple of 256", 0
' and here comes the code
alignl
code
''
'' the actual driver code
'' entered with ptra pointing at the mailbox
'' (we'll leave it that way)
''
org 0
' initialize
Init
call #Spi_Init
mov cmd, #PD_Release
call #Spi_Cmd8
drvh #spi_cs
waitx ##22_000 ' roughly 1 ms
mov cmd, #Reset_Enable
call #Spi_Cmd8
mov cmd, #Reset_Device
call #Spi_Cmd8
call #Spi_Wait
' wait for request to arrive in mailbox
mainloop
setq #3
rdlong params, ptra
tjz hubaddr, #mainloop
' check initial address
' process the request
process_request
tjf hubaddr, #endit ' address of -1 means quit
test ouraddr, #$FF wz ' check for bottom address bits all 0
if_nz jmp #bad_address
tjz size, #done_write ' skip everything if no data to write
and ouraddr, ##$00FF_FFFF ' erase high bits of address
and hubaddr, ##$00FF_FFFF ' erase high bits of address
' walk through writing pages
.page_loop
test ouraddr, ##$FFF wz ' check for 4K boundary
if_z call #Erase ' if on boundary, erase this page
' now write a page of 256 bytes
' pad page with $FF if necessary
cmp size, #256 wcz
if_b call #pad_page
call #WritePage
sub size, #256 wcz
if_a jmp #.page_loop
done_write
' send NULL for error pointer (means OK)
mov params, #0
send_response
wrlong params, ptra
jmp #mainloop
endit
' shut down the smart pins
fltl #spi_cs
fltl #spi_ck ' reset smart pins
fltl #spi_di
fltl #spi_do
wrpin #0, #spi_ck
wrpin #0, #spi_di
wrpin #0, #spi_do
cogid pb
cogstop pb
bad_address
mov params, ptra
add params, #(@err_badaddr-@mailbox)
' fill in the address
mov pb, #8
mov ptrb, params
add ptrb, #9
.addr
getnib tmp, ouraddr, #7
rol ouraddr, #4
add tmp, #"0"
cmp tmp, #"9" wcz
if_ae add tmp, #"A"-"0"
wrbyte tmp, ptrb++
djnz pb, #.addr
jmp #send_response
''
'' pad a page of data with $FF
''
pad_page
mov padsize, size
subr padsize, #256 ' padsize = 256 - padsize
mov ptrb, size
and ptrb, #$ff
add ptrb, hubaddr
rep @.loopend, padsize
wrbyte #$FF, ptrb++
.loopend
ret
''
'' medium level SPI routines
''
'' erase a 4K page of data at ouraddr
Erase
call #Spi_Init
mov cmd, #Write_Enable
call #Spi_Cmd8
mov cmd, #Erase_4k
mov adr, ouraddr
call #Spi_Cmd32
jmp #Spi_Wait
'' write a page of 256 bytes from hubaddr to ouraddr
WritePage
call #Spi_Init
mov cmd, #Write_Enable
call #Spi_Cmd8
mov cmd, #Write_Page
mov adr, ouraddr
add ouraddr, #256
call #Spi_Cmd32
mov rep1, #64
.wp_loop
rdlong data, hubaddr
add hubaddr, #4
call #Spi_Wr4Bytes
djnz rep1, #.wp_loop
jmp #Spi_Wait
''
'' low level SPI routines
''
'' initialize SPI for flash
Spi_Init
drvh #spi_cs
fltl #spi_ck ' reset smart pins
fltl #spi_di
fltl #spi_do
wrpin ##modeClk,#spi_ck ' smart pin transition mode
wrpin ##modeTXD,#spi_di ' smart pin synchronous serial transmit
wrpin ##modeRXD,#spi_do ' smart pin synchronous serial receive
wxpin #2,#spi_ck ' clock transition base period (sysclock/4)
dirh #spi_ck ' enable smart pin
ret
'' outputs 8 bits command, MSB first
'' command is in "cmd" register
SPI_Cmd8
outh #spi_cs
waitx #6 ' 12+6 = 18 ticks (50 ns minimum CS deselect for write commands)
shl cmd,#24 ' shift command up
rev cmd
wxpin #7, #spi_di ' 8 bits, continuous mode
wypin cmd,#spi_di
outl #spi_cs
wypin #16,#spi_ck ' start CLK sequence (16 transitions = 8 pulses)
dirh #spi_di ' enable TX smart pin
.waitRdy testp #spi_ck wz ' IN=1 if clocking finished
if_nz jmp #.waitRdy ' wait until last CLK sequence finished
dirl #spi_di
ret
' outputs 4 bytes: 8 bit command + 24 bits adr
Spi_Cmd32
outh #spi_cs
waitx #4 ' 14+4 = 18 ticks (50 ns minimum CS deselect for write commands)
shl cmd,#24 'shift command up
or cmd,adr 'or in address
rev cmd
wxpin #31, #spi_di ' 32 bits, continuous mode
wypin cmd,#spi_di
outl #spi_cs
wypin #64,#spi_ck ' start CLK sequence (64 transitions = 32 pulses)
dirh #spi_di ' enable TX smart pin
.waitRdy testp #spi_ck wz ' IN=1 if clocking finished
if_nz jmp #.waitRdy ' wait until last CLK sequence finished
dirl #spi_di
ret
'' write 4 bytes of data LSB first (little endian)
Spi_Wr4Bytes
movbyts data,#%00_01_10_11
rev data
wxpin #31, #spi_di ' 32 bits, continuous mode
wypin data,#spi_di
wypin #64,#spi_ck ' start CLK sequence (64 transitions = 32 pulses)
dirh #spi_di ' enable TX smart pin
.waitRdy testp #spi_ck wz ' IN=1 if clocking finished
if_nz jmp #.waitRdy ' wait until last CLK sequence finished
dirl #spi_di
ret
'' read 8 bits into data
Spi_RdByte
wxpin #7 | 1<<5, #spi_do ' 8 bits, with hold time (post-clock sampling)
wypin #16,#spi_ck ' start 16 clock transitions
dirh #spi_do ' enable RX smart pin
.waitDone testp #spi_do wz ' IN=1 if byte received
if_nz jmp #.waitDone ' wait until byte received
rdpin data,#spi_do
rev data
zerox data,#7 ' limit to 8 bits
dirl #spi_do
ret
Spi_Wait
mov cmd, #Read_Status
call #Spi_Cmd8
call #Spi_RdByte
test data, #$1 wz
if_nz jmp #Spi_Wait
drvh #spi_cs
ret
' and our variables
cmd res 1
adr res 1
data res 1
rep1 res 1
padsize res 1
params
hubaddr
res 1
ouraddr
res 1
size
res 1
reserved
res 1
tmp
res 1