How much memory is there really?

Hi

I just started trying some algorithms for our arm, and ran into some memory problems. I want a big lookup array.

The linker barfed when I tried to declare an 800 BYTE array. So I started poking around in the MAP file.

It seemed that there was a lot of code space to spare (I was only utilizing 12%), so I tried declaring the array as “rom” to see if I could use code space for my lookup table (since it will never change).

Woo-Hoo. I can seem to define SCADS of space there: eg:

rom BYTE Omega[20000][2] ;

The map file verifies that this allocation is OK and ending up in the code space… so my question is:

Does this memory really exist in the RC?
In other words will the linker protect me from over declaring space?

I want to be sure that I’m not just being fooled by the compiler/linker.

Any experiences with large const data in the code memory?

The Linker won’t necessarily directly complain about memory over “drafts,” so you have to watch it. What you usually get when you run out of space is a link error about not being able to load one of the libraries at the end. As reported by MPLab and the linker you will actual have less memory available to you due to other system overhead. As I recall the MAP file will report ~97% utilization when it is actually full.

We have three types of memory on the RC available to use.
See http://www.ifirobotics.com/rc.shtml#Specifications for the PIC18F8722

128,000 bytes program space
3936 bytes variable space (less the PROTECTED, etc. areas listed in 18f8722.lkr)
1024 bytes EEPROM

The default is to put variables into the 3936 byte variable space where your code can modify the values while it’s running. There is a further limitation on the variable space due to how the linker has the space blocked out.
You are only able to declare 256 bytes of variable space in any one MPLAB project file.

“rom” puts your stuff into the 128K program space, but you cannot change values placed here as you noted. They are fixed and read-only when your code runs. As you said it is probably the best place for our fixed lookup tables.

The EEPROM requires special runtime reads/writes to use, but it is maintained even when your robot power is off.

In MPLAB you can also check on your memory utilization via: View -> Memory Usage Gauge
Note though that the Program memory is stated in two-byte words, not bytes for some reason.

P.S. We’ve kept our autonomous modes in large arrays in program space since these PIC RCs came out in 2004.

Due to the linker methodology, each individual file can only declare up to 256 bytes of dynamic memory, things like lookup tables are constant, as you noted and should be done with rom qualifiers so they are stored as constants in program memory and not in the variable space. It’s just a limitation imposed by the way things are linked together, it’s not often a problem unless you use a lot of variables or large arrays. What you’re doing is the proper solution to the problem, by making the table a constant.

“You are only able to declare 256 bytes of variable space in any one MPLAB project file.”

That is the default. The default is based upon the h/w architecture having 256 byte ram banks. PIC18F instructions have 8bit ram address offset hence the default of 256 byte banks. However, you can override by changing the setup in the lkr file.

This shouldn’t be done lightly because it introduces more overhead in accessing data - but you essentially tell the linker to merge two adjacent ram banks and treat them in software as a single resource. Refer to the C18 user guide.

Changed 18f8722.lkr:

DATABANK   NAME=gpr2       START=0x200          END=0x2FF
DATABANK   NAME=grp3       START=0x300          END=0x3FF
DATABANK   NAME=gpr4       START=0x400          END=0x4FF

To:

DATABANK   NAME=gpr2       START=0x200          END=0x3FF
DATABANK   NAME=gpr4       START=0x400          END=0x4FF

created:
static unsigned char bigarray[500];

Build project, map file shows:

                 bigarray   0x000202       data     static user_routines.c
                           i   0x0003f6       data     static user_routines.c

x3f6-x202 = 500 byte array. Compiles/links/runs ok. I read about this in the C18 user guide somewhere. Currently only the “3.2.4 Managing the Software Stack” section jumps out at me - but it shows the steps for doing the same thing to create larger stack areas. Ram is a limited resource… use it wisely.

Bud

What do you use to see how much space is being used?

You can look at FrcCode.map and a ways down it will tell you how much program space you are using. It looks like:


                              Program Memory Usage
                               Start         End
                           ---------   ---------
                            0x000800    0x0078ba
            28859 out of 132120 program addresses used, program memory utilization is 21%

Thanks guys… It looks like I’ll be OK.

Now I just need to write the program to create the file that declares all the values :0

BTW, Nice PID article Matt. I’l have to hand it out at the next S/W meeting.

Phil.

Thanks for sharing the tip on using “rom” space for constant lookup tables!

Thanks for the information on the amount of memory available in each type of space! It’s nice to have the quick summary as well as the pointer to the description in the manual.

An additional approach I might mention is one that we used last year in order to be able to have declarations of more than 256 bytes of variable space within a single MPLAB file – we made use of the pragma directive to explicitly switch memory sections.


#pragma idata mem_section_1
  // put up to 256 bytes of declarations here

#pragma idata mem_section_2
  // put up to 256 more bytes of declarations here

#pragma idata mem_section_3
  // put up to 256 more bytes of declarations here

#pragma idata
  // actual code goes down here

I’m a proponent of the pragma approach Ken described rather than modifying the linker directives. The pragma’s are obvious and self-commenting in the code while the linker script is hidden and doesn’t leave a discernable audit trail. Misuse of the linker script is also harder to ferret out from a distance.

Linker script modification is obviously not an issue for the current knowledgable programmer, but more so for clarity when future programmers borrow code and they can’t figure out why it doesn’t work.

Correct, specifying essentially hand placement of data by specifying data section via pragma is the better choice except when you want to build a single array that is larger than 256 bytes. Then you really don’t have much choice except the linker modification.

Bud

You may also be able to configure the compiler to use the large memory model instead of the small one. (At least with the full compiler, I don’t know about the cbot version on the CD.)

Whoa, Whoa, Whoa, back the truck up a wee bit. So I can’t ROM declare variables? How many memory sections can I use #pragma on (1,2,3,4?) I don’t understand where to put it nor what “actual code goes here” means. Can anyone kindly expain this to me?

And how do I calculate variable usage? For program usage, do I just hex convert what was posted above and subtract?

You can declare constants, not variables, in rom. You can use EEPROM and program (flash memory) for constants. You can write EEPROM and reflash code memory (blocks) - but both are slow operations compared to regular ram.

I’ve seen code/application somewhere - Kevin’s code i believe - that had a write queue to eeprom for doing background writes. It eliminates having to wait to write the data but doesn’t make writes any faster - that is, eeprom isn’t a good place to have general read/write variables. It would be an ok place to store or build data for autonomous routines at the beginning of a match or for storing/updating variables that change infrequently (on the order of seconds between writes).

There is a 4k byte ram space, but a chunk of this is the memory mapped hardware control registers that serve specific functions. Another chunk is stack temporary variable space, another chunk is .tmpdata section used to hold intermediate computations for compiler generated code, another chunk is MATH_DATA section which holds temporaries for the math library, another chunk is used up if you use any of the interrupt driven uart routines for printf, etc.

I’ve never actually measured how much total ram space is really left for general user programmer, but maybe 2k. You can make this go a long way by using overlay and other means or you can just code and not worry about it until its gone. The only time I’ve had a problem is when I tried to build a number of arrays for debugging to hold/track data.

I strongly recommend reading the C18 user guide and PIC18F manual sections on memory models and address space to get a fuller understanding of how it works and the limitations of the different methods. If after reading these fairly short sections of the reference material you still have questions, feel freel to post them or private message me.

Like everything in life, there are tradeoffs and consequences of using the different types of memory available.

Bud

Actually, reading from EEPROM is very fast. It’s the writing that’s slow–and even then it’s not that slow.