Permalink
Browse files

riscv: A nascent Forth kernel and working remote execution!

This is an historic day!

Not only is this the 2000th commit to the muforth project repository,
but this commit means that muforth's RISC-V support has reached the
point that you can write and execute Forth code on a RISC-V chip!

The kernel included here is far from complete, but you can add,
subtract, do logical operations (and, or, xor, invert), and loop
(carefully ;-).

How do you get started? It's pretty easy. You have to build or download
a copy of openocd that has RISC-V support. (How you do that is outside
the scope of this commit message. ;-)

I'm assuming you're using a SiFive Hifive1 board. Nothing else will
work. ;-)

Edit the file

  mu/target/RISC-V/start-openocd.sh

so that the openocd variable points to *your* RISC-V-enabled openocd.

Then, in one window, run that shell script.

In another window, cd to muforth/mu/ (where you should *always* be when
running muforth code) and try this:

  ./muforth -f target/RISC-V/build.mu4

You'll see loading messages fly by, but hopefully no errors. Then type

  jtag

This should connect to the gdb stub in openocd, through two named pipes
that get created in mu/target/RISC-V/. The current values of some key
registers will get printed.

You are now "chatting" (the Ok prompt shows (chatting) to indicate
this), and words that you name on the command line will be executed not
on the host but on the target!

Each time you execute a word, up to 8 stack items are copied from the
host stack to the target, the word is executed, and the results are
copied back. Thus, it looks like code is being executed locally, except
that there is a slight pause while the register values and stack items
are shuffled back and forth.

Try this:

  10305 2040 +

This will add two hex values together and leave their sum on the stack.
You can try any of the words shown when you type

  target words

One last note: Even though it's not an issue yet (since the control
structure words don't yet completely work) as a helpful bit of UI the
value shown in the IP register is "annotated" with a "*" if the IP is
somewhere other than in the "trampoline" code. Generally this means that
execution of the last word is not complete; somewhere it hit a copy of
the word "bug" and stopped. To continue, type

  cont

and keep doing this until the * disappears.

Anyway, enjoy! There is more coming...
  • Loading branch information...
nimblemachines committed Apr 6, 2017
1 parent e6588d8 commit badd131afd1ec0078d129c5aad3c5ae96a0e81f8
@@ -12,14 +12,13 @@ ld target/RISC-V/memory.mu4 ( basic target memory management)
ld target/RISC-V/asm-rv32i.mu4
ld target/RISC-V/dis-rv32i.mu4
ld target/RISC-V/meta.mu4 ( metacompiler, baby!)
ram
ld target/RISC-V/kernel-itc.mu4 ( RISC-V ITC Forth kernel!)
ld target/RISC-V/interact.mu4 ( interaction with target)
-- ld target/RISC-V/flash.mu4 ( flash programming!)
ld target/RISC-V/debug-openocd-gdb.mu4
ram
ld target/RISC-V/kernel-itc.mu4 ( RISC-V ITC Forth kernel!)
( These settings will stick around if we're loaded with ld!)
__meta
hex
@@ -186,10 +186,9 @@ defer remote
-: ." (chatting)" ;
-:
( NOTE: If we want to remove .forth., replace it with .lex.)
.target. find if execute remote ^ then ( execute on target)
.meta. find if execute ^ then ( labels are in .meta.)
.forth. find if execute ^ then ( utility words in .forth.)
.lex. find if execute ^ then ( comments and such)
target-number ;
mode __chatting
@@ -136,26 +136,26 @@ compiler
: <cmd,n> ( u) \c char compile cmd,n ;
forth
: g.cycle <cmd> i ;
: g.cycles ( u) <cmd,n> i ;
: g.step <cmd> s ;
: g.why-halted <cmd> ? ;
: g.continue <cmd> c ;
: g.go ( addr) <cmd,n> c ;
: j.cycle <cmd> i ;
: j.cycles ( u) <cmd,n> i ;
: j.step <cmd> s ;
: j.why-halted <cmd> ? ;
: j.continue <cmd> c ;
: j.go ( addr) <cmd,n> c ;
.ifdef xlen32
: g.getregs ( buf)
: j.getregs ( buf)
<cmd> g [ #32 4 * ] ?hexstring> ;
: g.setregs ( buf)
: j.setregs ( buf)
<pkt char G >b [ #32 4 * ] >hexstring pkt> ;
( NOTE: reg #32 *is* the PC!)
: g.getreg ( n - value)
: j.getreg ( n - value)
<cmd,n> p pad 4 ?hexstring> pad lew@ ;
: g.setreg ( value n)
: j.setreg ( value n)
<pkt char P >b >num char = >b pad lew! pad 4 >hexstring pkt> ;
.else ( 64 bits, baby!)
@@ -164,34 +164,34 @@ forth
: le8@ ( a - d) dup lew@ ( low) swap 4 + lew@ #32 << + ;
: le8! ( d a) push dup r@ lew! ( low) #32 >> pop 4 + lew! ;
: g.getregs ( buf)
: j.getregs ( buf)
<cmd> g [ #32 8 * ] ?hexstring> ;
: g.setregs ( buf)
: j.setregs ( buf)
<pkt char G >b [ #32 8 * ] >hexstring pkt> ;
: g.getreg ( n - value)
: j.getreg ( n - value)
<cmd,n> p pad 8 ?hexstring> pad le8@ ;
: g.setreg ( value n)
: j.setreg ( value n)
<pkt char P >b >num char = >b pad le8! pad 8 >hexstring pkt> ;
.then
: addr-len ( a u - u) swap >num char , >b dup >num ;
: g.read ( buf a u)
: j.read ( buf a u)
<pkt char m >b addr-len pkt> ?hexstring> ;
: g.write ( buf a u)
: j.write ( buf a u)
<pkt char M >b addr-len char : >b >hexstring pkt> ;
: g.hello
: j.hello
open-gdb-pipes
<pkt " qSupported" >string pkt> ;
: g.get-sp ( - sp) [ \a sp 0 >reg nip ] g.getreg ;
: g.set-sp ( sp) [ \a sp 0 >reg nip ] g.setreg ;
: j.get-sp ( - sp) [ \a sp 0 >reg nip ] j.getreg ;
: j.set-sp ( sp) [ \a sp 0 >reg nip ] j.setreg ;
: gdb
chat-via g.hello g.get-sp g.set-sp g.read g.write g.go ;
: jtag
chat-via j.hello j.get-sp j.set-sp j.read j.write j.go ;
@@ -70,14 +70,17 @@ variable instr-pc ( points to _start_ of current instruction)
: .udec radix preserve decimal (u.) type ;
: .hex radix preserve hex (.) type ;
: 2sp space space ;
: 4# # # # # ;
: .h16 radix preserve hex <# 4# #> type ;
: .h32 radix preserve hex <# 4# 4# #> type ;
: .h16_16 radix preserve hex <# 4# char _ hold 4# #> type ;
: .u32 .h16_16 ; ( XXX this can be changed to .h32 if you prefer)
: .h16__ .h16 2sp ;
: .h32__ .h32 2sp ;
: 2sp space space ;
: .u32 .h16_16 ; ( XXX this can be changed to .h32 if you prefer)
defer .regname
-: ( reg) ." x" .udec ;
@@ -375,8 +378,8 @@ decimal
dup ea ! ( default ea: stay put)
dup .addr .nesting space dup p ! instr-pc !
op* ( low16) dup op16? if
4 spaces dup .h16 2sp shred16 else
op* ( hi16) 16 << + dup .h32 2sp shred32 then
4 spaces dup .h16__ shred16 else
op* ( hi16) 16 << + dup .h32__ shred32 then
drop space ;
@@ -42,24 +42,22 @@ variable ram-copied ( pointer to first un-copied byte)
\m here over - copy-chunk
\m here ram-copied ! ;
meta
variable sp ( target SP)
variable rp ( target RP)
variable ra ( target RA)
variable loop ( target loop counter)
forth
( Define local copies of target registers. Before executing code on the
target, we "push" these values to the target, and after executing code,
we "pull" the new values. We used the local, cached values when printing
the registers with .regs .)
( Name these so they don't conflict with assembler names.)
variable tsp ( target SP)
variable trp ( target RP)
variable tip ( target IP)
variable tix ( target loop index counter)
: get-regs ( just SP for now) t.get-sp \m sp ! ;
: set-regs ( just SP for now) \m sp @ t.set-sp ;
: get-regs ( just SP for now) t.get-sp tsp ! ;
: set-regs ( just SP for now) tsp @ t.set-sp ;
: .tr ( variable) ( "target register") @ .h32 2sp ;
: .regs
cr ." Loop SP RP PC"
( 00000000 00000000 00000000 00000000 )
cr \m loop .tr
\m sp .tr
\m rp .tr
\m ra .tr ;
defer .regs
: hi chatting on >chat t.hello get-regs .regs
ram-copied off copy-ram
@@ -99,18 +97,18 @@ meta
@ram #ram + constant sp0 ( D stack is at the end of RAM)
\m sp0 #32 \m cells - constant rp0 ( R stack is *below* D stack)
: depth \m sp0 \m sp @ - \m cell/ 1- ;
: depth \m sp0 tsp @ - \m cell/ 1- ;
forth
( stack> *first* builds a local image of the target stack - in the RAM
image - and *then* copies it, in one chunk, to the target.)
: stack> ( "push" stack to target)
depth 0 max 12 min
\m sp0 over 1+ \m cells - dup \m sp ! ( top of D stack) swap
\m sp0 over 1+ \m cells - dup tsp ! ( top of D stack) swap
for tuck image-! \m cell+ next ( copy each cell as a word to D stack)
"decafbad swap image-! ( sentinel)
\m sp @ image+ \m sp @ \m sp0 over - t.write ( copy stack image to target) ;
tsp @ image+ tsp @ \m sp0 over - t.write ( copy stack image to target) ;
( XXX add a way to automagically sign-extend or zero-extend values coming
from the target stack?)
@@ -121,31 +119,29 @@ forth
: stack< ( "pop" stack from target)
\m depth 0 max 12 min =if
push
\m sp @ image+ \m sp @ r@ \m cells t.read ( read target stack)
tsp @ image+ tsp @ r@ \m cells t.read ( read target stack)
pop
\m sp @ over ( n sp n)
tsp @ over ( n sp n)
for dup image-@ pop 2push \m cell+ next ( starting with top, push to R)
drop ( sp)
for 2pop push next ( pop from R to reverse order)
0
then drop ;
.ifdef notyet
( Target always starts by executing the code at continue-forth, with SP
pointing to the data stack, which contains both the data to be consumed,
and the "Forth VM" context.
When first executing a word, the host sets things up like this:
RA = trampoline
RP = bottom of R stack ie, empty R stack
Loop = 0
IP = trampoline
RP = bottom of R stack ie, empty R stack
IX = 0
When instead continuing execution - perhaps inside a loop that contains a
call to bug - the host sets things up like this:
RA = saved RA
RP = saved RP
Loop = saved Loop)
IP = saved IP
RP = saved RP
IX = saved IX)
: ?chat
chatting @ 0= if error" not connected to target" then ;
@@ -156,22 +152,20 @@ variable continue-forth
variable trampoline
forth
( NOTE: For initial execution of a Forth word, xn is cfa!)
: continue ( x0 .. xn ra rp loop - y0 .. yn ra rp loop)
( NOTE: For initial execution of a Forth word, xn is pfa!)
: continue ( x0 .. xn ip rp ix - y0 .. yn ip rp ix)
?chat
stack> p@ continue-forth runwait stack<
\m loop ! \m rp ! \m ra ! .regs ;
tix ! trp ! tip ! .regs ;
meta
: cont ( ) ( continue forth execution)
\m ra @ \m rp @ \m loop @ ( ra rp loop) continue ;
tip @ trp @ tix @ ( ip rp ix) continue ;
forth
( Set rp to rp0, loop to 0, and ra to trampoline.)
( Set rp to rp0, ix to 0, and ip to trampoline.)
-: ( cfa) ( execute target forth word)
p@ trampoline \m rp0 0 ( ra rp loop) continue ; is remote
.then ( notyet)
p@ trampoline \m rp0 0 ( ip rp ix) continue ; is remote
128 array riscv-seekeys
Oops, something went wrong.

0 comments on commit badd131

Please sign in to comment.