![]() |
SHDesigns: Embedded Systems Design, Consulting and Developer Resources | Page hits: ![]() |
There are two methods, depending on the board.
This is simple as all memory is battery-backed. The issue is all global variables (BSS) are zeroed on startup. Rather than mess with startup and possibly break library code, it is better to define a special segment.
Start by defining a battery-backed segment:
#pragma SEG(BSS,BB_BSS) ....battery-backed variables.... #pragma SEG(BSS) // return back to regular variables
This defines a seg called BB_BSS. Variables defined between the two pragmas will go in BB_BSS. These variables must not have an initializer, or they will go in the IDATA segment.
Create a new segment call BB_BSS in the linker settings. Move it in between the BSS and STACK segments:
Segment should precede STACK and be after BSS. Excluded may be needed (will not add it to the .bin file.) Do not put it before BSS, or it will be mapped to Flash by cstart.
On these boards xmem is battery-backed and not zeroed. So data allocated by xalloc() will be preserved.
Example:
#pragma SEG(BSS,BB_BSS) int current_state; struct error_log ErrorLog[100]; unsigned ErrorIndex; unsigned long mem_flag; #pragma SEG(BSS) char buff[100];
For the above, current_state, ErrorLog, ErrorIndex and mem_flag will be in BBRAM. However, they will initially be random data. I normally use checks on the data to make sure it is valid. Here is a sample:
#define MEM_FLAG 0xFACE55AAl if (mem_flag!=MEM_FLAG) // variable is not set, mem must be trashed { ErrorIndex=0; memset(ErrorLog,0,sizeof(ErrorLog)); current_state=0; mem_flag=MEM_FLAG; // future runs will see this is valid. }
I also add checks for pointers. I.e:
if (rd_ptr<bb_buff || (rd_ptr>(bb_buff+BUFF_SIZE)) rd_ptr=bb_buff;
The above checks that the pointer is inside the bb_buff array.
For data that may be in an unknown state after a reboot, I add CRC on the data. If the CRC fails, then I assume the data is bad and reinitialize.
Note: include files will also need the seg definitions for other modules to access the variables::
//somefile.h #pragma SEG(BSS,BB_BSS) extern int current_state; extern error_log ErrorLog[]; #pragma SEG(BSS)
Without the pragmas, external files will use the wrong segment. The compiler may not warn of this.
This gets more complicated. You will need to map memory to BBRAM. It is not in the 16-bit memory map. First, lets display the memory map:
Range | Mem type | Battery backed? |
00000-7FFFF | 512k Fast RAM | No |
80000-BFFFF | 256k Slow RAM | Yes |
C0000-FFFFF | 256k Slow RAM | Yes * |
* on 256k boards (RCM32000) this is not usable
Some versions of CSTART.asm had last 256k mapped to Flash.
Now, globals and the stack get mapped into BSS, STACK and IDATA. The end address for STACK sets where in memory this is located.
The slow RAM runs with 1 wait-state (3-clocks) Fast RAM runs 0 wait-state (2-clocks) so there is a 33% penalty for using BBRAM. You want to keep as much in fast RAM as possible for speed.
Option 1: map xmem to BBRAM:
This option is the simplest, set the end address for STACK to 7FFFF. This will leave the rest for xmem. All xmem allocated by xalloc() will be BBRAM but will have 1 added wait state. This will effect performance some, but not significantly. Since all BBRAM variables would be allocated with xalloc() and are far, there is a performance hit with slower RAM and more code to access.
Option 2: Map near data to BBRAM:
Set the end of STACK to 0x8BFFF. That will put, stack, globals and xmem in BBRAM. This will make all globals battery backed. You will then have to define a BB_BSS segment as described in the top section.
Since STACK now in slow RAM, it will effect speed. It does remove the added code to access far variables by mapping a near segment. It is a simple solution to implement.
Option 3: Map a BB_RAM segment to BBRAM:
This is complicated but has the highest performance and most flexibility. I'll review the data segments first:
BSS - Global variables BSSZ - Zeroed variables (seems to not be used.) IDATA - initialized variables, i.e int xyz=3; STACK - stack segment
These are basically one continuous "near" data area by default in Softools. The Rabbit will access this via 16-bit pointers. By default these are mapped by the STACKSEG, DATASEG and SEGSIZE registers:
Seg | Value | Mapping |
SEGSIZE | 0xSG | 0x0000-(0xG000-1= Root Code 0xG000-(0xS000-1) = Data seg 0xS000-0xDFFF= Stack seg |
STACKSEG | 0xNN | Physical=Addr+STACKSEG<<12 |
DATASEG | 0xNN | Physical=Addr+DATASEG<<12 |
SEGSIZE sets the boundaries between the root code, DATASEG and STACKSEG segments. Normally, Softools does not use the DATASEG. We can use this to map our own DATASEG. This is best described given a map file:
Logical Logical Physical Physical Size Start End Start End 00005400 00009205 00037400 0003B205 003E06 BSS DATA 00009345 0000CBFF 0003B345 0003EBFF 0038BC *IDATA DATA 0000CC00 0000DFFF 0003EC00 0003FFFF 001400 STACK STACK
BSS starts at logical address 0x5400 and physical address of 0x37400. Anything below BSS is root code or constants, all near addreses above this need to be mapped to RAM. Cstart.asm will use STACKSEG for this. So:
physical = STACKSEG <<12 + laddr; (laddr is logical address.)
So the compiler will address variables at a logical (near) address starting at 0x5400. It needs to be mapped to a physical address of 0x37400. So solving for DATASEG:
STACKSEG = (physical-laddr) >>12;
STACKSEG = (0x37400-0x5400)>>12 = 0x32.
So cstart.asm will set STACKSEG to 0x32. but we a only have half of the segment definition. We will need to describe what gets put in the STACKSEG. The SEGSIZE register sets the area of near memory mapped. SEGSIZE can only map on 4k boundaries. This is a limitation of the Rabbit CPU. Near (logical) data starts at 0x5400. We will map starting at 0x5000. So we will set SEGSIZE to 0x50. The '5' says STACKSEG starts at 0x5000 and the '0' says DATASEG starts at 0 (DATASEG==0) Logical addresses below 0x5000 use DATASEG and are mapped with logical==physical.
Ok, we have STACKSEG and DATASEG, but DATASEG is basically not mapped. If we set the start boundary for DATASEG to our own value, we can map our own segment. We will want to achieve something like the following:
Logical | Segment | Compiler segments | |
0000-4FFF | ROOT code/Const | Logical=Physical | CODE, CONST |
5000-7FFF | DATASEG | Mapped to BBRAM | BB_BSS |
8000-DFFF | STACKSEG | Mapped to Fast RAM and what the compiler expects | BSS, IDATA, STACK |
E000-FFFF | XPC | Used for FAR code | FARCODE, FARCONST |
On the above, SEGSIZE would be 0x85. '8' says anything above 0x8000 is STACKSEG and '5' says anything above 0x5000 but below 0x8000 is DATASEG.
Again we are stuck with 4k (0x1000) boundaries. We can use the linker to help us with this. Cstart will configure the STACKSEG and upper
First we will create a BB_BSS segment. Variables can be placed there with the #pragma SEG(BSS,BB_BSS) as described in the first section for systems with no fast RAM. In this case we will place BB_BSS before BSS. To get our mapping we will need to add some restrictions. Set both BB_BSS and BSS to have 4k alignment (1000). This does two things, it makes them start of 4k boundaries, and also will cause errors in the linker if this alignment can not be achieved.
As said before, cstart will set up STACKSEG and SEGSIZE for the BSS, IDATA and STACK segments. We will need to map our BB_BSS. This needs to use ASM code to use the seg variables. Sample code is as follows:
#pragma offset_labels on void map_bb_ram(char far * bb_addr) { #asm ld b,sgst BB_BSS>>12 ioi ld a,(SEGSIZE) or b ; add our BB_SEG start address ioi ld (SEGSIZE),a ld hl,(ix+.bb_addr) ; get lower 16 bits ld a,(ix+.bb_addr+2) ; get uypper 4 bits ld de,0x0fff add hl,de ; round up jr nc,..same_64k inc a ..same_64k: add hl,hl ; shift upper 4 buts of HL into rla add hl,hl rla add hl,hl rla add hl,hl rla ; A now has upper 8-bits of physical address sub sgst BB_BSS>>12 ; subtract locgical address 4k ioi ld (DATASEG),a }
The above function will map the BB_BSS segment to a physical address. Where do we get this address? We can just set a predefined address in the 0x80000-0xFFFFF range (512k bbram) or 0x80000-0xbFFFF (256k bbram). But we don't want to interfere with xalloc() or where the compiler mapped near data to RAM. A simple solution is to create another xalloc() that gets bbram.
This is be done by grabbing the top of the BBRAM for our variables:
// these are in cstart extern unsigned long _xmemSize,_xmemEnd,_xmemHeap; char far * bb_xalloc(unsigned long bb_size) { if ((_xmemEnd-0x80000l)<bb_size) // bbram starts at 0x80000 return (char far *)NULL; if ((_xmemEnd-_xmemHeap)<bb_size) // no free xmem at all! return (char far *)NULL; _xmemSize-=bb_size; // tell xalloc() there is bb_size bytes used _xmemEnd-=bb_size; return (char far *)_xmemEnd; // where our BB_BSS data resides }
We can define a function to find available bbram:
unsigned long bb_xavail(void) { if (_xmemEnd<0x80000l) // bbram starts at 0x80000 return 0l; return (_xmemEnd-0x80000l); }
This will steal the top of mem away from the xalloc() pool. Define a function to get the size of the BB_RAM seg rounded up to 4k:
unsigned get_bb_size(void) { unsigned bb_size; #asm ld hl,sgsz BB_BSS #endasm bb_size=_HL; //asm left HL with size of BB_BSS bb_size+=0x0fff; // round up 4k bb_size&=0xf000; // # of 4k pages return bb_size; }
Ok, we have just about everything. Now we would just define variables and allocate the BB_BSS seg at run time.
#pragma SEG(BSS,BB_BSS) // variables between these seg statements is battery-backed and not zeroed extern int current_state; extern error_log ErrorLog[]; #pragma SEG(BSS) void main(void) { unsigned bb_size; char far * bb_far_array; // main variables bb_size=get_bb_size(); // get the size of the segment rouned up to 4k; // assume this works map_bb_ram(bb_xalloc(bb_size)); // map STACKSEG to regular RAM, DATASEG to BB RAM // rest of main code // allocate a battery-backed far array bb_far_array=bb_xalloc(FAR_SIZE); }
Ok, that will map our BB_RAM variables. Now we will have full control over battery-backed global variables and regular variables and stack in fast RAM. The bb_xalloc() can be used to allocate far buffers that are battery backed. xalloc() will return fast RAM buffers that are not battery backed. Once xalloc() reaches 0x80000, it will start using battery-backed RAM.
A code sample is now available.
The sample does the following:
The sample includes a bb_ram.lib (and source) that can be used in your programs. The sample code will work on boards with fast RAM using the remapping techniques shown above. It can also be used on regular boards that have all RAM battery backed.
The sample can be downloaded here:
Note: this is not compatible with the CoExec with the far stack options. It uses similar tricks to use DATASEG and STACKSEG to have a different stack segment for each task. You can use the bb_xalloc() function with CoExec.