Right way to share memory
Steve Hicks (N5AC)
Posts: 20
I have several situations in which I want to share data between objects and cogs, but I'm not sure of the "right" way to do this... should I create the variables at the top level and pass them down into objects?· For example, I have· GPS object that is going to collect a number of pieces from the GPS and store/update them in memory.· I hate to pass a dozen variables in and out of the object... should I put the variables in the GPS VAR declaration and create set/get routines for all of them? Should I define a common block (structure) in memory and define it in both places along with an address so they are "overlaid" and handle it like this?
Thanks,
Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
Thanks,
Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
Comments
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Post Edited (Chip Gracey (Parallax)) : 11/6/2006 8:53:46 AM GMT
I've thought long and hard about this myself. From the past few months, I have determined one of the better, and seemingly more common, techniques is to pass the location of a block of memory (using the "@" symbol).
AKA
Let's say that you've got 16 arbitrary variables you want shared amonst different cogs, some SPIN, some PASM. You "create" a memory location in your primary cog.
When starting new cogs, send the address, like:
One main issue is cross-cog clobbering of variable (reading the same variable by two cogs = bad). You can avoid this due to one main important factor; as long as the variable is ONLY being read by one COG and written by another COG will you have no problems, due to the HUB access window. Any more access than this will lead to problems. (you can read more about this in the manual)
Regardless, you can use this block of memory in all 8 Cog's provided you are clever. AKA- each actual memory location is only read by one cog and written by another (which seems to be the most common use). For instance, using a Cog to update an ADC value that is used to compute some output value in another cog. Now, if you set it up such that each memory location is being used via the rule I explained, you could assign your 16 locations like this:
That code is not literal, only conceptual.
As long as Cog0 is reading, and Cog1 is writing, you will always seemlessly be able to access Memoryblck[noparse][[/noparse]0].
There are other well documented techniques you can search for, this is only one, and works best when working with Cogs running aseembly code...
-Parsko
and then in the calling routine:
This seems like common object oriented methodology and assumes a loosely coupled interaction between both modules·and maximum reusability.· I am currently using this and it works well.· It is rather tedious if you have a lot of variables and it's not terribly efficient and arguably a bit of overkill for a bit of·embedded software!
I do see the method for passing the address of a block of memory around, but it seems to preclude really typing or naming these in a way that makes the code very readable ... you end up referring to a variable as memoryblock[noparse][[/noparse]13] or something like that... what I'm wondering is: is there a better way?· For example, make a DAT section, use an ORG to go to a specific location (high memory?) and then define the variables in a subsequent variable block --- like an overlay of days gone by.· I tried this, and received the error that I couldn't go above $1F0 with my ORG... so I now understand that assembly ends up in cog memory, not main memory, so that's not going to work since cog memory is not shared.
Is there a way to specify the address that variables in a SPIN VAR block would be located?· Am I barking up the wrong tree?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
Have a look at my Propeller OS <http://forums.parallax.com/forums/default.aspx?f=25&m=147980>. The initialization routine allocates a block of pointers at the end of RAM, then allocates work areas below that and puts pointers to the work areas in the block of pointers. Various I/O drivers (like display, keyboard, etc.) use those areas rather than VAR allocations. The offsets from the pointers are declared in a CON section in each object and referred to with the obj#const notation in other objects.
Mike
Spin will treat you better if you don't try to use traditional OO development practices. It's really more of a module-based language than an object-based one. With some features missing (passing of objects, structures, polymorphism) and without any optimizations in the compiler, practices like encapsulation and delegation are only going to slow you down.
I know, it bugs me too.
73s,
W7CLB
So there's a block of variables and then constants·are set to match the locations in memory.· They just have to be kept in sync.··The address of the first variable (start of the block) is then passed back to the consumer of the object.· Now a look at the consumer of the object:
In the initialization call to the object, the address of the block of variables is stored in memory (GPSbase).· It is subsequently used as an offset when getting data out of the block of memory.
This seems to work fine, it's a good compromise and it's readable.· Note that the data I'm using is almost all longs or smaller -- the exception is the list of GPS satellites.· A lock (see LOCKNEW in Propeller manual v1.0 p230) may be need to be used in some situations where data larger than a long is being written by one entity and read by another (for example in this scenario, one satellite may drop off the start of the list as I'm reading the middle and the cascade ripple might cause me to lose one satellite as I read ... just an example).
Maybe this will help someone else struggling with the best way to do this!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
Here is a cleaner way to enumerate those offsets:
CON
· #0
· GPStime·········· [noparse][[/noparse]4]
· GPSlatitude······ [noparse][[/noparse]4]
· GPSlatitude_frac· [noparse][[/noparse]4]
· GPSlatitude_c
· GPSlongitude····· [noparse][[/noparse]4]
· GPSlongitude_frac [noparse][[/noparse]4]
· GPSlongitude_c
· GPSfix
· GPSsatellites···· [noparse][[/noparse]16]
· GPSspeed
· GPScourse
· GPSaltitude······ [noparse][[/noparse]4]
· GPSlock
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Is it just me, or is that some undocumented syntax you just posted?
Glad I'm not trying to write a SPIN parser, with these surprises.
CON
#0, None, Left, Right, Front, Back ‘Enumeracion de Direcciones
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Check the manual, pages 197ff. (The only thing new here is the size declaration in brackets. But I many not have the most recent manual either.)
-Phil
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
The autoincrement-constant pattern for enums is in there, but (as Phil stated) the ability to apply array dimensions to them isn't explicitly stated.
Weeeeeird.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Post Edited (Chip Gracey (Parallax)) : 11/7/2006 11:57:13 PM GMT
I'm sorry, I could have also done this, but wasn't at home. The following links might help:
What is the fastest method for data transfer between COGS?
Using Semaphores
Object Instances
ASM ref to Main memory OTHER than PAR passing address
How to pass data between multiable objects in multiple COGs?
Arrays
Stacks
They are from my "internal communications" folder in bookmarks(I hope I didn't mess up the name order). Not sure if all apply, but they all contributed to my understandings.
-Parsko
Post Edited (parsko) : 11/8/2006 5:32:07 PM GMT
(All for assembly language) The absolutely fastest way to transfer data between cogs is to use I/O pins, but that's a valuable and very limited resource. The next fastest way is to use a common area in main memory, either known to both cogs (fastest) or relative to PAR (almost as fast if you use instruction modification for locations other than the one actually in PAR).
Once you use SPIN, raw speed is not that important (different techniques don't save you much over code clarity).
Semaphores are also a limited resource and are only needed when more than one cog is going to access the same variables at the same time. The exception is the "single producer, single consumer" where only one cog can change a variable, but another cog will refer to the variable (like a buffer pointer). You don't need a semaphore for that.
In this example, lock ends up at $2484 and time ends up at $2488 ... just like I would expect.· But when I do it like this:
lock ends up at $25A1 and time at $2484 ... BEFORE lock in memory (and where lock was before)!
All this would be more evident if I could just print out a symbol table after the compile so this is a request!· I'd like to be able to print a symbol table out after the compile process.· Heck, seeing how everything fits in memory (not just symbols but sizes of routines and start/end addresses, long count) would be instructional.
Can someone explain what is going on here, what the rules are and if there is a dependable behavior?· For example, if all longs get grouped together in memory in incremental order (as they appear in source), I'll just make everything longs and be done with it.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
I'm sorry this has not been documented, but the compiler groups all longs, then words, then bytes, to avoid gaps between variables, thereby crunching VAR space. So, all VAR longs will be in the order that they were declared, as will all words and bytes.
I think we will add some kind of post-compile report, like you're talking about, to the Propeller Tool in the future. Sorry you had to spend so much time on this.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Post Edited (Chip Gracey (Parallax)) : 11/9/2006 8:26:06 PM GMT
Another thing to keep in mind is that the first 8 VAR longs and the first 8 local longs (beginning with RESULT, then parameters, then local vars) have special single-byte Spin codes for reading and writing them. While a byte may be all that's required to store your variable, it could be more efficient to use a long, instead. Bytes are only worthwhile if you have an array. The same·goes for·words. If it's just·a single variable, use a long.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.
Post Edited (Chip Gracey (Parallax)) : 11/9/2006 8:27:46 PM GMT
In either case, I think my recommendation will be to avoid the confusion and just pass longs unless there's a real memory issue.
Thanks!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
I made another post, above, that I hope you see. It adds a little more.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.