Shop OBEX P1 Docs P2 Docs Learn Events
SpinForth — Parallax Forums

SpinForth

salsancisalsanci Posts: 14
edited 2008-01-25 05:33 in Propeller 1
http://code.google.com/p/spinforth/

This forth is very useful for interactive hardware and software development. It is optimized for as many free registers as possible in the cogs.

The stacks are in the cogs, and there is stack checking.

While SpinForth is fast enough for many tasks, it is not fast enough for all.

So there is a built in assembler. The assembler has not been used for any large projects, but I have one over the next couple of months, so it will get shaken out.

Comments

  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2007-12-10 02:28
    Here I am procrastinating about writing a Forth for the Propeller and you have come up with this.

    Looks great!

    I'll play with it some more.

    Thanks!
    *Peter*
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2007-12-10 08:38
    I have had a chance to have a play with it and even recompile. I wanted to have a VGA text driver so I added that and made some adjustments of course. I like the way I can run a task (and leave it running then step the console onto the next available cog (using <ctrl a>) and keep on playing, cool.

    *Peter*
  • LeonLeon Posts: 7,620
    edited 2007-12-10 14:43
    I've been playing with it. How do I go about recompiling it?

    Leon
    ·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Amateur radio callsign: G1HSM
    Suzuki SV1000S motorcycle

    Post Edited (Leon) : 12/10/2007 3:23:13 PM GMT
  • mike101videomike101video Posts: 43
    edited 2007-12-10 20:35
    This is great!!!! Having Forth on the prop gives some great options for alternative development. Now I just need to take the time to get into it.
  • OzStampOzStamp Posts: 377
    edited 2007-12-10 22:02
    Hi

    I have heard alot about the Forth lingo over the years.
    Seems to be used in alot of real time apps.
    But never seen any real code examples..
    Can somebody drop an example of what it looks like.
    For example to sample an A/D chip simple 8 bit SPI loop .. clock collect store type stuff.
    Does not have to be in detail just a short blurb ..

    cheers Ron Mel OZ
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2007-12-10 23:21
    Hi Ron,

    This is just a quick example of writing 8-bits in SPI fashion. With a very minor change this routine can read in the data as well but for this example I have kept it simple.

    Notice that SpinForth is not a full Forth so simple operations such as SET and RESET bits have to be done the old fashioned way unless we create a definition of course.

    *Peter*

    
    HEX
    1 CONSTANT dat                                  \ mask for data out
    2 CONSTANT clk                                  \ mask for clock bit
    
    \ SPI example
    \ Write 8-bits MSB first to SPI with clock idling high
    \
    : SPI_WR  \ ( dat -- )
            OUTA @ dat INVERT AND clk OR OUTA !     \ ensure outputs are preset
            DIRA @ dat clk OR OR DIRA !             \ make dat and clk outputs
            8 0 DO                                  \ loop from 0 to 7 (8 is limit)
              DUP 80 AND                            \ test next msb of byte
              IF    OUTA @ dat OR                   \ set output high
              ELSE  OUTA @ dat INVERT AND           \ reset output
              THEN
              OUTA !                                \ write output
              OUTA @
              DUP clk INVERT AND OUTA !             \ clock low
              OUTA !                                \ clock high (restore)
             2*                                     \ shift next bit into msb
            LOOP                                    \ continue to loop
            DROP                                    \ discard dat
            ;
    
    
    \ the above example can be refactored which makes the code
    \ easier to read and test as well as provide the building blocks 
    \ for other SPI definitions
    
    
    : SPI_INIT
            OUTA @ dat INVERT AND clk OR OUTA !     \ ensure outputs are preset
            DIRA @ dat clk OR OR DIRA !             \ make dat and clk outputs
            ;
    
    : SPI_WRBIT \ ( dat -- dat*2 )
            DUP 80 AND                              \ test next msb of byte
            IF      OUTA @ dat OR                   \ set output high
            ELSE  OUTA @ dat INVERT AND             \ reset output
            THEN
            OUTA ! 2*
            ;
    
    : SPI_CLK
            OUTA @
            DUP clk INVERT AND OUTA !               \ clock low
            OUTA !                                  \ clock high (restore)
            ;
    
    : SPI_WR  \ ( dat -- )
            SPI_INIT
            8 0 DO                                  \ loop from 0 to 7 (8 is limit)
              SPI_WRBIT
              SPI_CLK
            LOOP                                    \ continue to loop
            DROP                                    \ discard dat
            ;
    
    

    Post Edited (Peter Jakacki) : 12/10/2007 11:30:05 PM GMT
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2007-12-11 00:08
    Does this SPI_WR example actually work? Well I tested it and you can see the results from the scope. It's not as fast as I would like it to be, especially compared to versions I have written for other processors, but it does the job.

    *Peter*


    DECIMAL
    1 29 LSHIFT CONSTANT dat            \ mask for data out
    1 28 LSHIFT CONSTANT clk            \ mask for clock bit
    HEX
    
    : SPI_INIT
        OUTA @ dat INVERT AND clk OR OUTA !    \ ensure outputs are preset
        DIRA @ dat clk OR OR DIRA !        \ make dat and clk outputs
        ;
    
    : SPI_WRBIT \ ( dat -- dat*2 )
        DUP 80 AND                \ test next msb of byte
        IF    OUTA @ dat OR            \ set output high
        ELSE  OUTA @ dat INVERT AND        \ reset output
        THEN
        OUTA ! 2*
        ;
    
    : SPI_CLK
        OUTA @
        DUP clk INVERT AND OUTA !        \ clock low
        OUTA !                    \ clock high (restore)
        ;
    
    : SPI_WR  \ ( dat -- )
        SPI_INIT
        8 0 DO                    \ loop from 0 to 7 (8 is limit)
          SPI_WRBIT
          SPI_CLK
        LOOP                    \ continue to loop
        DROP                    \ discard dat
        ;
    
    
    640 x 489 - 119K
  • OzStampOzStamp Posts: 377
    edited 2007-12-11 01:21
    Hi Peter.

    Thnx for the quick tutorial on Forth.
    As you said the speed seems a little slow... whats the problem there ?
    You would expect the speed to be much better than Spin ??

    Propeller Spin nearly beats it..

    cheers Ron Mel OZ
  • salsancisalsanci Posts: 14
    edited 2007-12-11 05:02
    http://code.google.com/p/spinforth/w/list
    is the url of the wiki. Documentation, and this project, could use help, any volunteers

    Amateur radio call sign: VE3SDC
    Suzuki SV650S Motorcycle

    As for the speed:

    hex

    \ set bit 0 as an output
    0 pxo

    \ ( num_loops -- num_loops )
    : forth_flip dup 0 do outa @ 1 or outa ! outa @ FFFFFFFE and outa ! loop ;

    :asm asm_flip
    mov treg1 , stTOS
    __1
    or outa , # 1
    andn outa , # 1
    djnz treg1 , # __1
    ;asm

    : test_result swap cnt @ swap - 2dup
    ." COUNT of " . ." for " . ." flips -- "
    swap u/ . ." clocks/flip " cr ;

    : test_forth cnt @ 100000 forth_flip test_result ;

    : test_asm cnt @ 100000 asm_flip test_result ;

    : test test_forth test_asm ;

    decimal
    test


    Produces:

    COUNT of 1862272112 for 1048576 flips -- 1776 clocks/flip
    COUNT of 12583536 for 1048576 flips -- 12 clocks/flip
    Cog2 ok

    So... as I wrote somewhere the built in assembler, which I have not yet really shaken down, or really documented is what you want to use for speed.

    The normal pattern is to write in forth, and replace the forth words which need to go fast with assembler.

    Hope this helps
  • salsancisalsanci Posts: 14
    edited 2007-12-11 05:08
    load cogasm
    before you try to use the assembler
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2007-12-11 06:15
    Hi Sal, how are things up in Toronto?? I have quite a few relatives up there.

    Thanks for the Forth, I'm evaluating it at present to see if I can use it in a project. Straight away I can see I need a method of autostarting an application plus I haven't yet seen where I can fire-up tasks from the program. I did add a cut down hires text driver plus I am looking at integrating an SD card driver with the higher level stuff in Forth.

    *Peter*
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-12-11 09:42
    salsanci said...
    http://code.google.com/p/spinforth/

    This forth is very useful for interactive hardware and software development. It is optimized for as many free registers as possible in the cogs.

    The stacks are in the cogs, and there is stack checking.

    While SpinForth is fast enough for many tasks, it is not fast enough for all.

    So there is a built in assembler. The assembler has not been used for any large projects, but I have one over the next couple of months, so it will get shaken out
    Thank you Salsanci! I spent this evening looking at your source code which is quite nicely commented.

    One thing I haven't figured out is why the nfa of secondaries get $10 added.

    But I think I spotted one optimization -- move the setting of lstatus outside of the push/pop routines. Let them have separate jump points in front of the a_reset routine where each particular lstatus value can be or'ed in.

    Post Edited (Fred Hawkins) : 12/11/2007 7:33:09 PM GMT
  • salsancisalsanci Posts: 14
    edited 2007-12-11 14:48
    The + $10 I think is a spin bug, when I was developing it didn't work. A little work with a hex editor, and I noticed all the addresses were $10 off.

    Perhaps someone way more familiar with spin than I can comment.

    As for auto starting,

    You can replace the fstart routine and recompile,

    or load a file called 2 or 3 or 4 or ... 7 into the eeprom which will be loaded by that cog every time it starts.

    BTW I was transplanted to Vancouver a while ago and recently to Victoria, so the VE3 is a bit old.

    Sal
  • deSilvadeSilva Posts: 2,967
    edited 2007-12-11 15:54
    OzStamp said...
    You would expect the speed to be much better than Spin ??
    No, that is a misconception! SPIN is a very clever interpreter. It is VERY difficult to beat it by the factor of 3 as Frohf did. Alas, Frohf has dissapeared somehow smile.gif
  • Paul BakerPaul Baker Posts: 6,351
    edited 2007-12-11 18:10
    salsanci said...
    The + $10 I think is a spin bug, when I was developing it didn't work. A little work with a hex editor, and I noticed all the addresses were $10 off.

    Perhaps someone way more familiar with spin than I can comment.

    As for auto starting,

    You can replace the fstart routine and recompile,

    or load a file called 2 or 3 or 4 or ... 7 into the eeprom which will be loaded by that cog every time it starts.

    BTW I was transplanted to Vancouver a while ago and recently to Victoria, so the VE3 is a bit old.

    Sal
    What you are likely noticing is that there is a 16 byte ($10 hex) header at the beginning of every program, this header contains vital information needed by the Propeller to run the program. So program space $0 is located at $10 in the hub memory.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.
  • deSilvadeSilva Posts: 2,967
    edited 2007-12-11 19:08
    TheManual p. 31 said...

    Main RAM
    The first half of Main Memory is all RAM.

    When a program is loaded into the chip, either from a host or from an external EEPROM, this
    entire memory space is written. The first 16 locations, $0000 – $000F, hold initialization data
    used by the Boot Loader and Interpreter. Your program’s executable code and data will
    begin at $0010 and extend for some number of longs. The area after your executable code,
    extending to $7FFF, is used as variable and stack space.

    There are two values stored in the initialization area that might be of interest to your program:
    a long at $0000 contains the initial master clock frequency, in Hertz, and a byte following it
    at $0004 contains the initial value written into the CLK register. These two values can be
    read/written using their physical addresses (LONG[noparse][[/noparse]$0] and BYTE[noparse][[/noparse]$4]) and can be read by using
    their predefined names (CLKFREQ and CLKMODE). If you change the CLK register without using
    the CLOCKSET command, you will also need to update these two locations so that objects which
    reference them will have current information.
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-12-11 19:34
    Paul Baker (Parallax) said...
    What you are likely noticing is that there is a 16 byte ($10 hex) header at the beginning of every program, this header contains vital information needed by the Propeller to run the program. So program space $0 is located at $10 in the hub memory.

    Aha!
    ps. does deSilva's dead cat bounce?
  • salsancisalsanci Posts: 14
    edited 2007-12-12 03:36
    The simplest way to get a forth cog to auto start a program on reset is to write a file that is the cog number to eeprom. When the cog is reset it will load the file. The following will write a file for cog 3 which defines pin 0 to an output and makes it blink (assuming there is an led on pin 0). Not a really useful function, but you get the idea.

    200 f_write













    ...3


    \ ( pin -- )
    : blink
    dup pxo \ pin out
    begin
    dup pxh \ pin hi
    100 delay_ms
    dup pxl \ pin lo
    100 delay_ms
    0 until drop ;

    : blink_start
    cogid 1+ connectcog \ give the console to the next cog
    0 blink ;

    blink_start

    \ eof
  • salsancisalsanci Posts: 14
    edited 2007-12-12 03:40
    Note: to write the file to eeprom, copy the code to the clipboard and paste into a terminal program. The fast_load and f_write words depend on keys coming in fast and buffer them to memory until there is a pause in the input.

    Now if you type really fast...
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-12-18 14:04
    bump for Graham's link list
  • salsancisalsanci Posts: 14
    edited 2007-12-19 04:39
    What is Graham's link list?
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-12-19 05:04
    At the top of this forum topic list are five·blue 'stickie'·topics (they stay the same as you page through the forum). The middle one has a message that points to this thread: http://forums.parallax.com/showthread.php?p=609066

    Which is·the famous (justifiably) Graham Stabler's Good Thread Index (New and Improved)·
  • salsancisalsanci Posts: 14
    edited 2008-01-25 05:27
    Version 1.1 has been posted. Available at http://code.google.com/p/spinforth/

    I have been shaking down the assembler. So far so good.

    One item I found in writing an SPI driver, testing a bit I needed to load ina to a temp reg and then check, verified this with spin assembler and got the same thing. Am I doing something way wrong??



    \ read in bit

    \ test ina , # _em_miso wc doing this directly does not seem to work

    mov treg5 , ina
    test treg5 , # _em_miso wc

    rcl stTOS , # 1
  • Mike GreenMike Green Posts: 23,101
    edited 2008-01-25 05:33
    You cannot use INA in the destination field of an instruction. If you do, it references the "shadow" memory location for INA rather than the register itself. This is true for other read-only memory locations like CNT and PAR as well.
Sign In or Register to comment.