Anything to convert spin to PASM?

T ChapT Chap Posts: 3,931
edited 2018-10-14 - 02:34:11 in Propeller 1
I looked at the resource sticky but didn’t see it. I want to find an SPI object and modify it to do a specific role and then convert it to PASM. The object needs to be sent a pointer to a long that will be read by others. The PASM object also needs to have a few spin commands from a main program to do some things as needed.

PASM Loop:

Read an SPI device
Update a variable with the value from a device
If requested from SPIN main program, send a value to the device via SPI then continue the Read loop.

Is there something that will let me write this in SPIN and convert it?

Edit After some digging it looks like fastspin command line exe converts spin to pasm. I downloaded the files from github, dragged a spin file on top of fastspin.exe and it output a text file of the pasm conversion. Now to write the spin program I need and test it.


  • Yep, fastspin converts spin to pasm. In fact if you have a small object that you want to run in another COG you can use
    fastspin -w myobj.spin
    to convert it to PASM that's "wrapped" with Spin methods. There's some documentation about this in the docs/ file in (Note: .md files are just regular text files, so Notepad can open them.)

  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 04:04:55
  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 16:05:57
    I have been experimenting with fastspin and have created a SPIN file that I sent to fast spin and created a PASM output. I took the PASM parts and copied it into a SPIN code to try to merge the parts to create a demo. I feel like the -w for spin wrapper is not the correct idea since I can't seem to get the PASM to stay in a loop, it only wants to be called from SPIN to do reads, I want the PASM to do its on loop with no supervision for the first test.

    The demo should have a main SPIN program that first launches the LS7366 PASM code that will only read the LS7366 quad encoder module via SPI. This requires the first step of toggling SSN pin from high to low, then SPI command to get the encoder position, then the SPI reading the position , then SSN pin goes back high.

    I know what I have will not work yet, I don't even know if the converted PASM code works as there is no way to test it until I connect its output to the LCD.
    I need some help with the PASM to get it at least to a starting point. The main program works in SPIN, and it does CNTRREAD in SPIN and returns the encoder position and it is displayed on the LCD. On the PASM version what I want to do is send the PASM core a variable to update with the SPI read position, the value to update is EncoderTest. At present the PASM code does not recognize the EncoderTest I sent it as a pointer. I need help to 1. tell the PASM code to return the value via EncoderTest and 1. keep looping.

    Any suggestions appreciated. I already have too much time in trying to figure this out, I'd be happy to pay a fee for a few hours if someone want to help write an object based on some existing SPIN code.
  • T Chap wrote: »
    I have been experimenting with fastspin and have created a SPIN file that I sent to fast spin and created a PASM output. I took the PASM parts and copied it into a SPIN code to try to merge the parts to create a demo. I feel like the -w for spin wrapper is not the correct idea since I can't seem to get the PASM to stay in a loop, it only wants to be called from SPIN to do reads, I want the PASM to do its on loop with no supervision for the first test.
    If you write the SPIN code to work the way you want (to stay in a loop reading the data) then the converted wrapper will work that way too. The wrapper should have exactly the same calling convention as the Spin, the only difference is that it will run in another COG and at PASM speed. So basically you'd remove all references to the LCD code, and your Setup routine would take a pointer as parameter and would loop forever:
    PUB Setup(EncoderPtr)
      SS(SSN, 1)
        long[EncoderPtr] := CNTRRead
        ' maybe put a waitcnt here??
    Then in your "regular" Spin code you'd do something like:
      myLS7366: "LS7366.cog.spin"   ' the converted wrapper
      long encoderVal
    PUB demo
      myLS7366.__cognew ' start the wrapper COG
      ' now encoderVal will be continually updated with new values

  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 19:44:16
    Thanks for the help. I have updated the main program and created a spin object that seems like it is what you described. I removed all the LCD and serial stuff, just left the basic functions that were working. I get 0 for a reply that changes to -1 after some time. The original SPI code has waitcounts in it so I would assume those would stay the same. But I'm not sure the other code is not running too fast now. I can connect a logic analyzer if needed to see what it looks like. But I wanted to see if anyone say anything obvious.

    Here is the main program, the SPIN version "object", and the fastspin conversion lsobject.cog.spin

    In added a substitution in the object to only return "5" just to see if the object worked and it did return 5. So it looks like the SPI is now working.

    using BST

    EDIT Actually nothing is happening on the lines. SSN is high and it should be LOW for all data to be moving. But __busy is -1. Remove the call for setup and busy is 0.

    Remove the setup call from main and loop Cntrread from main and the clock runs and SSN runs but no miso mosi activity.
  • The waitcnt(10_000_000) call inside the demo program is very suspicious. This will wait until the cycle counter CNT is exactly 10_000_000, which could take anywhere from 0 to 50 seconds depending on what the initial value of the counter is. I think you want
      waitcnt( cnt + 10_000_000 )

    Similarly for the waitcnt inside your Setup loop in LSobject.spin.

    I'm also curious about the code that looks like:
      !outa[Cpin] ' cycle clock
      waitcnt(1000 + cnt)
    that occurs in lots of places. This is toggling the Cpin very quickly indeed when compiled with fastspin. Possibly you want delays after both of the !outa[Cpin] and not just the first one? So something like:
      waitcnt(1000 + cnt)
      waitcnt(1000 + cnt)

    You might even want to make this a subroutine so that you can easily tweak the delays.

  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 21:28:44
    Thanks for catching the waitcnt issue. I never type cnt as I normally have a subroutine for waitcnt but for this scaled down version I removed everything including the wait sub and just typed it by habit. Now with the waitcnt fixed on both parts the clock is running and SSN is running but still no MOSI or MISO.

    I added the extra waitcnt lines but still no activity on MOSI or MISO.
  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 21:52:11
    Just for the heck of it I'll post the latest version with extra waits. But the problem is there are no MOSI MISO signals. I'd love to find somebody that could put together this object for a fee.

    I assumed based on what you suggested earlier that I did not need to call Cntrread, that it would automatically run because that's how the SPIN was running.
  • My guess is you also need some delays in SS, SHIFTOUT, and SHIFTIN when you're changing the Dpin values. Basically any time you change a pin's value by writing to outa you may need to have a delay.
  • Working! Many thanks Ersmith. this is very cool what you have designed for conversion. I added more delays as suggested and it started working right away.
  • T ChapT Chap Posts: 3,931
    edited 2018-10-14 - 22:42:56
    I don’t understand how calling other methods works. If I call Clear counter from main it does clear but then if you immediately after call setup or Cntrread it stops working. I thought it would go right back to reading. It actually gets stuck on the call, the main loop locks up as if it is waiting on something to continue. Maybe it is waiting on a lock?
      Repeat 500
       ser.decf(3, encoderVal, 5)
       ser.decf(3, lsobject.__busy, 5)    ' never gets back here to show busy status.  
  • Glad you got something working!

    The problem with CNTRClear after Setup is that once you call Setup the new COG will be spending its time in a polling loop doing CNTRRead and updating long[EncoderPtr]. It will literally have no ability to respond to any other commands (such as a request to do CNTRClear).

    You could extend Setup to have a command pointer as well, something like:
    PUB Setup(EncoderPtr, CommandPtr) | cmd
      SS(SSN, 1)
        ' check for commands
        ' command 1 = clear counter
        ' command 2 = stop updating EncoderPtr
        if long[CommandPtr] <> 0
          cmd := long[CommandPtr]
          if (cmd == 1)
            long[CommandPtr] := 0
          elseif (cmd == 2)
            long[CommandPtr] := 0  ' tell caller we are done command
            return ' stop processing commands and updating EncoderPtr
          long[EncoderPtr] := CNTRRead

    Alternatively, of course, you could never call Setup() at all and just have the main Spin cog poll by calling CNTRRead itself. I'm not sure what your goal is in having the sub (pasm) COG continually updating long[EncoderPtr]. Is it that you want the main COG to be doing some processing in parallel with the CNTRRead? If so you can split the CNTRRead up into two parts: StartCNTRRead (which actually does the work and sets a variable with the result, but does not return a value) and GetCNTRRead(which just returns the value from the variable). See the "Synchronous and Asynchronous Operation" section in the document for an example.

  • I updated my earlier post after you posted. I can tell that it is actually doing the clear but hangs after that.
  • There is a SPIN PID loop in a cog and I don't like to affect its timing, so that PASM reading the encoder count is a bit overkill but at least it will have the most up to date count possible when the PID looks at it. But your example will work perfectly. There are other uses for the fastspin conversion so this is partly a learning experience. Thanks!
  • T ChapT Chap Posts: 3,931
    edited 2018-10-15 - 00:05:58
    Actually it does not like to be disturbed after it is launched even with the new suggestions. But I can always get stop and start a new cog for each command since it is very rare that a reset will be required.

    Edit Nope it hangs up cogstop as well. I think the PASM code will have to be modified to watch a pointer to a command value as I don't think the SPIN calls are going to work after the loop starts.
  • T ChapT Chap Posts: 3,931
    edited 2018-10-15 - 00:31:01
    Got It! I was trying to call the setup again with the commandptr. You can't do a spin call after the loop starts or it hangs, no matter if its cogstop. BUT you can just update the commandPtr in main and the object will update as you want.

    Maybe that’s what you were thinking on the main side but I was trying to re call setup.

    PUB demo
      ls.__cognew ' start the wrapper COG
      ls.Setup(@encoderVal, @commandPtr)
      Repeat 500
       ser.decf(3, encoderVal, 5)
      commandPtr := 1  ' CLEAR COUNT  <<<<<<<<<<<<<<<<<<<<<<
      waitcnt(10_000_000 +cnt)
       ser.decf(3, encoderVal, 5)

    PUB Setup(EncoderPtr, CommandPtr)
      SS(SSN, 1)
          long[EncoderPtr] := CNTRRead
          IF long[CommandPtr] == 1    '  WATCH for a Command here
  • The SPIN version fits fine in a core but the PASM conversion -w from fastspin is 35longs too big. The error shows fit 496 as the problem. I notice some other compile options. Is there any smaller compile version? I still have more to add. Thanks.

    EDIT I combines some repeated items into a method and shaved 25 longs now 10 longs over.
  • I would suggest making some common routines like
      waitcnt(delay + cnt)
    into subroutines; that should save some space. Perhaps you could combine some code as well; MDR0write and MDR0Read are almost the same, for example, so perhaps they could both call a common subroutine.

    I'm curious: what's your overall goal with converting this code to PASM? Is it to speed it up? You may be able to just compile your whole project with fastspin (in LMM mode, so it doesn't have to fit in a COG).
Sign In or Register to comment.