Samuel Tardieu @ rfc1149.net

rforth1 optimizations

,

I worked a lot on rforth1 lately, a Forth compiler targetting the PIC 18f family of microcontrollers. I have added many new optimizations in order to generate smaller and more efficient code.

Let’s take an example. The Forth code below cycles through the 8 possible states of 3 leds connected to ports B5, B6 and B7 of a PIC:

\\ Define three words led0, led1 and led2 designating the leds

LATB 5 bit led0
LATB 6 bit led1
LATB 7 bit led2

\\ Use timer 0 to wait for 100ms (with a 40MHz crystal)

: tmr0-init ( -- ) $84 T0CON c! ;    \\ Enable timer, 16 bits, prescaler = 32
: 100ms ( -- ) -31250 TMR0L ! TMR0IF bit-clr begin TMR0IF bit-set? until ;

\\ Move leds -- when led0 goes to 0, switch led1. When led1 goes to 0, do
\\ the same thing with led2

: leds-init ( -- ) 0 LATB c! $1F TRISB c! ;   \\ B5, B6 and B7 are outputs
: switch-led2 ( -- ) led2 bit-toggle ;
: switch-led1 ( -- ) led1 bit-toggle led1 bit-clr? if switch-led2 then ;
: switch-led0 ( -- ) led0 bit-toggle led0 bit-clr? if switch-led1 then ;

\\ Loop indefinitely with a pause between each led change

: mainloop ( -- ) begin switch-led0 100ms again ;

\\ Main program: initialize the timer and the leds then run the main loop

: main ( -- ) tmr0-init leds-init mainloop ;

Here is the assembly code with the default compiler switches: (in order to keep it relatively short, I’ve omitted the declaration of constants such as LATB, which are included automatically, as well as the assembly file header)

; main: defined at example.fs:26
main
        call tmr0_init
        call leds_init

; mainloop: defined at example.fs:22
mainloop
        call switch_led0
        call _100ms
        bra mainloop

; switch-led0: defined at example.fs:18
switch_led0
        btg LATB,5,0
        btfsc LATB,5,0
        return

; switch-led1: defined at example.fs:17
switch_led1
        btg LATB,6,0
        btfsc LATB,6,0
        return

; switch-led2: defined at example.fs:16
switch_led2
        btg LATB,7,0
        return

; tmr0-init: defined at example.fs:9
tmr0_init
        movlw 0x84
        movwf T0CON,0
        return

; 100ms: defined at example.fs:10
_100ms
        movlw LOW(-31250)
        movwf TMR0L,0
        movlw HIGH(-31250)
        movwf (TMR0L+1),0
        bcf INTCON,2,0
_lbl___197
        btfsc INTCON,2,0
        return
        bra _lbl___197

; leds-init: defined at example.fs:15
leds_init
        clrf LATB,0
        movlw 0x1f
        movwf TRISB,0
        return
END

The assembly code is almost a one-to-one mapping to the Forth one. However, you may notice that the compiler chose to reorder the various parts so that fallbacks can be used between Forth words. For example, switch-led0 potentially falls back through switch-led1 because of the btfsc (test one bit and skip next instruction [return in this case] if bit is clear).

However, here we have not used a nice feature of rforth1 which is the automatic inlining of words if the generated code is either smaller or more efficient. With the automatic inlining turned on, we now get:

; main: defined at example.fs:26
main
        movlw 0x84
        movwf T0CON,0
        clrf LATB,0
        movlw 0x1f
        movwf TRISB,0
_lbl___219
        btg LATB,5,0
        btfsc LATB,5,0
        bra _lbl___220
        btg LATB,6,0
        btfss LATB,6,0
        btg LATB,7,0
_lbl___220
        movlw LOW(-31250)
        movwf TMR0L,0
        movlw HIGH(-31250)
        movwf (TMR0L+1),0
        bcf INTCON,2,0
_lbl___222
        btfsc INTCON,2,0
        bra _lbl___219
        bra _lbl___222
END

Isn’t that nice? You can identify the various parts of the code: between main and _lbl___219, you get the timer and ports initialization. Between _lbl___219 and _lbl___220 is the whole logic of led switching. Between _lbl___220 and _lbl___222, the timer is reset in order to wait for 100ms, and the last three lines loop until the timer fires and then goes back to the led switching logic.

If you want to try rforth1, get it here, it is free and distributed under the GNU General Public Licence version 2. At this time, it has no documentation at all but comes with several examples that you can use as a template. And people who can understand French can read this tutorial written by one of the rforth1 users.

blog comments powered by Disqus