Both compiled fine, but upon execution, the Primary program is showing that the Subsidiary always responds with a "1", regardless of what input value was provided to it. According to the Subsidiary code, it's supposed to return the square of the input.
You are missing a line in your "primary.c" program. I have attached a copy with the necessary line added.
Here are the commands I used to compile your program (this was for my C3 platform - you will probably need to modify at least the -R parameter):
Also, note that in the "primary.c" code, I've added but then commented out a new variable called "shareptr" which is set to the address of the shared structure.
I hope this will be acceptable, because in my GPS Project, the Primary main() function will need to pass this pointer to various functions that it calls in order to manipulate and display the data within the shared structure.
Ditto for the Subsidiary main() function. This main() function will call several other functions to parse, process, reformat, and store the streaming data from the GPS receiver into the shared structure. The only way I know to do this is to pass the pointer that main() received over to these other functions so they can access the structure.
It is acceptable, but note that you can't just allocate a pointer to this type - that allocates no actual memory. You should allocate your shared variable in the main program as an actual type, then take its address and pass it to the subsidiaries that need it. You could instead allocate it in the subsidiary and pass it to the primary program, but to do so you will have to define a variable suitable for passing it in the primary program.
Within your "primary.c" program you've included a subsidiary memory check feature to verify that the "subsidiary_1.c" program was indeed loaded into the proper location in HubRam. The results of this check are then output to the Console (i.e. default UART port).
This approach will create a problem in my GPS Project because I'm using the 4-Port serial driver, and the CduPrint() function is contained within the Subsidiary Program, not the Primary Program. So if there's a memory check problem, I will never get a response on the Console port.
Another option would be to just identify the area of Hub RAM that is unused by your primary, and just use it manually, such as is done in my demo example 1.
What is the Memory Map of HubRam?
What location boundaries are acceptable to contain the Subsidiary program?
Ah! That depends on many things - e.g. the size of the primary program, and the stack space it needs, and also the plugins you load, and the cache options you use. Try writing a very simple primary program that loads all the necessary plugins etc (i.e. is compiled with exactly the same options as the real primary program) but which just prints out the address of a variable local to the main stack frame. Then estimate your actual subsidiary program size and estimated stack usage and subtract it from this value.
Is there any way to compile the Subsidiary program to some default memory location, then have the compiler automatically reposition it to the proper memory location when compiling the Primary program in order to avoid the "hit and miss" approach mentioned above at runtime (and thus requiring a Console port to see the results?)
Sadly, no. All subsidiary programs are compiled to run at a specific address.
Finally, what is the maximum memory size allowed for a single CMM Subsidiary Program if I have it configured to use the 4-Port UART driver and the RTC driver, and the Primary Program is using 8K of cache while operating in the LARGE Memory Model?
Use the technique above to find out. Here is an example program you could try compiling:
#include <catalina_hmi.h>
#include <stdlib.h>
void main(void) {
int var;
t_printf("memory below %x is reserved\n\n", _sbrk(0));
t_printf("memory above %x is reserved\n", &var);
t_printf("(and from this value you must\n");
t_printf("subtract your expected stack usage!)\n");
while (1);
}
As mentioned previously for my GPS Project, the main() function in the Secondary Program will need to pass the address of the Shared Structure to various other functions in order to process, parse, store, etc the GPS data.
This Test Program incorporates the concept I plan on using to do that, unless you can think of a better way.
The first variable in the Shared Structure itself will contain the Memory Address of the Structure, and it will be loaded by the Primary Program prior to activating the Secondary Program.
Here's the Primary Program:
//Program Is Primary.c
//Last Revision On 8 May 20
//catalina primary.c -lci -C C3 -C TTY -C COMPACT
//catalina primary.c -lci -C TTY -C CUSTOM -C LARGE -C CACHED_8K
#include <catalina_cog.h>
#include <catalina_hmi.h>
#include <catalina_plugin.h>
#include <propeller.h>
#include <math.h>
#include "secondary.inc"
typedef struct CduType
{
unsigned int Memory;
unsigned int CduPrintEmpty :1;
unsigned int CduRxDKey :8;
unsigned int CduTaskLine :7;
unsigned int CduPrintScreen :16;
char CduTxDStr[200];
}CduInfo;
void main(int argc, char *argv[])
{
char SECONDARY_RESERVED_SPACE[SECONDARY_RUNTIME_SIZE];
CduInfo Shared;
int Cog;
t_printf("Primary program started.\n");
if(SECONDARY_CODE_ADDRESS != (int)&SECONDARY_RESERVED_SPACE)
{
t_printf("Error: Secondary Not Compiled To Run At 0x%X - Edit And Recompile\n",SECONDARY_RESERVED_SPACE);
while(1);
}
t_printf("Secondary Space Is 0x%X\n", &SECONDARY_RESERVED_SPACE);
Shared.Memory=(int)&Shared;
Shared.CduPrintEmpty=1;
Shared.CduRxDKey=0;
Shared.CduTaskLine=0;
Shared.CduPrintScreen=0;
strcpy(Shared.CduTxDStr,"This Is A Test");
t_printf("Location Of Structure=0x%X\n",Shared.Memory);
t_printf("Starting Secondary\n");
Cog = start_SECONDARY(&Shared, ANY_COG);
while(1);
}
And here's the Secondary Program:
//Program is secondary.c
//Last revision on 9 May 20
//catalina secondary.c -lc -lm -R 0x5184 -M64k -C CUSTOM -C TTY -C NO_ARGS
//spinc -B2 -n SECONDARY -s 200 -c -l secondary.binary > secondary.inc
#include <catalina_cog.h>
#include <catalina_hmi.h>
#include <catalina_plugin.h>
#include <propeller.h>
#include <math.h>
typedef struct CduType
{
unsigned int Memory;
unsigned int CduPrintEmpty :1;
unsigned int CduRxDKey :8;
unsigned int CduTaskLine :7;
unsigned int CduPrintScreen :16;
char CduTxDStr[200];
}CduInfo;
void ViaPtr(CduInfo *Ptr)
{
t_printf("Access Using CduPtr\n");
t_printf("Location Of Shared Memory Is 0x%X\n",(*Ptr).Memory);
t_printf("CduPrintEmpty=%d\n",(*Ptr).CduPrintEmpty);
t_printf("CduRxDKey=%d\n",(*Ptr).CduRxDKey);
t_printf("CduTaskLine=%d\n",(*Ptr).CduTaskLine);
t_printf("CduPrintScreen=%d\n",(*Ptr).CduPrintScreen);
t_printf("CduTxDStr=%s\n\n",(*Ptr).CduTxDStr);
}
void main(CduInfo *Shared)
{
CduInfo *CduPtr;
CduPtr=(CduInfo*) Shared;
t_printf("Secondary program running on cog %d\n\n",_cogid());
t_printf("Direct Access Via Shared\n");
t_printf("Location Of Shared Memory Is 0x%X\n",Shared->Memory);
t_printf("CduPrintEmpty=%d\n",Shared->CduPrintEmpty);
t_printf("CduRxDKey=%d\n",Shared->CduRxDKey);
t_printf("CduTaskLine=%d\n",Shared->CduTaskLine);
t_printf("CduPrintScreen=%d\n",Shared->CduPrintScreen);
t_printf("CduTxDStr=%s\n\n",Shared->CduTxDStr);
ViaPtr(CduPtr);
while(1);
}
And here's the result:
So this approach seems to work.
For some reason I'm having trouble getting the Primary to execute in XMM LARGE (payload is giving me a timeout error after upload), so I'll focus on that next since XMM LARGE is what will be required.
As mentioned previously for my GPS Project, the main() function in the Secondary Program will need to pass the address of the Shared Structure to various other functions in order to process, parse, store, etc the GPS data.
Your method is more complex than strictly necessary. In particular, you don't need the "Memory" field of your shared structure, since the address you put in there is simply the address of the shared structure itself, which the subsidiary program is given (as you can see when the program is executed).
I would also recommend defining your shared variable structure in a common header file that is then included by all the programs (primary and subsidiary). This will make program maintenance easier.
I am not sure what the XMM issue is. I run both XMM SMALL and XMM LARGE versions of the demo programs on my C3. You may want to check your program size actually fits in your XMM RAM?
Don't know why I went off on the Memory variable tangent, but after checking on dinner and coming back I looked at the code again and realized it's not needed.
Yes, for the actual GPS Program itself, and not this Test program, all Defines, Structures, Variables, etc., will be in separate files that can be #include by both the Primary and Secondary Programs. But for this Test example, I decided to have the Structure listed in each respective source file.
I'll have to take a look at the XMM LARGE issue later tonight after dinner. It's likely a setting I have wrong because it was working with a different program earlier.
I'll have to take a look at the XMM LARGE issue later tonight after dinner. It's likely a setting I have wrong because it was working with a different program earlier.
It may also be worth checking that your XMM and loader utilities have been built correctly - especially if you use different cache sizes for different programs. Rebuild them using the "build_utilities" script if necessary.
I got the XMM Primary Program to run. It appears that I somehow had messed with some of the settings under the Build Options in Code::Blocks.
When experimenting with my TEST program mentioned above (which was running in CMM for both Primary and Secondary) I was using the -ltty and -C TTY compiler switches for both, and it was working fine as noted in the posting.
I switched over to a different program, MenuTest, which essentially has more junk to send to the screen than TEST. It runs the Primary in XMM, the Secondary in CMM, and uses the 4-Port serial library. But I can't get any response from the Secondary upon activation.
I've verified that the only Plugin that's loaded by both the Primary and Secondary programs is the 4-Port library. I've also compiled both with the -C NO_HMI option. The Primary program uses the -lc library while the Secondary uses -lci.
When I execute MenuTest , the Primary prints to Console using the s4_tx() function as expected, and shows that the Secondary is loaded at the correct address. But there's no response from Secondary. It should give me a "Secondary Activated" prompt but doesn't.
I'll need to take a closer look either tomorrow or Monday at the 4-Port issue to see what I'm missing.
When I execute MenuTest , the Primary prints to Console using the s4_tx() function as expected, and shows that the Secondary is loaded at the correct address. But there's no response from Secondary. It should give me a "Secondary Activated" prompt but doesn't.
The 2 most common issues I have found when messing about with Multi-Memory Model programs are:
1. Accidentally using an area of memory that is no longer free (especially if you are using manual memory allocation, and your program grows in size or you change your compiler options, such as including caching or changing your plugins etc)
2. Not allocating enough stack space for the subsidiary program (via the -s option to spinc).
The 2 most common issues I have found when messing about with Multi-Memory Model programs are:
1. Accidentally using an area of memory that is no longer free (especially if you are using manual memory allocation, and your program grows in size or you change your compiler options, such as including caching or changing your plugins etc)
2. Not allocating enough stack space for the subsidiary program (via the -s option to spinc).
It is worth checking both of these!
OK, while still working with the MenuTest program, I've configured both the XMM Primary and the CMM Secondary to compile with the -lc, -lm, and -lserial4 libraries. I needed -lc because I'm using the itoa() function in the Secondary (without it I previously compiled using -lci).
I've adjusted the XMM Primary to use the LARGE memory model, and varied it's cache from 8K down to 1K (using build_utilities each time to reconfigure it). No response from Secondary. I've adjusted XMM Primary to use the SMALL memory model, and again varied its cache from 1K to 8K. No response from Secondary.
I've adjusted the CMM Secondary stack space from 200 up to 1000. No response from Secondary.
I have to run some errands, but hopefully I'll be able to examine this further later on today when I get back...
I have to run some errands, but hopefully I'll be able to examine this further later on today when I get back...
I'm out most of today but if you don't solve it, email me your complete program (including build script). I'll compile it here and should be able to tell if everything is correct even if I can't actually run it.
1. If I compile the Primary as XMM SMALL and leave the Secondary as CMM, I get the same results as shown above. The Primary activates, but no response from the Secondary.
2. Leaving the Primary as XMM SMALL, and the Secondary as CMM, but also including the EEPROM loader, the Primary code will execute as above, Secondary still doesn't, but for some reason the Prop appears to reboot/reset repeatedly with the above message displayed over and over again while scrolling the screen. Very strange.
3. Compiling the Primary as CMM, and leaving the Secondary as CMM, results in the Primary not activating at all. Just a blank screen. Odd, because the code did compile fine even though the resulting file size was a bit over 31K bytes. I don't understand why the Primary no longer works.
Maybe there's enough here to narrow down the problem. I'm hoping it's just a compiler setting causing the Secondary not to activate, and can easily be remedied by changing a compiler switch or two...
Those are my observations for today. I have some other tasks I must work on tomorrow, but hopefully I can revisit this again later in the day then.
I think I have found your problem - your primary program is allocating the SECONDARY_RESERVED_SPACE array within the CheckMemory() function. Any memory allocated within a function is released once the function exits. This was corrupting the memory being used by the secondary program
Instead, allocate this memory in the main() function, and just pass the address into the CheckMemory() function. Here is some code - amended as described - which now seems to work as expected:
// Program Is Primary.c
// Last Revision On 10 May 20
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <catalina_serial4.h>
#include <catalina_cog.h>
#include <catalina_plugin.h>
#include <propeller.h>
#include "Structs.h"
#include "secondary.inc"
char CheckMemory(void *Memory)
{
char TempStr[80];
if (SECONDARY_CODE_ADDRESS != (int)Memory)
{
sprintf(TempStr,"Error: Secondary Not Compiled To Run At 0x%X - Edit And Recompile\n",
Memory);
s4_str(0,TempStr);
return(No);
}
sprintf(TempStr,"Secondary Code Address At 0x%X\n\n",Memory);
s4_str(0,TempStr);
return(Yes);
}
void main(void)
{
char SECONDARY_RESERVED_SPACE[SECONDARY_RUNTIME_SIZE];
CduInfo CduData;
char TempStr[80];
int Cog;
CduData.CduPrintScreen=0;
CduData.CduTaskLine=0;
strcpy(CduData.CduTxDStr,"Hopefully Secondary Is Now Active\n");
sprintf(TempStr,"Primary Program Is Active\n\n");
s4_str(0,TempStr);
if(CheckMemory(SECONDARY_RESERVED_SPACE) == Yes) Cog=start_SECONDARY(&CduData,ANY_COG);
while(1);
}
There is another (minor) problem in Structs.h, which is that both yes and no were being defined as 1. Here is the amended code:
// Segment Is Structs.H
// Last Revision On 8 May 20
#define CduPort 0
#define Yes 1
#define No 0
typedef struct CduStuff
{
unsigned int CduPrintScreen;
unsigned int CduTaskLine;
char CduTxDStr[200];
}CduInfo;
CduInfo *CduPtr;
While investigating this problem, I did find a potential problem with the serial4 library which would affect a program (such as yours) which uses it from both the primary and the secondary programs - it is possible the output would end up corrupted. I will fix this in the "official" release.
Looks like I did some rather sloppy programming there. The problem with the #define in Structs.h is inexcusable.
I made the corrections, and it works fine now in XMM LARGE. Both the Primary and Secondary are active.
However, I recompiled the Primary to run under XMM SMALL, and neither the Primary nor Secondary are working. Just a blank screen.
I then recompiled the Primary to operate in CMM. Same problem as XMM SMALL: neither Primary nor Secondary are working, and just a blank screen.
I don't know if the cause in these two cases could be the 4-Port driver issue you mentioned or something else.
At this point it's not a major issue for me, just as long as XMM LARGE is working.
Question: Must the char SECONDARY_RESERVED_SPACE[SECONDARY_RUNTIME_SIZE]; only be defined within the main() of the Primary program?
Can it be defined as a Global Variable within the Primary program, or is it restricted to being a local variable within main() of the Primary? Just curious.
Anyway, great progress made on this today with your help.
We'll see what tomorrow brings (if I get the time to work on it some more).
Question: Must the char SECONDARY_RESERVED_SPACE[SECONDARY_RUNTIME_SIZE]; only be defined within the main() of the Primary program?
Can it be defined as a Global Variable within the Primary program, or is it restricted to being a local variable within main() of the Primary? Just curious.
The array must exist in Hub RAM. If you make it a global, that would work ok for the CMM, EMM, SMM, LMM, XMM SMALL & (on the P2) NMM memory models. But when using the XMM LARGE memory model globals are allocated in XMM RAM, not Hub RAM. Only local variables (e.g. variables allocated on the stack) are allocated in Hub RAM.
So, the easiest way to make sure your code will work under ALL memory models is to make the array a local variable of the main function, since main only ever exits if your program terminates.
Note: The array could be allocated in a function if that function starts, uses and then terminates the secondary program before the function returns.
Of course, you will need to change C3 (to CUSTOM), and possibly the -R parameter.
The program will not work when primary.c is compiled as COMPACT. Even with optimization, it is too large.
Ross.
The Secondary program will be activated under XMM SMALL if I use CACHED_1K or CACHED_2K when compiling the Primary.
If I try CACHED_4K or CACHED_8K, the Primary will activate but the Secondary never will.
No luck getting Secondary to work using these last two cache sizes, even though I double checked the configuration each time I used build_utilities to change the cache settings...
Did CACHED_4K and CACHED_8K work for you? If so, then I still don't have something right within the compiler settings...
The array must exist in Hub RAM. If you make it a global, that would work ok for the CMM, EMM, SMM, LMM, XMM SMALL & (on the P2) NMM memory models. But when using the XMM LARGE memory model globals are allocated in XMM RAM, not Hub RAM. Only local variables (e.g. variables allocated on the stack) are allocated in Hub RAM.
So, the easiest way to make sure your code will work under ALL memory models is to make the array a local variable of the main function, since main only ever exits if your program terminates.
Note: The array could be allocated in a function if that function starts, uses and then terminates the secondary program before the function returns.
OK, that makes perfect sense.
In the XMM LARGE memory model, global variables will always be allocated in external memory and never in HubRam.
So for all memory models (CMM,EMM,SMM,LMM,XMM SMALL, XMM LARGE) auto variables will always be allocated on the Stack portion of HubRam?
And in all memory models, except for XMM LARGE, variables designated as static within a function will be located in HubRam, but for XMM LARGE they will be located in external memory only?
I was wondering about that in the case of XMM LARGE. Originally I thought that all variables were allocated within external memory.
With external memory containing its own Code, Cnst, Init, and Data segments, if it also had its own separate Stack, then auto variables would be stored there too, and there would be no way to share variables between an XMM LARGE program and a Subsidiary program running in HubRam. Unless there was a special designator for such variables.
However, with your approach XMM LARGE Primary and CMM Secondary variables can be shared via the Stack within HubRam.
That means that I had better account for the size of the shared structure and allocate a sufficient stack size within spinc of the Secondary. In my GPS Program case, we're likely looking at up to 1K of memory.
But, I digress...
So if I do this:
void main(void)
{
char TempStr[80];
strcpy(TempStr,"This Is A Test\n");
}
In all memory models, TempStr[80] will be allocated on the Stack within HubRam?
But in XMM LARGE only, if I do this then the variable will be placed within external memory:
void main(void)
{
static char TempStr[80];
strcpy(TempStr,"This Is A Test\n");
}
OR This:
char TempStr[80];
void main(void)
{
strcpy(TempStr,"This Is A Test\n");
}
If what I'm assuming here is correct about memory management, then I will attempt to minimize the use of HubRam within an XMM LARGE program by making as many variables as possible either static within a function or global...
If I try CACHED_4K or CACHED_8K, the Primary will activate but the Secondary never will.
I didn't try them. But if 1K and 2K work but 4K and 8K don't, it will be because your secondary program is just too large to accommodate the larger cache.
Even when using COMPACT mode for the secondary program, that program takes around 12k of Hub RAM. Add an 8K cache and this is about 20k. Add in the static portion of the XMM SMALL primary program (over 10k!) - which would normally be in XMM RAM when you compile in LARGE mode - and you can see that there is very little Hub RAM left anything else - including the main program stack - in XMM XMALL mode.
The upshot is that you will probably need to use XMM LARGE mode.
There is no easy way to tell how much Hub RAM a program will need at run time - remember that it is not just the code, but also the stack and heap (if you use dynamic memory allocation). Any program that uses the stdio functions (sprintf() etc) will always take a lot - change your secondary program to not use sprintf and try it again.
In the XMM LARGE memory model, global variables will always be allocated in external memory and never in HubRam.
So for all memory models (CMM,EMM,SMM,LMM,XMM SMALL, XMM LARGE) auto variables will always be allocated on the Stack portion of HubRam?
Yes.
And in all memory models, except for XMM LARGE, variables designated as static within a function will be located in HubRam, but for XMM LARGE they will be located in external memory only?
Yes.
I was wondering about that in the case of XMM LARGE. Originally I thought that all variables were allocated within external memory.
No, local variables are always allocated on the stack, which is always in Hub RAM.
With external memory containing its own Code, Cnst, Init, and Data segments, if it also had its own separate Stack, then auto variables would be stored there too, and there would be no way to share variables between an XMM LARGE program and a Subsidiary program running in HubRam. Unless there was a special designator for such variables.
True. But having the stack in XMM RAM would make program execution glacial. The change I originally thought about making was to have a way to force the allocation of a global (or static) variable into Hub RAM, even in LARGE mode. But there is no provision for this in ANSI C, and I am not really inclined to make one up since it is really unnecessary.
However, with your approach XMM LARGE Primary and CMM Secondary variables can be shared via the Stack within HubRam.
That means that I had better account for the size of the shared structure and allocate a sufficient stack size within spinc of the Secondary. In my GPS Program case, we're likely looking at up to 1K of memory.
But, I digress...
So if I do this:
void main(void)
{
char TempStr[80];
strcpy(TempStr,"This Is A Test\n");
}
In all memory models, TempStr[80] will be allocated on the Stack within HubRam?
Yes.
But in XMM LARGE only, if I do this then the variable will be placed within external memory:
void main(void)
{
static char TempStr[80];
strcpy(TempStr,"This Is A Test\n");
}
OR This:
char TempStr[80];
void main(void)
{
strcpy(TempStr,"This Is A Test\n");
}
Yes.
If what I'm assuming here is correct about memory management, then I will attempt to minimize the use of HubRam within an XMM LARGE program by making as many variables as possible either static within a function or global...
Yes. But you will sacrifice some speed of access, so it is probably only worth doing for large structures or arrays.
That means that I had better account for the size of the shared structure and allocate a sufficient stack size within spinc of the Secondary. In my GPS Program case, we're likely looking at up to 1K of memory.
I just re-read this bit, and I think I should specifically clarify this point ...
If your shared structure is a local variable in the primary program, then you do not need to allocate any space for it in the secondary program. The memory used is allocated on the stack of the primary program, and only a pointer to it is passed to the secondary program. When your secondary program uses that pointer to access the shared variable, it is accessing the memory already allocated on the stack of the primary program.
I just re-read this bit, and I think I should specifically clarify this point ...
If your shared structure is a local variable in the primary program, then you do not need to allocate any space for it in the secondary program. The memory used is allocated on the stack of the primary program, and only a pointer to it is passed to the secondary program. When your secondary program uses that pointer to access the shared variable, it is accessing the memory already allocated on the stack of the primary program.
Thanks for the clarification. I set the stack size using spinc to 1300 bytes with no problems. I'll tweak the setting as I go along and see how it works out.
Good News: I was able to get my latest version of MenuTest running using XMM LARGE Primary and a CMM Secondary.
The Primary generates various Menu items. For each item it uses my CduPrint() function which fills a string with the desired, formatted output. This string is contained within the shared Structure. The Secondary Program grabs this string and sends it to the Console.
It's working great! I will layer more Menus into it later this week, and then start adding the GPS tasking to the Secondary.
This Multi-Memory capability is a really impressive feature you've added!
I look forward to pushing it to its limit
This has been a very productive day, but now it's time to call it quits for the night.
Comments
You are missing a line in your "primary.c" program. I have attached a copy with the necessary line added.
Here are the commands I used to compile your program (this was for my C3 platform - you will probably need to modify at least the -R parameter):
It is acceptable, but note that you can't just allocate a pointer to this type - that allocates no actual memory. You should allocate your shared variable in the main program as an actual type, then take its address and pass it to the subsidiaries that need it. You could instead allocate it in the subsidiary and pass it to the primary program, but to do so you will have to define a variable suitable for passing it in the primary program.
Another option would be to just identify the area of Hub RAM that is unused by your primary, and just use it manually, such as is done in my demo example 1.
Ah! That depends on many things - e.g. the size of the primary program, and the stack space it needs, and also the plugins you load, and the cache options you use. Try writing a very simple primary program that loads all the necessary plugins etc (i.e. is compiled with exactly the same options as the real primary program) but which just prints out the address of a variable local to the main stack frame. Then estimate your actual subsidiary program size and estimated stack usage and subtract it from this value.
Sadly, no. All subsidiary programs are compiled to run at a specific address.
Use the technique above to find out. Here is an example program you could try compiling:
OK, I think I'm getting the hang of this.
As mentioned previously for my GPS Project, the main() function in the Secondary Program will need to pass the address of the Shared Structure to various other functions in order to process, parse, store, etc the GPS data.
This Test Program incorporates the concept I plan on using to do that, unless you can think of a better way.
The first variable in the Shared Structure itself will contain the Memory Address of the Structure, and it will be loaded by the Primary Program prior to activating the Secondary Program.
Here's the Primary Program: And here's the Secondary Program: And here's the result:
So this approach seems to work.
For some reason I'm having trouble getting the Primary to execute in XMM LARGE (payload is giving me a timeout error after upload), so I'll focus on that next since XMM LARGE is what will be required.
One step at a time...
Your method is more complex than strictly necessary. In particular, you don't need the "Memory" field of your shared structure, since the address you put in there is simply the address of the shared structure itself, which the subsidiary program is given (as you can see when the program is executed).
I would also recommend defining your shared variable structure in a common header file that is then included by all the programs (primary and subsidiary). This will make program maintenance easier.
I am not sure what the XMM issue is. I run both XMM SMALL and XMM LARGE versions of the demo programs on my C3. You may want to check your program size actually fits in your XMM RAM?
Yes, that just dawned on me. The Memory variable isn't needed at all because of the casting that is done in the Secondary Program: Don't know why I went off on the Memory variable tangent, but after checking on dinner and coming back I looked at the code again and realized it's not needed.
Yes, for the actual GPS Program itself, and not this Test program, all Defines, Structures, Variables, etc., will be in separate files that can be #include by both the Primary and Secondary Programs. But for this Test example, I decided to have the Structure listed in each respective source file.
I'll have to take a look at the XMM LARGE issue later tonight after dinner. It's likely a setting I have wrong because it was working with a different program earlier.
Thanks for feedback!
It may also be worth checking that your XMM and loader utilities have been built correctly - especially if you use different cache sizes for different programs. Rebuild them using the "build_utilities" script if necessary.
I got the XMM Primary Program to run. It appears that I somehow had messed with some of the settings under the Build Options in Code::Blocks.
When experimenting with my TEST program mentioned above (which was running in CMM for both Primary and Secondary) I was using the -ltty and -C TTY compiler switches for both, and it was working fine as noted in the posting.
I switched over to a different program, MenuTest, which essentially has more junk to send to the screen than TEST. It runs the Primary in XMM, the Secondary in CMM, and uses the 4-Port serial library. But I can't get any response from the Secondary upon activation.
I've verified that the only Plugin that's loaded by both the Primary and Secondary programs is the 4-Port library. I've also compiled both with the -C NO_HMI option. The Primary program uses the -lc library while the Secondary uses -lci.
When I execute MenuTest , the Primary prints to Console using the s4_tx() function as expected, and shows that the Secondary is loaded at the correct address. But there's no response from Secondary. It should give me a "Secondary Activated" prompt but doesn't.
I'll need to take a closer look either tomorrow or Monday at the 4-Port issue to see what I'm missing.
I'm calling it a night for now.
The 2 most common issues I have found when messing about with Multi-Memory Model programs are:
1. Accidentally using an area of memory that is no longer free (especially if you are using manual memory allocation, and your program grows in size or you change your compiler options, such as including caching or changing your plugins etc)
2. Not allocating enough stack space for the subsidiary program (via the -s option to spinc).
It is worth checking both of these!
OK, while still working with the MenuTest program, I've configured both the XMM Primary and the CMM Secondary to compile with the -lc, -lm, and -lserial4 libraries. I needed -lc because I'm using the itoa() function in the Secondary (without it I previously compiled using -lci).
I've adjusted the XMM Primary to use the LARGE memory model, and varied it's cache from 8K down to 1K (using build_utilities each time to reconfigure it). No response from Secondary. I've adjusted XMM Primary to use the SMALL memory model, and again varied its cache from 1K to 8K. No response from Secondary.
I've adjusted the CMM Secondary stack space from 200 up to 1000. No response from Secondary.
I have to run some errands, but hopefully I'll be able to examine this further later on today when I get back...
I'm out most of today but if you don't solve it, email me your complete program (including build script). I'll compile it here and should be able to tell if everything is correct even if I can't actually run it.
I wasn't able to do much with the code when I got home yesterday, but I've had a chance to work on it this morning.
I'm still getting the same results: Primary activates fine as an XMM LARGE executable, but Secondary never activates.
Here's a screenshot:
I've attached copies of Primary.c, Secondary.c, Structs.h, and Secondary.inc.
The compiler switches for Primary.c are as follows:
I've tried attaching Primary.cbp file but the system won't let me do it.
The compiler switches for Secondary.c are contained within the file itself (commented out at the top).
Hopefully there are enough clues here to help nail down the problem.
Just a few more observations on this.
1. If I compile the Primary as XMM SMALL and leave the Secondary as CMM, I get the same results as shown above. The Primary activates, but no response from the Secondary.
2. Leaving the Primary as XMM SMALL, and the Secondary as CMM, but also including the EEPROM loader, the Primary code will execute as above, Secondary still doesn't, but for some reason the Prop appears to reboot/reset repeatedly with the above message displayed over and over again while scrolling the screen. Very strange.
3. Compiling the Primary as CMM, and leaving the Secondary as CMM, results in the Primary not activating at all. Just a blank screen. Odd, because the code did compile fine even though the resulting file size was a bit over 31K bytes. I don't understand why the Primary no longer works.
Maybe there's enough here to narrow down the problem. I'm hoping it's just a compiler setting causing the Secondary not to activate, and can easily be remedied by changing a compiler switch or two...
Those are my observations for today. I have some other tasks I must work on tomorrow, but hopefully I can revisit this again later in the day then.
I think I have found your problem - your primary program is allocating the SECONDARY_RESERVED_SPACE array within the CheckMemory() function. Any memory allocated within a function is released once the function exits. This was corrupting the memory being used by the secondary program
Instead, allocate this memory in the main() function, and just pass the address into the CheckMemory() function. Here is some code - amended as described - which now seems to work as expected:
There is another (minor) problem in Structs.h, which is that both yes and no were being defined as 1. Here is the amended code:
While investigating this problem, I did find a potential problem with the serial4 library which would affect a program (such as yours) which uses it from both the primary and the secondary programs - it is possible the output would end up corrupted. I will fix this in the "official" release.
Ross.
Many thanks for nailing down the problem.
Looks like I did some rather sloppy programming there. The problem with the #define in Structs.h is inexcusable.
I made the corrections, and it works fine now in XMM LARGE. Both the Primary and Secondary are active.
However, I recompiled the Primary to run under XMM SMALL, and neither the Primary nor Secondary are working. Just a blank screen.
I then recompiled the Primary to operate in CMM. Same problem as XMM SMALL: neither Primary nor Secondary are working, and just a blank screen.
I don't know if the cause in these two cases could be the 4-Port driver issue you mentioned or something else.
At this point it's not a major issue for me, just as long as XMM LARGE is working.
Question: Must the char SECONDARY_RESERVED_SPACE[SECONDARY_RUNTIME_SIZE]; only be defined within the main() of the Primary program?
Can it be defined as a Global Variable within the Primary program, or is it restricted to being a local variable within main() of the Primary? Just curious.
Anyway, great progress made on this today with your help.
We'll see what tomorrow brings (if I get the time to work on it some more).
Works for me (on a C3) compiled as either LARGE or SMALL. I suspect your compiler options may be incorrect. Here are the commands I used:
Of course, you will need to change C3 (to CUSTOM), and possibly the -R parameter.
The program will not work when primary.c is compiled as COMPACT. Even with optimization, it is too large.
Ross.
The array must exist in Hub RAM. If you make it a global, that would work ok for the CMM, EMM, SMM, LMM, XMM SMALL & (on the P2) NMM memory models. But when using the XMM LARGE memory model globals are allocated in XMM RAM, not Hub RAM. Only local variables (e.g. variables allocated on the stack) are allocated in Hub RAM.
So, the easiest way to make sure your code will work under ALL memory models is to make the array a local variable of the main function, since main only ever exits if your program terminates.
Note: The array could be allocated in a function if that function starts, uses and then terminates the secondary program before the function returns.
If I try CACHED_4K or CACHED_8K, the Primary will activate but the Secondary never will.
No luck getting Secondary to work using these last two cache sizes, even though I double checked the configuration each time I used build_utilities to change the cache settings...
Did CACHED_4K and CACHED_8K work for you? If so, then I still don't have something right within the compiler settings... OK, that makes perfect sense.
In the XMM LARGE memory model, global variables will always be allocated in external memory and never in HubRam.
So for all memory models (CMM,EMM,SMM,LMM,XMM SMALL, XMM LARGE) auto variables will always be allocated on the Stack portion of HubRam?
And in all memory models, except for XMM LARGE, variables designated as static within a function will be located in HubRam, but for XMM LARGE they will be located in external memory only?
I was wondering about that in the case of XMM LARGE. Originally I thought that all variables were allocated within external memory.
With external memory containing its own Code, Cnst, Init, and Data segments, if it also had its own separate Stack, then auto variables would be stored there too, and there would be no way to share variables between an XMM LARGE program and a Subsidiary program running in HubRam. Unless there was a special designator for such variables.
However, with your approach XMM LARGE Primary and CMM Secondary variables can be shared via the Stack within HubRam.
That means that I had better account for the size of the shared structure and allocate a sufficient stack size within spinc of the Secondary. In my GPS Program case, we're likely looking at up to 1K of memory.
But, I digress...
So if I do this: In all memory models, TempStr[80] will be allocated on the Stack within HubRam?
But in XMM LARGE only, if I do this then the variable will be placed within external memory:
If what I'm assuming here is correct about memory management, then I will attempt to minimize the use of HubRam within an XMM LARGE program by making as many variables as possible either static within a function or global...
I didn't try them. But if 1K and 2K work but 4K and 8K don't, it will be because your secondary program is just too large to accommodate the larger cache.
Even when using COMPACT mode for the secondary program, that program takes around 12k of Hub RAM. Add an 8K cache and this is about 20k. Add in the static portion of the XMM SMALL primary program (over 10k!) - which would normally be in XMM RAM when you compile in LARGE mode - and you can see that there is very little Hub RAM left anything else - including the main program stack - in XMM XMALL mode.
The upshot is that you will probably need to use XMM LARGE mode.
There is no easy way to tell how much Hub RAM a program will need at run time - remember that it is not just the code, but also the stack and heap (if you use dynamic memory allocation). Any program that uses the stdio functions (sprintf() etc) will always take a lot - change your secondary program to not use sprintf and try it again.
Yes.
Yes.
No, local variables are always allocated on the stack, which is always in Hub RAM.
True. But having the stack in XMM RAM would make program execution glacial. The change I originally thought about making was to have a way to force the allocation of a global (or static) variable into Hub RAM, even in LARGE mode. But there is no provision for this in ANSI C, and I am not really inclined to make one up since it is really unnecessary.
Yes.
Yes.
Yes. But you will sacrifice some speed of access, so it is probably only worth doing for large structures or arrays.
I just re-read this bit, and I think I should specifically clarify this point ...
If your shared structure is a local variable in the primary program, then you do not need to allocate any space for it in the secondary program. The memory used is allocated on the stack of the primary program, and only a pointer to it is passed to the secondary program. When your secondary program uses that pointer to access the shared variable, it is accessing the memory already allocated on the stack of the primary program.
Thanks for the clarification. I set the stack size using spinc to 1300 bytes with no problems. I'll tweak the setting as I go along and see how it works out.
Good News: I was able to get my latest version of MenuTest running using XMM LARGE Primary and a CMM Secondary.
The Primary generates various Menu items. For each item it uses my CduPrint() function which fills a string with the desired, formatted output. This string is contained within the shared Structure. The Secondary Program grabs this string and sends it to the Console.
It's working great! I will layer more Menus into it later this week, and then start adding the GPS tasking to the Secondary.
This Multi-Memory capability is a really impressive feature you've added!
I look forward to pushing it to its limit
This has been a very productive day, but now it's time to call it quits for the night.