Using flash memory

From Spectrum
Jump to navigation Jump to search

Like the static RAM, there is 128K of flash memory. It's accessed the same way as static RAM - for information on how to page the flash memory, see the article about using the static RAM. This article deals with how to erase and write to the flash memory.

The difference between flash and RAM

You have probably seen that the flash is often referred to as 'flash ROM' - while you can in fact write to it. The distinction is made because ordinarily, the flash is read-only, and acts like an EPROM. You can't, for example, use the instruction LD (0x0123), A to write to the flash memory; that instruction would have exactly the same effect that it would when writing to a traditional mask-programmed ROM - i.e. none at all!

The features of flash are as follows. Compare to the features of RAM.

  • Flash is non-volatile - it retains its programming when powered off.
  • Flash is organized into "sectors". The Am29F010 chip used by the Spectranet is organized into eight 16K sectors.
  • Flash must be erased before you can write to it. You cannot change a bit that has been set to 0 back to 1 again. The minimum amount of data you can erase is an entire sector, so for the Am29F010, 16K.
  • Unwritten flash bits are set to 1.
  • To write a byte, you must send the flash chip a sequence of commands to unlock the byte, then write a value.
  • To erase a sector, you must send a sequence of commands.

By contrast, RAM:

  • RAM is volatile - it loses its programming on power off.
  • You can arbitrarily write to any byte in RAM. There's no need to erase first.

There is also such a thing called non-volatile RAM - typically, this is static RAM that has battery backup, or a more exotic technology such as MRAM, which is a type of non-volatile RAM that does not need battery backup.

Given that true non-volatile RAM exists - why use flash at all, given the extra steps needed for writing, detailed above?

There are two major reasons: reliability and cost. Battery-backed static RAM is extremely easy to corrupt, so it is not very suitable for use as ROM. Purpose-built battery backed static RAM (which typically incorporates the RAM and battery in one package) mitigates this with extra circuits to disable writes when the supply voltage starts to fall, and also delays the availability of RAM on power-up so that the power supply may be fully stable before the memory can be written. This purpose-built non volatile RAM is very reliable, but it's also very expensive - costing between ten and twenty times as much as a flash chip of the same capacity. To mitigate the cost of the device, it would be possible to design a circuit using standard SRAM and battery backing, however, this would make the PCB much larger, since it requires a relatively large battery (physically larger than the Spectranet's CPLD) which would just transfer the extra cost to the PCB itself, and adding the cost of a battery plus its holder. Additionally, both of these approaches mean that either an obscenely long reset pulse would be required on the Spectrum (so that the RAM is ready for system variables) or a software delay. It still doesn't prevent corruption - I have a ZXCF that regularly gets corrupted non volatile RAM when the Spectrum is powered off, presumably because the capacitance on the 5v supply is large enough that the write signal can bounce during the power off before the supply voltage has fallen enough to disable the write pin.

A new technology, MRAM, doesn't need battery backing but it still would need to mitigate the problem of the write signal bouncing on power on/power off, and it's also extremely expensive - about twenty times the price of the same amount of flash.

In a nutshell, flash offers far superior reliability (extremely difficult to accidentally corrupt), requires no extra logic to make it normally read-only, at a much better price - for the expense of having to write a simple routine to erase and program it. Unlike EPROM, you can program it from the Spectrum, too, since the erase and write sequences are made up of standard CPU write cycles.

The type of flash used by the Spectranet is quite different to the kind of flash you might find in a CF card or USB drive. Mass storage flash is typically NAND flash that can only be addressed by whole sectors. The kind of flash used for firmware is NOR flash (made up of NOR logic gates), and is addressable by byte, just like any other type of memory. It has the same pin-out as an equivalent EPROM. Unlike an EPROM, though, it doesn't require high-voltage programming - programming is carried out at 5 volts.

As for the write 'wear lifetime' of the AMD Am29F010, it's guaranteed for a minimum of 100,000 program/erase cycles. This is more than sufficient for the Spectranet!

Flash as used by the Spectranet

As explained in the paged RAM article, the memory is organized as 4K pages. This is true for all Spectranet memory. The flash occupies the lowest 128K of the 1MB of address space on the Spectranet, from page 0x00 to page 0x1F (32 4k pages). Pages 0x00-0x03 are reserved for the Spectranet's own ROM modules (page 0x00 is where the library lives,and is permanently mapped to 0x0000-0x0FFF in the Z80's address space). Additionally, the last 256 bytes of page 0x1F is used to store the Spectranet's configuration - things like MAC address, IP address, whether to use DHCP etc.

Erasing the flash

As you read earlier, the flash has 16K erase sectors. This means if you want to re-write a 4K page, you have to erase the entire sector. This means it's a good idea to store the other three pages that are going to be erased in RAM, so they can be written back. If you look at the source code for the configuration utility, you will see this being done - see configmain.asm's F_copyconfig routine where it saves the last 4 pages of the flash chip to RAM, in preparation for the erase operation.

The code that erases a flash sector cannot actually run from the flash itself. This is because the CPU fetching the instructions to execute the code that performs the erase would disturb the erase sequence, and cause it to abort. Therefore, the code to erase (and write) to the flash is copied to RAM. The configuration utility, for example, copies its flash programmer to the temporary workspace RAM at 0x3000.

Writing the erase sequence is done just as if you were accessing the actual addresses in the write sequence as memory. This is the sequence you write to the flash to cause a sector to be erased:

  • Write 0xAA to address 0x555 - this is unlock code 1.
  • Write 0x55 to address 0x2AA - this is unlock code 2.
  • Write 0x80 to address 0x555 - this is erase command 1.
  • Write 0xAA to address 0x555 - this is erase command 2.
  • Write 0x55 to address 0x2AA - this is erase command 3.
  • Write 0x30 to the lowest address of the sector you want to erase.

The last operation is carried out by paging the lowest 4K page in the sector into one of the Spectranet's paging areas, and then writing 0x30 to its lowest address. The reason for this seemingly long sequence of commands (also note the alternating 101010... and 0101010... patterns) is to make it exceedingly unlikely that the sequence can happen by accident, either by signal lines bouncing during power up and power down, or by bugs in unrelated pieces of code.

To see an example of an erase routine (and one you probably want to just include in your own projects) see F_FlashEraseSector in the linked file.

The erase operation takes some time to complete (not long in human terms, but a couple of million Z80 T-states). The flash chip provides a method to see when the erase operation has completed, and whether it has completed successfully. The following code shows how. In this code snippet, the sector being erased is paged into paging area B (0x2000-0x2FFF).

       ld hl, 0x2000
.wait
       bit 7, (hl)     ; test DQ7 - should be 1 when complete
       jr nz, .complete
       bit 5, (hl)     ; test DQ5 - should be 1 to continue
       jr z, .wait
       bit 7, (hl)     ; test DQ7 again
       jr z, .borked

DQ7 and DQ5 are just the names of the pins on the flash chip (i.e. bit 7 of the byte, and bit 5 of the byte, which are on data bus lines D7 and D5 respectively. 'DQ' just indicates that this is a pin that is both an input and output pin).

Writing to the flash

The basic principles are similar to erasing flash: you send a sequence of commands, followed by the byte to write. Each byte written requires the command sequence - you can't just write an 'unlock' sequence and LDIR the data in. However, the Spectranet configuration utility has a routine called F_FlashWriteBlock that is morally equivalent to LDIR which you can use in your own projects.

An important principle to note is that an unwritten bit of flash is set to 1, and a written bit is set to 0. Any bit can be set to 0 without needing to erase first, even if some other bits in the same byte have already been set to 0. However, a bit set to 0 cannot be reset to 1 - only an erase can do this (and as previously noted, only for a whole 16K sector, or the entire chip). So if you've already written to a sector, you can keep writing to it as long as you like, over multiple power cycles, so long as you never try to set a bit that's been set to 0 back to 1 (which will fail). You'll likely not ever want to write to the same byte more than once, but you're quite likely to want to write adjacent bytes between power cycles, so it's important to know you can do this.

The write sequence looks like this:

  • Write 0xAA to address 0x555. This is unlock code 1.
  • Write 0x55 to address 0x2AA. This is unlock code 2.
  • Write 0xA0 to address 0x555. This is the 'program address' command.
  • Write the byte, in the normal way (i.e. with something like LD (address), a)

Just like the erase operation, you can test whether the byte was written correctly, and again, it is done by testing bits 5 and 7 of the byte you just wrote. To see an example of writing a byte (which you can incorporate in your own project), see the F_FlashWriteByte routine in flashwrite.asm.

Again, the routine to write a flash byte can't actually be running from the flash chip, or CPU instruction fetches will invalidate the unlock/program address sequence - so ensure the program is running from RAM, either the Spectrum's main memory or the static RAM on the Spectranet.

More information

To get the complete information on all flash command sequences, see the datasheet for the chip which can be found here.