💾 Archived View for librehacker.com › gemlog › tech › 20220701-0.gmi captured on 2024-07-09 at 00:36:38. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-05-10)

-=-=-=-=-=-=-

Mecrisp Stellaris Forth for RP2040: Flash and SRAM

I use the Rasberri Pi Pico Microcontroller which uses the RP2040. I was in the process of writing a gemlog post about my recent adventures in code optimization in Mecrisp Stellaris Forth, but I got hung up on a few ancilliary questions about how Mecrisp's memory model works on the RP2040 platform. Since I put so much energy into figuring that out, I thought I would first post some basic information about it.

The big difference between the RP2040, and most (all?) of the other ARM microcontrollers used with Mecrisp Stellaris, is that RP2040 does not have any onboard flash memory. Rather, flash memory is provided over a serial link to external QSPI Flash modules. The Rpi Pico happens to include 2 MB of QSPI flash memory. As far as onboard volatile memory, RP2040 includes 264 KB of SRAM, not including SRAM for registers and data buffers.

RP2040 does support XIP, to execute code directly from flash memory, but that will be very slow compared to executing code from SRAM, since the code must first be pulled four-bits-at-a-time over the QSPI connection. So, in the case of the Mecrisp port to RP2040, the design choice was made to run all code (except the code for initially loading Forth) in RAM.

To accomplish this, Mecrisp divides the flash memory (logically) into 1B000h byte images (about 110 KB each), except for the first 6000h bytes are reserved for the core Mecrisp code. Each image (if you have used it) contains a "flash dictionary" of Forth words, which you copy to RAM with the "load" word, and the first image loads automatically when you power on the MC. Once your forth system is running in SRAM, you are able to edit the "flash dictionary" (now residing in RAM) however you would like, and then use the "save" word to save it back to an image.

After the flash dictionary is loaded, the other half of the RAM is made available for the "ram dictionary" a separate dictionary which can use words from the flash dictionary. The ram dictionary is cleared when you restart or power cycle the MC. It also must be cleared whenever you load a new image from flash, since the code addresses it used may no longer be available. Mecrisp forth searches both dictionaries for words, starting with the RAM dictionary. There are words "compiletoflash" and "compiletoram" to switch where new words are defined, though physically speaking both dictionaries reside in RAM while you are defining words.

Since you have multiple images available to work with, you can store other programs, or different versions of the same programs, in your other images. The main downsides to this approach are (1) any given flash dictionary must be limited in size to about 110 KB, and (2) you cannot share words across images, except by duplication.

It is, however, possible to read and to write directly to any area of flash memory, so that you could use some of your flash for storing other kinds of data like big data tables (PCM samples, for example), and to share data between images. Reading from flash directly is pretty easy, since flash is mapped to address 10000000h and up, and you can use the "@" word for this.

Writing to flash directly is more complicated, since this cannot be done with the ! word. This has something to do with the restrictions RP2040 places on writing data to flash from memory, I believe - a flash write directly to a mapped memory location is just ignored. However, it is possible to do a non-mapped copy of data from SRAM to flash, and the details of that are (mostly) handled by a Mecrisp forth word called "program-range". It is documented inside an assembly code file:

@------------------------------------------------------------------------------
@ Program data to a range of flash addresses starting at addr
@ (offset from the start of flash) and count bytes in size.
@ addr must be aligned to a 256-byte boundary,
@ and count must be a multiple of 256
@
@ Example:
@   connect-flash exit-xip
@   $10000 here 256 program-range
@   flush-cache enter-xip

   Wortbirne Flag_visible, "program-range" @ ( addr data count -- )

With this word, you must specify the address as the actual offset from the beginning of flash memory, the address of some data in SRAM that you want to copy over, and then the number of bytes to copy. Plus you have to handle a few setup and cleanup tasks. But it is doable, and some of the details could be hidden away in a user-defined word.

Just keep in mind though that reading from and writing to flash is slow, and writing to flash wears out those memory cells. So you might not want to do it too often.

On RP2040, SRAM is mapped at 20000000h and up. For more information on the memory mapping of registers, see the RP2040 data sheet. For more information about how Mecrisp uses SRAM, see the "rp2040-ra/README" file in the mecrisp-stellaris download.

Mecrisp Files page (SourceForge)