1
\$\begingroup\$

I have effectively three sets of variables to put in two locations. Sets 1 and 2 are never used together, so they can occupy the same space. Set 3 needs to be unique.

I know I can use a union of structs to get what I want, but that's a lot of data to force into the same area, and it makes the names unnecessarily complex in my opinion:

union
{
    struct
    {
        ...
    } set_1;
    struct
    {
        ...
    } set_2;
} shared_variables;

I can also assign a specific address to each variable, but that doesn't allow any freedom for the linker to optimize them:

unsigned char foo_1 @ 0x1A0;
unsigned int  bar_1 @ 0x1A1;

unsigned long foo_2 @ 0x1A0;    //same address as foo_1

I can assign a section name to each variable and tell the linker what to do with the sections, but I still think it's too restrictive:

unsigned char __section("set_1") foo_1;    //set_1 is 1 byte
unsigned int  __section("set_1") bar_1;    //set_1 is now 3 bytes

unsigned long __section("set_2") foo_2;    //set_2 is 4 bytes

//linker: -L-Ashared_class=<address_range> -L-Pset_1=shared_class -L-Pset_2=shared_class
//actually concatenates them somewhere in the specified range (total 7 bytes), not sure how to overlay them (total 4 bytes)

Is there a way to tell it, "I don't care if this set of variables overlaps that set of variables, but this other set of variables must be unique," and just leave it at that?

Edit in response to comments:

  • Set 1 + Set 2 + Set 3 represent all the variables that are used in the entire project.
  • The code that uses them is currently spread across 4 source modules.
  • Like most if not all chips in this range made by Microchip, the memory is fragmented in hardware into 80-byte chunks separated by hardware configuration registers. There is another addressing mode that requires a pointer to access all the fragments as if they were contiguous, but that would thrash the pointer registers a lot. (and there seems to be a bug in the debugger that corrupts data that's used that way in certain cases; normal running is okay though)
\$\endgroup\$
  • 4
    \$\begingroup\$ I find unions a perfectly fine solutions for this job. \$\endgroup\$ – Dzarda Sep 24 '14 at 19:40
  • \$\begingroup\$ Normally yes, but the full project has a lot more data than will fit in one bank of RAM, and the two sets that can overlap have to do with completely separate activities that are coded in different source files. So a union would probably confuse things. \$\endgroup\$ – AaronD Sep 24 '14 at 19:51
  • \$\begingroup\$ If they are "never used together" can't you simply let scope handle this for you? Writing C code with variables at specific addresses defeats the point of writing in a high level, portable language! \$\endgroup\$ – David Sep 24 '14 at 22:12
  • \$\begingroup\$ @David: I don't think XC8 is smart enough to figure that out. The manager code uses a variable to determine whether to run a particular module or not. If so, it calls the module's function(s); if not, it doesn't. From the compiler's point of view, it looks like they could all run concurrently, which is not true. \$\endgroup\$ – AaronD Sep 24 '14 at 22:19
  • \$\begingroup\$ If the function(s) are not nested (do not call each other) and aren't called from interrupt routines then I'd have thought XC8 should figure it out for local variables. Do you absolutely need global variables (where of course the compiler will struggle)? \$\endgroup\$ – David Sep 24 '14 at 22:40
1
\$\begingroup\$

I find unions perfectly fine for "sharing" variables. Let me expand on this with code:

typedef struct
{
  union
  {
     struct
     {
     } set_1;
     struct
     {
     } set_2;
  } set_union;
  struct
  {
  } set_3;
} data_set;

... Of course, this could be made much more readable using typedefs on set_1 and set_2.

\$\endgroup\$
  • \$\begingroup\$ For relatively small data sets, that works perfectly. I actually do that already to translate from a generic bit bucket to a more organized structure, for example. But at the level that this question refers to, your outer struct would actually cover the entire project. Most of the time, the code will work a certain way, but sometimes a major part of it will be disabled and a different part enabled for a while. Both of the alternate activities will assume a fresh startup when enabled, and will re-initialize themselves. \$\endgroup\$ – AaronD Sep 24 '14 at 20:02
  • \$\begingroup\$ @AaronD What do you mean by cover the entire project ? The binary size will be exactly how you wanted. \$\endgroup\$ – Dzarda Sep 24 '14 at 20:04
  • \$\begingroup\$ There would be no variables outside of the outer struct. Part of the code manages some shared resources and determines what other parts can run; it uses set_3. The other two parts are mutually exclusive and provide more specific functionality; they use set_1 and set_2 respectively. There is no other code. \$\endgroup\$ – AaronD Sep 24 '14 at 20:11
1
\$\begingroup\$

It is straightforward to solve the "makes the names unnecessarily complex" problem.

Extending your example:

union
{
    struct
    {
        int fubar;
        ...
    } set_1;
    struct
    {
        int snafu;
        ...
    } set_2;
} shared_variables;

A solution is use some macros:

#define a (shared_variables.set_1)
#define b (shared_variables.set_2)

a.fubar = ...;

b.snafu += ...;

Part of the advantage of this solution is code is still ordinary C, and doesn't depend on compiler specific features.

There is a small possibility that the compiler will generate better code when it is doing all of the allocation of variables, rather than pushing some of the information into the linker. However, I don't know enough about xc8 to say that is the case.

Edit:
It seems okay to direct variables to specific segments. That will only effect the two sets which are mutually exclusive, and not all variables.

The xc8 compiler user manual also describes another mechanism for placing objects in "5.14.4.8 THE #PRAGMA PSECT DIRECTIVE", which may be preferable, AFAICT a single #pragma would place all of the subsequent variables.

This seems much simpler, and less error prone than __section("...") on every variable, compared to a single #pragma in front of each list of variables.

There is a specific warning about the #pragma, which probably does not apply to mutually exclusive variables:

This pragma should not be used for any of the data psects (data or idata) that hold initialized variables.

However, I would use a union + two structs unless there are more constraints.

Edit2:
When memory allocation is very tight, and it is essential to pack variables into fixed size segments, I might write a program to generate the C code of the declaration. I'd use a language with good text processing to make it easier.

\$\endgroup\$
  • \$\begingroup\$ Yes, I've seen that too, and it does affect the entire file. I have to use it to make some other parts of this project work. I like the __section() specifier much better, when it works. Either way, it still seems that the linker will refuse to overlay two psects unless it's forced to by giving them the same specific address, which is only better than a direct address in its abstraction from the source code. \$\endgroup\$ – AaronD Sep 24 '14 at 23:11
  • \$\begingroup\$ @AaronD - A. I don't understand why "the linker will refuse to overlay two psects unless it's forced to" would be unexpected. IMHO that is the only obvious behaviour for a linker. B. Defining the same start address for two 'psects' seems to be the right level of information, however a union + two structs seem better than futzing with psects and linker scripts. Forcing a developer to read linker scripts to understand a program seems to be a bad idea, and using a lot of repetitive declarations seems poor practice. C. "I like the __section() specifier much better". Okay. So what? \$\endgroup\$ – gbulmer Sep 24 '14 at 23:26
  • \$\begingroup\$ The __section() specifier allows similar things in the same file to go in different places and doesn't care where they would have gone. The #pragma must be the only one in the file because it really is global to the file and not "until further notice", and its function is really to rename a preexisting section that the compiler came up with. So you have to know where stuff would have gone in order to use it, which is not obvious unless you're very experienced with this compiler. If there was an option that would work until further notice, that would be better still. \$\endgroup\$ – AaronD Sep 24 '14 at 23:45
  • \$\begingroup\$ I'm not saying that the behavior is unexpected or that I don't like it in general. In fact, I do like that it does that by default, just not in this specific situation. \$\endgroup\$ – AaronD Sep 24 '14 at 23:48
  • \$\begingroup\$ As for reading linker scripts, I'm afraid they're going to be necessary for other reasons. So I'm trying to keep them as generic as possible so they don't have to change to make a minor revision work. \$\endgroup\$ – AaronD Sep 24 '14 at 23:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.