Table execution in SPIN?
Larry Martin
Posts: 94
I have a SPIN/PASM application with 4 nearly identical cogs. The cogs are adapted from Full Duplex Serial, but know the trigger input and serial protocol. They transmit on trigger and retry autonomously on receivce failures. The cogs differ only by a VERSION line at the end of each one (thanks to Tracy Allen). My SPIN top level has about 10 functions like this:
I'm up against memory limits and could use a more compact syntax like <MOD>.rxflush, but SPIN doesn't seem to let me assign a MOD value like UidTid to a variable. I would vastly prefer to do something like:
A jump table would also work. Am I right in thinking SPIN doesn't provide either?
Thanks,
Larry
PUB RdrFlush(pRdrIx) case (pRdrIx) 0: UidTid.rxflush 1: Encode1.rxflush 2: Encode2.rxflush 3: Encode3.rxflush
I'm up against memory limits and could use a more compact syntax like <MOD>.rxflush, but SPIN doesn't seem to let me assign a MOD value like UidTid to a variable. I would vastly prefer to do something like:
LONG mod_p ... mod_p := UidTid ... mod_p.rxflush
A jump table would also work. Am I right in thinking SPIN doesn't provide either?
Thanks,
Larry
Comments
where Rfid1..4 are generated by a batch file before building. The file I maintain is just called rfid.spin.
Would I need a custom SPIN compiler for that, Dave?
NVM, that's only the DAT section that is shared.
uarts.rxflush(mod_p)
in one cog and one instance of the code.
What is the nature of the triggers and auto-retry? I'm wondering if it would take any modification at all of the 4-port object.
http://forums.parallax.com/discussion/128397/callbacks-and-method-pointers-in-spin
No, the existing logic about fills a cog, with 36 words left over. I can't post it, but it:
* gets a pointer to a chain of command bytes in global memory (multiple commands)
* gets a pointer to an array of command lengths
* on trigger, sends the first command and starts looking for reply bytes
* parses the reply length from received bytes and handles the end of reply correctly
* checks reply status and resends the command on bad status
* if status is good, the command length cursor indexes and the next command is sent
* when the next command length is 0, a Complete flag is sent to SPIN via wrlong
* with tracing
all in PASM, in 4 cogs simultaneously, at 115 kbaud. IMHO, it's about maxed out.
https://forums.parallax.com/discussion/86518/callback-hack
'Not sure I'd recommend using it production code, though. It is, after all, just a hack.
-Phil
forums.parallax.com/discussion/120467/procedure-variables-or-callbacks-in-spin
Based on that article, I have tried changing this:
to this:
Given this:
The trick seems to be to put square brackets after a symbol, fooling SPIN into indexing it like an array. Type goes with section, so a VAR symbol can only do VAR stuff, but items indexed off an OBJ symbol can do OBJ stuff.
It compiles. I'll see if it really works tomorrow.
My system has 4 serial connected devices that do the same task with different data, at the same time, on an external trigger. Timing is so tight that each needs its own COG so trigger response and error recovery can happen at the speed of PASM.
My understanding of the COG mechanism may be a little off, but here goes. I think:
1. The way to get a COG to execute PASM is to put the PASM in a DAT block and have SPIN call cognew with a pointer to an "entry" label in PASM.
2. If you have more than one identical instance of an OBJ, then SPIN will combine them, so you have to make the DAT sections unique. Tracy Allen taught me this a couple of years ago, when I was trying to use two instances of the four-serial object.
3. So I wrote a batch file to copy my RFID.spin to RFID1..4.spin, appending a VERSION line to each one.
Is it possible instead to run multiple COGs off one OBJ? Something like this:
Would that lead to 4 COGs running the same PASM against 4 different blocks of global data?
Yes, that's the idea of the Spin objects. There are spearate base pointers for data and code, and if the code is the same for several objects, the memory needs only to hold one instance, but allocates a sepearate data section for every included object.
The object must be programmed the right way for that, the FullDuplexSerial supports this.
If you change a Version line, the object gets 4 times copied into hub-memory, so you waste a lot of memory.
I think the 4 port-serial is not made for this, that's why the version-line was needed to get more than one independent instance.
Andy
Dave, I think you were right the first time. Last night I changed all my CASE statements to indexed accesses to a base OBJ symbol. The system seems to be working today. Actually, it's getting bad results, so there may still be something sneaky going on. But the lights controlled by each COG come on, all four serial streams are working, and my diagnostics show different reasonable values for each COG. The changes gained me back about 100 words.
Thanks, everybody.
Larry
So what feature of the programming allows this to be different? Does the self patching behavior of 4-port drive the need for separate DAT sections enforced by VERSION lines?
I just spent a couple of hours consolidating the four MODs into one. I gained around 1000 longs, which is great. But the image resets on RAM load, so I only see the safe version in EEPROM. So I guess I got some indexes wrong. I sense a black hole of debugging, so that one is getting put away for a while. Thanks for the idea.
Yes, that worked too, and saved 1500 longs! Thank you very much!
Here are diagnostic dumps of the RX buffer of two different COGs' RX buffers:
The contents are different and reasonable. So even though there is just one source file, four cogs are running the PASM separately, referenced to 4 different global memory regions. The OBJs are being accessed through array accesses to the first one:
This is awesome, thanks again.
And here's how I uses that object array -- it's as simple as it seems it should be.
It's probably been covered already, but keep in mind that when you use the same object file more than once, the hub code and DAT sections are shared between them, though each instance will get its own set of variables.