-
Notifications
You must be signed in to change notification settings - Fork 30
/
chat-asm.mu4
493 lines (392 loc) · 13.9 KB
/
chat-asm.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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading RISC-V serial chat protocol (core)
| Taking inspiration from the wildly successful S08 serial chat protocol.
|
| Responds to the following commands. NOTE: these are hex values!
|
| 00 - 0f idle - these command bytes are ignored
|
| 10 version - get the 32-bit version commit
| 11 set address - set the memory address pointer
| 12 read words - read N words, incrementing by 4 as we go
| 13 write word - write a word to memory, incr pointer by 4
| 14 get status - get sp
| 15 run - set sp and pc and execute
|
| 16 - ff idle - these command bytes are ignored
__meta
hex
abi-regs
( Forward references.)
hook reset-entry
( Our two "millicode" routines.)
| lr should be something outside of C reg set; we are using standard ABI
| "alternate link register" x5/t0.
label nest
s2 ra rpush2 0 t0 jr ;c
label unnest
s2 ra rpop2 0 t0 jr ;c
label exit
unnest t0 jal 0 ra jr ;c
__host
assembler
: c asm{ ra jal } ;
: ret asm{ 0 ra jr } ;
: {{ asm{ nest t0 jal } ;
: }} asm{ unnest t0 jal } ;
: }}; asm{ exit j } ;
forth
__meta
.equates. .contains uart0.rxdata .if
( Support words for SiFive HiFive1.)
#160,000,000 equ cpuclk
label b> ( - b)
uart0.rxdata a1 lui
begin uart0.rxdata a1 a0 lw a0 0>= until
"0ff a0 a0 andi ret ;c
label >b ( b)
uart0.txdata a1 lui
begin uart0.txdata a1 a2 lw a2 0>= until
uart0.txdata a1 a0 sw ret ;c
label uart-init
uart0.txdata a1 lui ( get address of UART0 block)
1 a0 li uart0.txctrl a1 a0 sw ( set txen)
uart0.rxctrl a1 a0 sw ( set rxen)
cpuclk 2* #115200 / 1+ 2/ 1- ( divisor) a0 li uart0.div a1 a0 sw
( Set GPIO IOF bits to allow UART to talk to pins. UART0 is pins 16 & 17.)
gpio.iof_en a1 lui
gpio.iof_en a1 a2 addi ( full address)
0003_0000 a0 lui ( mask with bits 16 and 17 set)
a2 a0 x0 amoor.w ( addr src dest)
gpio.iof_sel a1 a2 addi ( full address)
a0 a0 not ( mask with bits 16 and 17 clear)
a2 a0 x0 amoand.w ( addr src dest)
ret ;c
( Iteration count in a0.)
label delay begin -1 a0 a0 addi a0 0= until ret ;c
label clock-init
( Turn on external 16M xtal oscillator and wait for it to start up.)
hfxosccfg a1 lui
hfxosccfg a1 a2 addi ( full address)
4000_0000 a0 lui ( enable)
begin a2 a0 a3 ( addr src dest) amoswap.w a3 0< until ( until ready bit)
( Get full address of pllcfg into a2. We are going to be accessing it a lot!)
pllcfg a1 a2 addi ( full address)
( When not executing out of reset, turn off pll before re-configuring.)
.ifdef in-ram
1_0000 a0 lui ( pllsel) a0 a0 not
a2 a0 x0 amoand.w ( clear pllsel bit; run from hfrclk)
4_0000 a0 lui ( pllbypass)
a2 a0 x0 amoor.w ( set pllbypass bit: turn off pll)
begin 0 a2 a0 lw a0 0>= until ( pll not locked)
.then
( Set PLL to 16M /2 *80 /4 = 160M, running the VCO at 640M.)
( Q=/4 F=*80 R=/2 + pllbypass+pllrefsel)
%110_0000_10_100111_00_01 a0 la ( full value)
0 a2 a0 sw
( Turn on PLL by clearing pllbypass bit.)
4_0000 a0 lui ( pllbypass) a0 a0 not
a2 a0 x0 amoand.w ( clear pllbypass bit: turn on pll)
| Delay at least 100us. This count will give us a delay of at least
| 300us and probably closer to 500us.
1000 a0 lui {{ delay c }}
( Wait for PLL to lock.)
begin 0 a2 a0 lw a0 0< until ( plllock)
( Finally, switch clock source to PLL.)
1_0000 ( pllsel) a0 lui a2 a0 x0 amoor.w
ret ;c
.ifdef **unused**
label >hfxosc
hfxosccfg a1 lui
hfxosccfg a1 a2 addi ( full address)
4000_0000 a0 lui ( enable)
begin a2 a0 a3 ( addr src dest) amoswap.w a3 0< until ( until ready bit)
pllcfg a1 a2 addi ( full address)
0007_0000 a0 lui ( pllbypass+pllrefsel+pllsel)
a2 a0 x0 amoor.w ( addr src dest)
ret ;c
.then
| At reset the SPI interface that reads the flash is set to run at 1/8 of
| the cpu/bus clock speed. The formula is f/2*(div+1), where f is the
| CPU/bus clock speed, and div is the value stored in the spi0.sckdiv
| register. At reset sckdiv is set to 3.
|
| We are now running the CPU at 160MHz. To run the SPI interface at 16MHz we
| need a divisor of 10, so div=4.
label set-spi-speed
cpuclk #16,000,000 ( spi clk) / 2/ 1- a0 li
spi0.sckdiv dup a1 lui a1 a0 sw ret ;c
| GPIO pins connected to RGB led. These are active low, so we *set* the
| gpio.out_xor bits for these pins.
1 #22 << equ red
1 #19 << equ green
1 #21 << equ blue
red green + equ yellow ( aka -blue)
red blue + equ magenta ( aka -green)
green blue + equ cyan ( aka -red)
label led-init
gpio.iof_en a1 lui ( base addr)
red green blue + + a0 lui ( bit set mask)
gpio.output_en a1 a2 addi ( full address)
a2 a0 x0 amoor.w
gpio.out_xor a1 a2 addi ( full address)
a2 a0 x0 amoor.w ( we want the port bits to be 1 for on)
a0 a0 not ( bit clear mask)
gpio.iof_en a1 a2 addi ( full address)
a2 a0 x0 amoand.w ( bits belong to the pins)
gpio.port a1 a2 addi ( full address)
a2 a0 x0 amoand.w ( turn LEDs off)
ret ;c
label on-time
40_0000 a0 lui delay j ;c
label off-time
20_0000 a0 lui delay j ;c
( a0 has bits to set/reset; a1 has port address.)
label blink {{
a0 s2 mv
a1 s2 x0 amoor.w ( on) on-time c s2 s2 not
a1 s2 x0 amoand.w ( off) }} off-time j ;c
label blink-leds
{{ gpio.port a1 la ( full address)
cyan a0 lui blink c
magenta a0 lui blink c
yellow a0 lui }} blink j ;c
label chip-init
{{ clock-init c uart-init c set-spi-speed c led-init c }}
blink-leds j ;c
.then ( SiFive HiFive1 support)
.equates. .contains USART1_SR .if
( Support words for GigaDevice GD32VF103.)
( NOTE: On GD32VF103, USART and GPIO registers are *word* access only!)
label ?status ( bit in a0)
USART1_SR a1 lui
begin USART1_SR a1 a3 lw a0 a3 a3 and a3 0!= until
ret ;c
label b> ( - b)
{{ 1 #5 << ( RXNE bit) a0 li ?status c
USART1_DR a1 a0 lw ( read data byte) }};
label >b ( b)
{{ a0 s2 mv 1 #7 << ( TXE bit) a0 li ?status c
USART1_DR a1 s2 sw ( write data byte) }};
label clock-init
RCC_CR a1 lui RCC_CFGR a1 x0 sb ( request HSI as sysclk)
begin RCC_CFGR a1 a0 lbu a0 0= until
1 a0 li ( HSEON, PLLOFF) RCC_CR 2 + a1 a0 sh
2 a0 li ( HSERDY)
begin RCC_CR 2 + a1 a2 lhu a0 a2 a2 and a2 a0 = until ( PLLRDY=0, HSERDY=1)
RCC_CFGR2 a1 x0 sw ( clear all bits)
0051_0400 a0 la ( USBPRE=/1; PLLMUL=6; PLLSRC=HSE; APB1=PCLK/2)
RCC_CFGR a1 a0 sw
1 a0 li ( PLLON) RCC_CR 3 + a1 a0 sb
2 a0 li ( PLLRDY)
begin RCC_CR 3 + a1 a2 lbu a0 a2 a2 and a2 0!= until ( PLLRDY=1)
%1010 a0 li ( request PLL as sysclk) RCC_CFGR a1 a0 sb
begin RCC_CFGR a1 a2 lbu a0 a2 = until
( Enable clocks for USART1, GPIOA, and GPIOC.)
1 #14 << ( USART1)
1 4 << ( GPIOC) + ( need this for LEDs; might as well do it here)
1 2 << ( GPIOA) + a0 la RCC_APB2ENR a1 a0 sw
ret ;c
| On older chips - eg F103/F105 - and on the new GigaDevice GD32VF103! -
| the GPIO configuration is somewhat different.
|
| GPIOx_CRL and _CRH have 4 bits per GPIO pin: CNF<2>:MODE<2>
|
| Here are the tables of their meanings:
|
| MODE Meaning
| 00 Input
| 01 Output; Maximum speed 10 MHz
| 10 Output; Maximum speed 2 MHz
| 11 Output; Maximum speed 50 MHz
|
| If MODE = 00:
| CNF Meaning
| 00 Analog
| 01 Input floating
| 10 Input pull-up / pull-down
| 11 Reserved
|
| If MODE != 00:
| CNF Meaning
| 00 Output, general purpose, push-pull
| 01 Output, general purpose, open-drain
| 10 Output, alternate function, push-pull
| 11 Output, alternate function, open-drain
|
| If MODE = 00 and CNF = 10, the GPIOx_ODR bit corresponding to the pin
| determines the direction of the pull-up/-down:
|
| ODR
| 0 pull-down
| 1 pull-up
__host
( CNF bits, MODE=0)
0 equ gpio-in-analog
4 equ gpio-in-floating
8 equ gpio-in-pulled
( CNF bits, MODE=1,2,3)
4 equ gpio-open-drain
8 equ gpio-alternate-function
( MODE bits.)
1 equ gpio-out-10m
2 equ gpio-out-2m
3 equ gpio-out-50m
__meta
label ports-init
| Set up ports needed for USART1 and LEDs. PA9 is TX; PA10 is RX.
| PC13 is Red; PA1 is Green; PA2 is Blue.
GPIOC_CRL a1 lui ( base addr)
gpio-out-10m a0 li a0 #20 a0 slli ( PC13: output, push-pull, 10M, gpio)
GPIOC_CRH a1 a0 sw
1 #13 << a0 lui GPIOC_BSRR a1 a0 sw ( turn off PC13: set to 1)
GPIOA_CRL a1 lui ( base addr)
( PA2, PA1: output, push-pull, 10M, gpio)
gpio-out-10m 11 * ( duplicate!!) a0 li a0 4 a0 slli
GPIOA_CRL a1 a0 sw
6 a0 li GPIOA_BSRR a1 a0 sw ( turn off PA1 and PA2: set to 1)
gpio-out-2m gpio-alternate-function + ( PA9: output, push-pull, 2M, alt fn)
gpio-in-pulled 4 << + ( PA10) a0 li a0 4 a0 slli
GPIOA_CRH a1 a0 sw
1 #10 << a0 li GPIOA_BSRR a1 a0 sw ( PA10: pulled up)
ret ;c
( Set baud rate, turn on rx and tx, and finally, enable the USART.)
label uart-init
USART1_SR a1 lui ( base addr)
USART1_CR1 a1 x0 sw ( reset and disable USART)
%1100 ( TE+RE) a0 li ( enable tx and rx) USART1_CR1 a1 a0 sw
#48,000,000 #115,200 / a2 li USART1_BRR a1 a2 sw
1 #13 << ( UE) a2 lui ( enable USART)
a2 a0 a0 add ( UE+TE+RE) USART1_CR1 a1 a0 sw
ret ;c
label delay
begin -1 a0 a0 addi a0 0= until ret ;c
label on-time
10_0000 a0 lui delay j ;c
label off-time
8_0000 a0 lui delay j ;c
( a0 has bits to set/reset; a1 has address of GPIOx_BRR.)
label blink {{
a0 s2 mv
0 ( GPIOx_BRR) a1 s2 sw ( on) on-time c
GPIOA_BSRR GPIOA_BRR - a1 s2 sw ( off) }} off-time j ;c
label blink-leds {{
GPIOC_BRR a1 la ( full address)
1 #13 << ( PC13: Red LED) a0 lui blink c
GPIOA_BRR a1 la ( full address)
2 ( PA1: Green LED) a0 li blink c
4 ( PA2: Blue LED) a0 li }} blink j ;c
label chip-init
{{ clock-init c ports-init c uart-init c }} blink-leds j ;c
.then ( GD32VF103 support)
label w>
{{ b> c a0 s2 mv
b> c a0 #8 a0 slli a0 s2 s2 add
b> c a0 #16 a0 slli a0 s2 s2 add
b> c a0 #24 a0 slli s2 a0 a0 add }};
label >w
{{ a0 s2 mv >b c
s2 #8 a0 srli >b c
s2 #16 a0 srli >b c
s2 #24 a0 srli }} >b j ;c
| Compile the first 32 bits of the current muforth Git commit. When called,
| send the commit and our PC so the host can tell if we are running from
| flash or ram.
label version
{{ muforth-commit drop 8 evaluate
a0 la >w c
here a0 auipc drop }} >w j ;c
label set-address
{{ w> c a0 s1 mv }};
label read-words
{{ b> c ( count) a0 0!= if a0 s2 mv
begin 0 s1 a0 lw cell s1 s1 addi >w c
-1 s2 s2 addi s2 0= until
then
}};
label write-word
{{ w> c 0 s1 a0 sw cell s1 s1 addi }};
label get-status
sp a0 mv >w j ;c
label run
{{ w> c ( sp) a0 sp mv w> c ( pc) }} fence.i 0 a0 jr ;c
( XXX Should this be a table of relative pointers instead?)
label cmd-table
( 10) version j
( 11) set-address j
( 12) read-words j
( 13) write-word j
( 14) get-status j
( 15) run j
;c
label do-command
-10 a0 a0 addi ( 00 to 0f become large unsigned numbers)
do-command cmd-table - cell/ ( #cmds) a1 li a0 a1 u< if
a0 2 a0 slli ( word index)
cmd-table a1 auipc a1 a0 a0 add ( offset) a0 jr
then ret ;c
| Even though chat-entry makes calls, it never returns to *its* caller, so
| it doesn't bother to save its return address.
label chat-entry
begin b> c ( cmd) do-command c again ;c
| XXX Right now exceptions are handled a bit oddly, and this probably needs
| to change. Returning from Forth code, in bug (in kernel-itc.mu4) a bunch
| of registers get pushed *before* the ebreak/ecall that causes an exception.
| Any other exception will *not* have pushed that state, and so the host
| stack will end up messed up.
|
| Also, the way ebreak/ecall works is that I do an mret *to* the chat-entry
| routine. So the exception is *over* before the chat conversation with the
| host begins.
|
| Both of these are probably wrong. I should probably stack some
| "interesting" registers on exception entry, have the chat conversation
| occur in the context of the exception - with the register frame still on
| the stack - and when we go to execute code, we pop the "interesting" regs
| and mret.
label interrupt
( ignore interrupts for now)
( fall thru) ;c
label exception-exit
a1 a0 pop2 mret ;c
label exception-entry
a1 a0 push2
mcause a0 csrr interrupt a0 0>= until ( branch back if interrupt)
( exception, not interrupt)
rp0 a1 la 4 a1 a0 sw ( save mcause)
chat-entry a0 auipc ( offset) a0 a0 addi
mepc a0 a0 csrrw 0 a1 a0 sw ( swap chat-entry with mepc)
exception-exit j ;c
hooks reset-entry
here { 'reset ! } ( XXX this should be somehow automagic...)
label do-reset
8 ( MIE) mstatus csrci ( disable interrupts)
rp0 rp la 0 rp x0 sw 4 rp x0 sw ( slots to save mcause and mepc)
sp0 sp la
exception-entry a0 auipc ( offset) a0 a0 addi a0 mtvec csrw
chip-init c
chat-entry j ;c
{ h @ } ram
.ifdef hifive-compat
label execute-assembly
8 ( MIE) mstatus csrci ( disable interrupts)
tp sp mv
t0 rp pop2
ra rpush1 0 t0 ra jalr ra rpop1
sp tp mv ret ;c
.else
label execute-assembly
8 ( MIE) mstatus csrci ( disable interrupts)
t0 rp pop2 0 t0 jr ;c
.then
{ h ! }
__host
( Optionally print registers pushed by execute-assembly.)
: .rxregs ;
( For quick and dirty execution of remote assembly code.)
: rx ( x0 .. xn 'code rp - y0 .. yn <regs>)
?chat \m rp0
stack> \m execute-assembly runwait stack< .rxregs ;
__meta