A (real) Overview of Rabbit 4000 Memory Management

After picking through umpteen manuals to figure this all out, I am posting here in the hopes that someone else can benefit. This should be an app note.

  [b]Overview of Rabbit 4000 / RCM4200 Memory Management[/b]

The information in this document was accumulated from:

Rabbit� 4000 Microprocessor User�s Manual 019�0152 � 070720�H
Rabbit 4000� Microprocessor Designer�s Handbook 019-0156 � 070920-D
RabbitCore RCM4200 User�s Manual 019�0159 � 070928�C

Most, if not all, of the details can be found in the Rabbit 4000 User’s manual.

The Rabbit 4000 has a total of 24 physical address lines:

dedicated lines A0-A19
configurable lines PE0-PE3 which can function as A20-A23

The 4000 has either 8 or 16 data lines:

dedicated lines D0-D7
PD0-PD7 function as D8-D15 when 16 bit mode is enabled

The 4000 has:

three chip select pins: /CS0, /CS1, and /CS2
two read strobes: /OE0 and /OE1
two write strobes: /WE0 and /WE1

The memory is divided into four banks of equal size from 128KB up to 4MB. The bank size is set by telling the 4000 which two address lines should be used to control the bank switching. For example, if the 4000 is configured to use A17 and A18 for bank switching, then A0-16 will access 2^17 bytes (128KB) in each bank. The address lines above the pair used for bank selection are not used.

Figure 5-1. Mapping Rabbit 4000 Physical Memory Space

Each bank can be configured to use a specific chip select pin and a specific Read/Write enable pair. This allows each bank to be read from a different device. The three chip selects in conjunction with the two enable pairs allow the use of 6 different devices. For example /CS0 and /OE0:/WE0 might access chip one while /CS1 and /OE0:/WE0 could access chip two. Since both the chip select line and the enables are required to access a chip, the two chips are controlled independently. The same is true in the opposite fashion; two chips might have the same select line but different enable pairs.

In figure 5-1, banks one and two use the same select and enable lines so they both read from the same device. This brings up a very important point:

Note 1: The bank selection pair of address lines still drive external pins and are used as address bits.

This is important when two adjacent banks are setup to access a single chip. The bank selection pair of address lines are fed to the chip, even as they also perform bank selection duties. In the example above, the bank select pair would be A18:A19, with A0:A17 (2^18 = 256 KB) addressing within the bank.

When accessing memory below 256KB in this case, A18:A19 would both be zero so bank 0 would be selected as they are also the bank selection pair. When the second 256KB data range is accessed, A18:A19 would be 0:1 as required to create an address in this range. The A18:A19 pair are fed to the data chip so that it returns data from the proper address. As the pair also serves to select the bank, they now switch to bank 1. Since bank 1 is set up in this case to also access the same chip as bank 0, the data from the first and second 256KB block can be read seamlessly from this single chip.

The bank size is setup using the MECR register by specifying the pair of address lines to use for bank switching. The timing, select line, and enable lines are setup using MB0CR, MB1CR, MB2CR, and MB3CR.

The MBxCR registers also allow the bank select pair to be inverted. This trick allows even more memory to be accessed. For the example above without inversion, A18:A19 set to 0:0 would access bank 0 and the output pins would access the lowest 256KB block. If the register is now changed to invert A19 going out to the pin, the A18:A19 pair is still 0:0 and selecting bank 0 while the A19 pin is now a 1, which would load data from the third 256KB segment in the chip. This allows bank 0 to access pages at higher addresses and thus creates a paged memory system.

The Samples\MEMORY_USAGE.C program provided with the Dynamic C installation can be used to learn how the memory is configured on a particular Rabbit board. Running this program for the RCM4200 with

“Project Options/Compiler/Store Program in = RAM” selected shows:

MB0CR=c6=11000110: 0/1 Read/Write wait states, /CS2, /OE1 & /WE1
MB1CR=c6=11000110: 0/1 Read/Write wait states, /CS2, /OE1 & /WE1
MB2CR=45=01000101: 2/3 Read/Write wait states, /CS1, /OE1 & /WE1
MB3CR=00=00000000: 4/5 Read/Write wait states, /CS0, /OE0 & /WE0

The schematic for the RCM 4200 shows the /CS2, /OE1, and /WE1 are connected to the 512K FSRAM, so banks 0 and 1 access this chip.

By modifying the original MEMORY_USAGE.C program to display the MECR register using the MECRShadow value, the following is discovered:


This value is obviously wrong as it violates some restrictions on the MECR register as explained in the Rabbit 4000 User Manual. Looking at StdBios.c, it seems obvious that the shadow register is not always properly updated when a value is stuffed to the MECR register. Using the method used in StdBios.c for reading the value of MECR:

printf(" MECR=%02x



MECR=20=00100000=Bank select address is A[20:19]=512KB bank size

Since the bank size is 512KB and the FSRAM is 512KB, bank 0 and 1 both access the full range of the chip, so either could be used. Since the execution code and data area are normally based at location 0x00, bank 0 would be used to execute code.

Looking at the schematic, the SRAM chip uses /CS1, /OE1, and /WE1, thus bank 2 accesses this memory. Finally, the FLASH chip uses /CS0, /OE0, and /WE0, thus bank 3 accesses this memory.

Note2: The purpose of the “shadow” register macros is to keep a copy of the last value written to the associated register. Some registers are write only so the only way to know what was written to them is to keep a copy in memory. The MECR register can be read via the MECR_VALUE macro, so the bios’ failure to keep the shadow value updated is mitigated.

To recap

Bank 0 accesses 512KB of FSRAM (fast execution ram)
Bank 1 accesses 512KB of FSRAM (mimics Bank 0)
Bank 2 accesses 512KB of SRAM (battery backed up)
Bank 3 access 512KB of FLASH

The setup is exactly the same when “Project Options/Compiler/Store Program in = FLASH” is selected. This makes sense because with this option, the code is stored in the FLASH memory but copied to the FSRAM before being executed. In both the RAM and FLASH options, the code is still being run in FSRAM so the bank layout should be the same.

Code and BIOS in Flash, Run in RAM

Several of the manuals recommend running in the “Code and BIOS in Flash, Run in RAM” mode - but this option is no longer available in the Dynamic C IDE, at least for the RCM4200. Apparently, because this mode is the only viable option for the higher speed RCM4200, the compiler now defaults to this mode regardless of which of the remaining options of RAM or FLASH is chosen.

In RAM mode, the FLASH is bypassed and the code is compiled to and run in the FSRAM. This is used mainly for debugging to save wear on the FLASH.

In FLASH mode, the code is stored to the non-volatile FLASH and then copied to the FSRAM before execution. This is used for production as the code will not be lost when the power to the board is cycled.

Storing Code in Serial Flash

Code can also be stored in the serial FLASH chips which provide a lot of memory for code and data such as web pages. The only way to specify this seems to be to define SERIAL_BOOT_FLASH in the Project Options/Defines window or in the code itself. This was discovered by examining the code in MEMORY_USAGE.C which checks to see if this is defined before reporting that the code is compiled to serial FLASH.