Writing to Flash Memory on the NanoCore12C32

Dwayne Litzenberger
Last Updated: May 14, 2006

Introduction

The NanoCore12C32 is a 24-pin DIP microcontroller module sold by Technological Arts for about $50. It is a pre-built module designed around the Motorola/Freescale MC9S12C32 microcontroller, and it features 32 KiB of on-chip non-volatile flash memory. This document aims to serve as an introduction to writing to the 9S12's on-chip flash memory.

It is assumed that the reader is familiar with assembly-language programming for the MC9S12C32.

CAUTION: Before you start mucking around with the flash memory, be aware that the serial monitor (the program that allows you to program the NanoCore12 over the serial port) and the interrupt vectors (which are responsible for calling the serial monitor code) are also located in flash memory. If you corrupt the serial monitor or the interrupt vectors, it will probably become impossible to re-program your NanoCore12 module using the serial port. I've heard that the serial monitor supposedly locks its own area of flash so that you can't accidentally erase it, but I didn't feel like testing it on my hardware, so I can't really say for sure. Therefore, I repeat: You might damage your hardware by following these instructions. Use these instructions at your own risk!

Basics

Flash memory is similar to read-only memory (ROM). When you read from some address, you get the data stored there, and when you write to that address, nothing appears to happen -- the old values remain. Although flash memory can be written to, you need to follow a special procedure to modify the data in flash memory.

Sectors and Words

Flash memory is divided into sectors, which are themselves divided into words. If you want to write to flash, you first need to erase the sector you're interested in. Once you've erased a sector, all the words in the sector may be programmed. However, after being erased, each word in a sector is only allowed to be programmed once; If you want to re-program a word, you must again erase the entire sector.

On the MC9S12C32, sectors are 512 bytes each and words are 2 bytes each.

Memory Map

NanoCore12 Memory Map

The default memory map for the NanoCore12 module (assuming the Motorola Serial Monitor is intact) is as follows:

Address rangeDescription
$0000-$03FFReserved (Device registers)
$3800-$3FBFUsable RAM (when INITRM is set to $39)
$3FC0-$3FFFReserved (used by the serial monitor when debugging)
$4000-$7FFFBottom 16 KiB flash region (usable)
$8000-$BFFF16 KiB Page Window (see below)
$C000-$F5FFTop 16 KiB flash region (usable)
$F600-$FFFFReserved for bootloader/monitor (do not touch)

The Address Range $8000-$BFFF, or Where You Should Not Put Your Program (unless you need to)

The address range $8000-$BFFF is actually a mirror of either the bottom flash region or the top flash region, depending the current value of the PPAGE register. For the purposes of this tutorial, it is recommended that you avoid using this address range.

You may have seen a demo program that starts with the following line:

org $8000   ; Start of program memory

In your programs, you should replace the above line with a line like this:

org $C000   ; Start of program memory

Also: You may need to to update your interrupt vectors, if you had any of them hard-coded to $8000.

This will cause your program to be placed in the top region of flash. As a result, you will be free to use the entire bottom region of flash ($4000-$7FFF) for your own purposes, without having to worry about overwriting either your program code or the bootloader/monitor code.

Flash Registers

The following register addresses may be useful to you:

AddressNameDescription
$0100FCLKDIVFlash clock division register
$0105FSTATFlash status register
$0106FCMDFlash command register

Reading Flash

To read a value in flash memory, you don't have to do anything special; Just read the data the same way you would read any other value from memory. However, you cannot read flash at the same time as you are erasing/programming flash. Trying to do so will cause your program to crash. The implications of this limitation will be discussed in more detail below.

Setting the FCLKDIV register

Before you can erase or program the flash, you must set the FCLKDIV register so that the flash core runs between 150kHz and 200kHz. Be careful here. The manufacturer's documentation states that if the flash clock is too slow (divided too much), you will damage the flash memory module.

If your PLL clock is running at 24 MHz, you can use the value $51, which divides the clock down to about 175 MHz. However, since you can damage your hardware if you're not careful, it is recommended that you read the manufacturer's documentation and determine the correct value for yourself.

In any case, this is the code that corresponds to the value $51:

movb #$51, FCLKDIV   ; Set flash clock to approx. 175 kHz

Erasing a Sector

Before you can program the flash, you must first erase the sector you're interested in. To do this, perform the following steps:

  1. Perform an ordinary write to any address inside the sector you want to erase
  2. Set FCMD to $40 ("sector erase" command)
  3. Clear the CBEIF bit by writing $80 to the FSTAT register. This starts the erase process.
  4. Wait until the CCIF bit (bit 6) in the FSTAT register is set. This indicates that the erase process is complete.

Programming a Word

Once you've erased the sector, you can program the 16-bit word you're interested in. Note that the word must be aligned on 16-bit boundaries. (i.e. It can be a 16-bit word starting at $4000, $4002, $4004, etc., but not at $4003.) Then, perform the following steps:

  1. Write the 16-bit value to the address you want to program.
  2. Set FCMD to $20 ("word program" command)
  3. Clear the CBEIF bit by writing $80 to the FSTAT register. This starts the erase process.
  4. Wait until the CCIF bit (bit 6) in the FSTAT register is set. This indicates that the erase process is complete.

When I try to erase or program the flash, the CPU resets!

Your flash programming routine must be in RAM, not in flash. An easy way to accomplish this is to copy your flash programming routine to RAM and execute it there, rather than executing the routine from flash directly.

The problem with executing your flash programming routine from flash is that as soon as you write to the FSTAT register, the CPU will try to read the next instruction -- from flash. Since the flash cannot be read while it is being programmed, this read will fail and the CPU will crash.

Example

Initialization code


  FCLKDIV	equ $0100
  FSTAT	equ $0105
  FCMD	equ $0106
    org $C000   ; Start of program memory
    
    ; ... Do your own initialization here, including setting the system clock
    ; ... to 24 MHz
    
    ; Set flash clock to approx. 175 kHz
    movb #$51, FCLKDIV   
    
    ; Initialize ProgramFlash
    jsr CopyProgramFlashRoutine
  

Routine to copy the ProgramFlash routine from flash to RAM


  ;;;;;;;;;;;;;;;;
  ; CopyProgramFlashRoutine - Copy ProgramFlash_ROM to ProgramFlash
  CopyProgramFlashRoutine:
    ldab #(ProgramFlash_ROM_END-ProgramFlash_ROM)
    ldx #ProgramFlash_ROM
    ldy #ProgramFlash

  __CopyProgramFlashRoutine_loop:
    ldaa 0,X
    staa 0,Y
    inx
    iny
    dbne B,__CopyProgramFlashRoutine_loop
    
    rts
  

The actual flash programming routine


  ;;;;;;;;;;;;;;;;
  ; ProgramFlash_ROM - Re-programs the non-volatile variable
  ; Note: This function clobbers the registers
  ; NOTE: This function MUST be copied to some non-flash storage (e.g. RAM) and
  ;       executed from there.  Otherwise, the CPU tries to fetch instructions
  ;       from flash while the flash controller is erasing/programming, which
  ;       doesn't work and causes the CPU to do weird things (like resetting!).
  ; NOTE: If you change this function, you may need to adjust how much memory
  ;    is reserved for ProgramFlash.
  ProgramFlash_ROM:
    ; Erase sector
    std flash_counter
    ldaa #$40           ; erase
    staa FCMD
    ldaa #$80           ; clear CBEIF
    staa FSTAT          ; Execute
    
    ; wait for completion
  __ProgramFlash_ROM_wait1:
    ldaa FSTAT
    anda #$40
    beq __ProgramFlash_ROM_wait1
    
    ; Program word
    ldd counter
    std flash_counter
    ldaa #$20           ; program
    staa FCMD
    ldaa #$80           ; clear CBEIF
    staa FSTAT

  __ProgramFlash_ROM_wait2:
    ldaa FSTAT
    anda #$40
    beq __ProgramFlash_ROM_wait2
    
    rts
  ProgramFlash_ROM_END:
  

Reserve space in RAM for ProgramFlash


    org $3800       ; RAM
  ProgramFlash:
    rmb $30         ; Space reserved in RAM for the flash writing routine
  counter:
    rmb 2           ; Space reserved in RAM for the counter variable
  

Designate space in flash to hold your variables


    org $4000   ; Flash memory
  flash_counter:
    rmb 2       ; Space reserved in flash for the counter variable
  

Acknowledgements

I would like to thank Exequiel Rarama from Technological Arts, for providing me with information for the memory map section; John Palmarin, for being a guinea pig for this tutorial, and for providing example source code in C; and Mark Anderson, for helping to come up with this code in the first place.

Example C Source Code

John Palmarin has provided some example source code in C to complement this tutorial.

References

[1] MC9S12C Family Device User Guide V01.05, Motorola, Inc. Revised: 11 February 2004.
[2] FTS32K Block User Guide V01.03, Motorola, Inc. Revised: 1 April 2003.