PIC18 heap issue

qft0 wrote on Saturday, December 20, 2008:

Richard,

From the PIC MPLAB port for 18F4520 thread…

Re-state in as few words as possible? What are you trying to say? :slight_smile:

I think I may be on my own, but here’s my issue. John Franklin’s attempt to solve this problem is in the FreeRTOS doc’s along with a link to his solution. I don’t see how his solution can work, and suspect that it worked for him by fluke. This, I hope, means I don’t understand it, not that it won’t work.

The issue is specific not to PIC18 peripherals but to the PIC18’s internal RAM. Memory is organized in 256 byte banks. The manual for the linker (MPLINK, not the C18 compiler) specifically says not to combine banks as the compiler won’t generate code that can navigate across a bank boundary. For instance a 32-bit variable might live at 0x1fe:0x201.

Here’s what I’m hoping for: If you must combine banks into a large section, you can protect the space (use PROTECTED keyword in linker command file) and allocate it to the heap with the right #pragma directives and section assignments wrapped around said heap declaration. This will allow pointers and arrays to grow past 256 bytes with proper addressing.

Where I see it going pear-shaped is that the heap is not a single array, but a space into which variables are allocated. If this is the case, I can’t see how this will ever work, other than by providence. Is there something I don’t get about how space is allocated to variables on the heap? I’m really struggling to understand this.

OK, so I failed to keep it short, it’s not that easy of a problem to explain.

Lawrence

mikej42 wrote on Tuesday, December 23, 2008:

Hello Lawrence

As I understand the Microchip C18 compiler/linker, the compiler assumes that the variables in a single file live in the same page of RAM, and doesn’t use the bank select register for these accesses. Similarly an array or structure must not span more than one page (256 bytes) (no use of bank select).

However when variables are accessed via pointers, they can exceed the 256 byte limit. To allow this, the linker must be told of some large area that can be used.

To avoid the linker putting ‘ordinary’ variables into these larger areas, they must be ‘PROTECTED’ ie only specified variables can be put into this area.

The heap is always doled out as pointers, so should always be safe.

The stack (by default) also is assumed to be within one page. The compiler will complain is one function places too much stuff on the stack, but not (of course) if further calls break the size limit. There is a compile time option for a larger stack, under Build Option/MPLAB C18, category Memory Model called Stack Model is Multibank, which is selected for the demo projects.

So everything should be safe.

I am using freeRTOS on the atmel SAM7S, and have not used the Microchip version although I have used the PIC18s quite extensively.

Best regards

Mike Newsome
Hitek Power Ltd England

mikej42 wrote on Tuesday, December 23, 2008:

I have had another look at the rtos demos (in v5.1.0) and notice that the region BIG_BLOCK is not PROTECTED, which means the compiler can allocate any variables here, and not necessarily generate bank select statements to access them.

So I think that a region of RAM should be allocated (say called ‘BIG_BLOCK’) to use for that purpose, which must be PROTECTED:
DATABANK   NAME=BIG_BLOCK       START=0x80          END=0x4FF          PROTECTED

This needs to be connected to a section:
SECTION    NAME=HEAP       RAM=BIG_BLOCK

Some RAM is needed to be declared outside this protected area for users global and static allocations:
DATABANK   NAME=gp0        START=0x500           END=0x5ff

The definition of the heap (in heapx.c) needs to identify to the linker that that section should be used:
#pragma udata HEAP
static struct xRTOS_HEAP
{
    unsigned portLONG ulDummy;
    unsigned portCHAR ucHeap[ configTOTAL_HEAP_SIZE ];
} xHeap;
#pragma udata

Note that the heap is 4bytes larger than configTOTAL_HEAP_SIZE because of the ulDummy used for byte alignement purposes, and the STACK is declared in here also.

I think the demos ought to be adjusted with these changes in mind. It seems to be accidental that they work, and may not do when a user starts adding his/her code. It is difficult to find the problem of incorrect bank access.

As a final question, what is the STACK region that is declared in the linker used for? As I understand it, this is only used by the start up code prior to running tasks, and could possibly be used as part of the system RAM after this time.

Best regards

Mike Newsome
Hitek Power Ltd England

rtel wrote on Tuesday, December 23, 2008:

Excellent information!  Could you send your new linker script to r (dot) barry [at] freertos.org so I can ensure I get the changes correct.  I can then check it into SVN to ensure it is included in the next release.

Thanks for taking the time to make this contribution to the project.

Regards.

qft0 wrote on Wednesday, December 24, 2008:

Yes, you’re right, the BIG_BLOCK region is not protected.  IIRC, if you use the PROTECTED keyword, you have to explicitly assign variables to the space, as the linker won’t touch it.  You can cheat by declaring the BIG_BLOCK region to be just big enough to hold the heap.  The linker will figure out that the only place the heap can go is into that space, and give you the same result.

Looking at the example in the compiler manual, I get the impression that the pagesel directives are related to how the array is accessed, not how the array is declared.  I admit I haven’t tried compiling with or without the #pragma and/or PROTECTED keywords.

The trouble is that the correct (only?) way of accessing the multi-bank array is via a pointer.  If variables are allocated from the heap, presumably you won’t be accessing them exclusively via pointers.  If I’m wrong on this, it means I’m ignorant (wouldn’t be the first time) and don’t understand how the heap is really used.  Reading the manual for FreeRTOS, it looks like it’s the Kernel that uses the heap to allocate space for tasks, queues, and semaphores.  Is the allocated space strictly addressed via pointers?  If so, the pagesel directives should be included and all should be well.  I’m still grappling with this.

The STACK region is supposed to be for the software stack (not the hardware call-stack).  Local variables and function parameters are stored to it (anything with storage class auto).  See section 11.7 and the glossary of the linker manual.  You have to explicitly tell the linker where to put the stack.  The PROTECTED keyword is not required for this, just the linker command file STACK declaration.

If you want a stack bigger than a bank, there’s information on this in the compiler manual. When specifying STACK in the linker script, first combine the number of sections you want, much like the BIG_BLOCK declaration for the heap.  In this case, you MUST specify the PROTECTED keyword, unlike with a smaller stack.  Specify STACK as before, assigning it to the bigger space just created.  When compiling (not linking), specify -ls on the command line.  This causes pagesel directives to be included when addressing the stack.

So, you can’t use the stack space for anything else, your program’s using it.

qft0 wrote on Wednesday, December 31, 2008:

On the issue of the stack, my confusion continues.  The stack is defined in the linker command file as being less than 1 section:

DATABANK   NAME=gpr15      START=0xF00          END=0xF7F
STACK SIZE=0x80 RAM=gpr15

Given the stack is less than 256 bytes, the -Ls option is not required when compiling.

I have tried compiling with and without this option, and…the 1st demo won’t run without the -Ls option set.  wtf?!?  In the scheduler, the program appears to get stuck here:

for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
    /* There is nothing to do here, we are just iterating to the
    wanted insertion position. */
}

Can anybody explain why I need the -Ls option if I never use the compiler-generated software stack, once the scheduler has started?

mikej42 wrote on Monday, January 05, 2009:

Hi LCJ

When the scheduler is started, all tasks use individual stacks located in the heap, with a size allocated when each task is started, and the system stack is no longer used. Since we don’t know where the actual stack will be, nor how large we must use the -ls flag to ensure the compiler produces proper code to use 16 bit stack accesses.

This is why I wondered if the system stack space could be used by the heap manager.

Possibly if no task stack crosses a page boundary, the flag could be dropped, but enforcing this would be difficult.

mikej42 wrote on Monday, January 05, 2009:

Hello lcj

Variables allocated from the head can only be accessed via pointers, as absolute addresses cannot be defined at run time. So there should be no problem with accesses.

According to the manual, the -ls flag causes 16 bit arithmetic to be done for push/pop, rather than sungle byte when the stack is within a page.

Finally, the stack is accessed via the frame pointer (FSR2) which doesn’t use the banksel register, as it a 16 bit wide register.

Hope all this helps - it has certainly helped me trying to explain it all!

Best regards, and wishes for the new year

Mike

qft0 wrote on Tuesday, January 06, 2009:

Hi Mike,

Thank-you, great information!  I got out your point about the stack not being used a couple of days after your original post.  I’m a little slow on the uptake. :slight_smile:

The only use I can see is that if the scheduler can’t allocate enough space for everything, it will return to main().  If you want to do something at this point, like display a message on an LCD or send a message off through one of the processor ports it might be nice to still have a stack, since I think any variables declared within main() will still be sitting there.

Running the RTOS and getting “Error, return from scheduler” on the LCD is annoying but at least I’m not left wondering what went wrong.

Just to be clear though, which stack is accessed via FSR2, the task stack allocated from heap space or the stack as defined by the compiler?  I thought push/pop instructions used bank select registers, hence the compiler explanation of the -ls option.  This implies push/pop instructions are used for each task’s stack.  Aren’t bank select registers still required in this case?

Cheers,
Lawrence