Shop OBEX P1 Docs P2 Docs Learn Events
Automatically converting Spin objects to PASM - Page 2 — Parallax Forums

Automatically converting Spin objects to PASM

2»

Comments

  • OK, my apologies -- I actually forgot that I had put propeller-load binaries into the git repo. Those are only there for spinconvert, which should probably be broken out into its own repo. I don't even know if anyone uses spinconvert. I thought the GUI was kind of interesting in a minimalistic sort of way, but it's probably too minimal for Windows users and too much for Unix users. (I don't tend to use it myself, for example, but I'm a command line guy.) For now I'll get rid of the Linux binary, since it isn't used in default builds, but I'll leave the Windows binary there for spinconvert.exe purposes.

    Eric
  • On the main topic of the thread: how important / useful would it be to allow the .cog.spin file to run on its own COG? For example, if you just compile it on its own should it be able to run (assuming the original .spin was able to do so)?

    I initially didn't worry about that possibility, regarding .cog.spin as just for device drivers. But I see that the frequency counter example can run on its own (actually I guess all PropBASIC programs kind of have to), and I could see a use for small tests or demos to be included. On the other hand the COG space is really tight, and any code to support a default or demo method will take away space that could be used for other things.

    I can see a few possibilities:

    (1) The current status quo: you need to call __cognew before doing anything else, so there's no way to launch the object in its own COG. Fairly straightforward.

    (2) The Spin way: if you run the object on its own, the first method gets invoked automatically, and when it exits the COG shuts down. This will involve some initialization code that many objects won't actually find useful (because their first methods aren't intended to run on their own)

    (2b) Like (2), but we only include the initialization code in certain cases. For example, we could only include it if the first method is named "demo" or "main". Or, we could leave it out if the first method has any parameters. Or, control its inclusion/exclusion based on a symbol in CON.

    (3) No automatic startup, but some way to relaunch the code in the same COG. Something like:
       foo.__coginit(cogid, mystartupmethod(params))
    
    Like (2) there would be some associated cost in COG space.

    Any preferences?
  • jmgjmg Posts: 15,144
    ersmith wrote: »
    ...
    Any preferences?
    I'm not following the finer points of those questions, some examples of code size before/after and use cases might help.

    Of course, any added feature that can shrink code size, and give more user control, is a good idea :)

    Taking that FrequencyCounter example, another thread has already asked about launch of multiple copies, and the P1 has a max of 16 x 32b counters.

    Simple and practical could be 7~14 concurrent Reciprocal Counters** in 7 (identical code) slave COGS, and one master.
    Each slave is started, with PinA,PinB,MinGateTime and reports to HUB memory.
    (or, it could extract PinA,PinB,MinGateTime from Hub, by mutual agreement, via COG#)
    The master cog does the calcs and string/report formatting.

    ** I think 14 counters is possible, with a slight compromise, where edge align is derived by tight polling.
    WAIT pin code can only support 1 chan/COG, but can resolve to 1/80M or 12.5ppb/sec = highest performance.
    CTR polling might need ~ 20-40 sysclks granularity, but that's still 0.25-0.5ppm, and many applications would consider sub-ppm granularity to be just fine. (better than xtal precision)

  • jmg wrote: »
    ersmith wrote: »
    ...
    Any preferences?
    I'm not following the finer points of those questions, some examples of code size before/after and use cases might help.

    Never mind. I was basically asking if the created .cog.spin object should be able to run on its own. But nobody has asked for that feature, and it does add overhead, so for now .cog.spin objects will always have to be included and started from another .spin object. It's simpler that way.

  • ersmith wrote: »
    I was basically asking if the created .cog.spin object should be able to run on its own. But nobody has asked for that feature, and it does add overhead, so for now .cog.spin objects will always have to be included and started from another .spin object. It's simpler that way.
    I prefer keeping it this way.
  • jmgjmg Posts: 15,144
    ersmith wrote: »
    Never mind. I was basically asking if the created .cog.spin object should be able to run on its own. But nobody has asked for that feature, and it does add overhead,...
    How much overhead does this add ?

  • ersmithersmith Posts: 5,910
    edited 2018-04-25 01:08
    jmg wrote: »
    ersmith wrote: »
    Never mind. I was basically asking if the created .cog.spin object should be able to run on its own. But nobody has asked for that feature, and it does add overhead,...
    How much overhead does this add ?

    The instructions needed to set up and jump to the first method; something roughly like:
        rdlong sp, mboxptr
        add   mboxptr, #4
        rdlong objbase, mboxptr
        call   #default_method
        cogid  arg1
        cogstop arg1
    
    Plus the Spin code to invoke this, although modern Spin compilers are smart enough to remove unused methods so that shouldn't be an issue.
  • I've updated the top post with the newest fastspin.zip. The inline comments should work somewhat better, and there are a number of additional optimizations to the generated code.
  • Updated again with an important bug fix. Incorrect code was being generated for things like
      a[i++] ^= N
    
    That's fixed now, and I think things should be pretty stable.

    I'm pretty pleased with some of the new optimizations. For example, this Spin function:
    '' stream n bytes of data out a pin as fast as we can
    
    pub sendbuffer(buf, n, pin) | c
      repeat n
        c := byte[buf++]
        repeat 8
          OUTA[pin] := c
          c := c >> 1
    
    gets turned into this PASM:
    _sendbuffer
            mov     _var_05, #1
            shl     _var_05, arg3
            cmps    arg2, #0 wz
     if_e   jmp     #L__0009
    L__0015
            rdbyte  _var_04, arg1
            add     arg1, #1
            mov     _var_06, #8
    L__0010
            shr     _var_04, #1 wc
            muxc    OUTA, _var_05
            djnz    _var_06, #L__0010
            djnz    arg2, #L__0015
    L__0009
    _sendbuffer_ret
            ret
    
    which I think is pretty close to how most PASM programmers would write it (although a human might perhaps forget to check for n == 0).
  • ersmithersmith Posts: 5,910
    edited 2018-05-23 21:03
    If you've been following the "fastspin for P2" thread you know that there have been a lot of changes to fastspin recently, but I may not have made it clear that many of the improvements also work on P1. There's a new release of fastspin (3.8.4) at https://github.com/totalspectrum/spin2cpp/releases/latest. Besides the obvious main draw of fastspin (that it compiles to PASM, both as a whole program or as a single object converted to run in another COG) there are some other additions to the language:

    (1) Multiple assignments: swap two variables by doing:
        (x,y) := (y,x)
    

    (2) Multiple return values from functions: define a 64 bit addition function like
    PUB add64(ahi, a, bhi, b): chi, c
      c := a
      chi := ahi
      asm
        add c, b wc
        addx chi, bhi
      endasm
    
    and use it like:
        (a,b) := add64( add64(x0, x1, y0, y1), z0, z1 )
    
    add64 returns two values, and both of those values may be passed in as two of the parameters to a function.

    (3) A new flag, -l, to produce a listing file. The listing file is of the DAT section only, and is in place of a binary output, so it isn't quite as nice as the listing produced by bstc, but it's still pretty handy sometimes.

    (4) Default values for parameters. In a function like:
      PUB inc(n = 1)
        a += n
    
    calling just plain
      inc
    
    is the same as calling
      inc(1)
    

    (5) Automatic addition of "string" around literal strings passed to parameters that are known to be strings. If your serial print function is declared as:
    PUB str(x = string(""))
    
    then calling
       ser.str("a")
    
    will be the same as
      ser.str(string("a"))
    
    instead of what Spin normally produces, which is
      ser.str($61)
    

    This last one is the biggest departure from normal Spin syntax, but I personally find it really useful. I'd like to hear others' opinions on it though. Is it too big a change?

    Eric
  • Eric, that looks great! I've always contended that what people are calling Spin2 could have run on the P1. There are many extensions to the language that people have asked for over the years, but it seems like the P2 was needed to motivate the addition of the extensions. Keep up the good work.
  • Cool! Love the string idea especially...could save some typing...awesome to see some new life breathed into Spin
Sign In or Register to comment.