Skip to content

Developing flash driver functions

doctek edited this page Aug 8, 2011 · 4 revisions

What this page is about.

This page discusses some of the functions that are in a flash driver. Explained are both useful details of how the functions work in the flash driver, as well as how the code is developed and the experiments I did along the way. This should be useful to others as they explore new chips and develop flash drivers for them.

How do I "probe" flash?

Actually, you don't really probe the flash. You probe the chip itself, usually via on-chip registers, to identify it and be sure the flash driver is appropriate. Part of the identification is to know the size of the flash, to determine if it's protected or not, and to make sure the information needed by erase and write routines is put in appropriate structures. If you haven't looked at the source code I provide, now is the time to do that. The two drivers show the methods needed by each chip to identify them using the on-chip registers.

How is the chip erased?

Gee, this seems like an easy one, right? I certainly thought it would be easy to do this for the NUC-120. There was some sample code in the Nuvoton SDK. But it didn't work as I expected and I discovered the documentation wasn't as complete as I thought it was. So I'm working now to figure out the erase process for this chip.

How does the Flash Write process work?

One way to program flash memory is one word (or byte) at a time. While certainly possible, this method is not commonly used for at least a couple of reasons. First, flash must often be programmed in blocks, so programming a word at a time is cumbersome at best. Second, word at a time is a very slow process. Most people won't wait that long.

The usual method implemented in OpenOCD is to load a small program into on-chip ram and transfer control to it. This small program reads the target program (which is to be placed in flash) from a ram buffer and writes it to flash. OpenOCD keeps the ram buffer filled until the entire target program is written to flash. The small program then ends and the flash is programmed. Right now, this is all my "understanding" based on reading the code. When I have developed the write function for the NUC, I will know a lot more about writing to flash in OpenOCD.

One detail I have yet to experiment with is how to have values passed in registers that the write code (on the ARM) can access. There seems to be some "magic" involved which I need to explore and understand. Perhaps others can comment?

How is the write routine developed?

It should be clear that a key step in developing a flash driver for OpenOCD is developing the program that OpenOCD will load into on-chip ram to write the target program to flash. Developing this program should not be difficult, but involves using familiar tools in a slightly different way, one many people may not have tried. (The hardest part is figuring out how to write to your particular chip's flash memory. Once you know this, then it's a matter of getting the programming code into a form that can be loaded by the flash driver into the target chip.)

Not surprisingly, we will use OpenOCD to do the development of this program. OpenOCD should be able to control your target device. That is, OpenOCD should be connected to your chip via an interface of some sort. Details of using the STM32Discovery board as an interface are given elsewhere (pointers are on the Introduction Page). You may of course use any alternative interface that works for you.

With your interface connected appropriately, start OpenOCD in a terminal window and start Telnet in another terminal window. This procedure should be well known to you.

Even if you are an experienced OpenOCD user (you should be), there are a few details that may be of use to you. The NUC-120 is a Cortex-M0 which is not specifically supported in OpenOCD. Happily, treating it like a Cortex-M3 works just fine. It also works fine to use the stm32x configuration file. With that configuration, OpenOCD will connect to the NUC and will handle the "reset halt" command correctly. Trying other commands, like "mdw 0", also work. Reading and writing registers and sram works also, so development proceeds using OpenOCD. On the NUC, I found that reads of bytes or half words did not give correct results, so I stick to word access in what follows.

Following are details of some experiments I tried:

Experimental chip: Novoton NUC-120, an ARM Cortex M0 with 128K of Flash, 16K of sram. Sram is at 0x20000000 and flash is at 0x0000000000.

First experiment: Read/Write to ram. Look at the documentation for your processor. It will tell you what address range your ram responds to. Read from ram using mdw and write to ram using mww. Accessing ram as bytes or half words was not reliable when reading - OK when writing. No idea of why!

mdw 0x20000000 10

0x20000000: aaaaaaaa aaaaaaaa 0000aaaa 000000aa 000000aa 000000aa 000000aa 000000aa 0x20000020: 000000aa 000000aa

mww 0x20000000 0x55555555 5

mdw 0x20000000 10

0x20000000: 55555555 55555555 55555555 55555555 55555555 000000aa 000000aa 000000aa 0x20000020: 000000aa 000000aa

Second Experiment: Put a simple program in ram and run it. A really simple program is

here: br here @ always branches in a tight loop

This program compiles to E7 FE So put this in ram. Next, use

resume 0x2000000000

to run the program. Of course, nothing will visibly happen as the program runs. So when you get bored looking at an unchanging screen, enter halt. The program you entered will halt and display the status. Here I first load the (two byte) program, then use the disassembler to look at 4 statements (3 are junk), then run the program, and finally halt. Note the status from halt shows that the next instruction will be at 0x20000000 - just as expected since we just sit in a loop.

mww 0x20000000 0xe7fe

arm disassemble 0x20000000 4

0x20000000 0xe7fe B 0x20000000

0x20000002 0x0000 LSLS r0, r0, #00

0x20000004 0x5555 STRB r5, [r2, r5]

0x20000006 0x5555 STRB r5, [r2, r5]

resume 0x20000000

halt

target state: halted

target halted due to debug-request, current mode: Thread

xPSR: 0xc1000000 pc: 0x20000000 msp: 0x20000800

Of course, typing in our program by hand is tedious. So let's use the hex file load capability of OpenOCD to load the program from a hex file and run it. First we'll clear out memory, then we'll load the file, look at the disassembly, rum our code, halt, and do a couple of single steps as another verification that we're just looping in one place.

mww 0x20000000 00000000 10

mdw 0x20000000 10

0x20000000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0x20000020: 00000000 00000000

load_image /home/jim/ARM/projects/ocdTest/flashHack.hex 0x20000000

2 bytes written at address 0x20000000

downloaded 2 bytes in 0.006320s (0.309 KiB/s)

arm disassemble 0x20000000

0x20000000 0xe7fe B 0x20000000

resume 0x20000000

halt

target state: halted

target halted due to debug-request, current mode: Thread

xPSR: 0x01000000 pc: 0x20000000 msp: 0x20000800

step

target state: halted

target halted due to single-step, current mode: Thread

xPSR: 0x01000000 pc: 0x20000000 msp: 0x20000800

step

target state: halted

target halted due to single-step, current mode: Thread

xPSR: 0x01000000 pc: 0x20000000 msp: 0x20000800

These simple experiments should convince you that a flash write (or erase or whatever) program can be developed and should suggest how to go about testing and debugging it. You should create an environment where test routines can be assembled (or compiled). If you've done anything with ARMs, you probably have this environment set up already.