GigaDevice is well known for its high-performance and sanction-proof microcontrollers. The company also provides various types of flash memory as standalone and SoC-integratable variants. In the following steps, we will use GigaDevice's evergreen GD25 SPIO NOR flash chip to add remanent memory to an Arduino Duo.


Why Bother?

Most contemporary microcontrollers come with a more or less generously-sized flash-memory allowance, enabling embedded software to store constants and measured values on the chip.

Using them, however, has disadvantages. First, almost all flash memory suffers from limited write-cycle tolerance - if it is expended, an expensive CPU must be thrown away. An external flash memory chip can sit on a daughterboard, which is technician-replaceable.

The second and more insidious effect concerns modern microcontrollers. In a technology that Microchip pioneered, the dedicated EEPROM is often replaced by a bit of logic, which permits the microcontroller to use parts of its flash memory as storage for remanent values.

While this yields a significant reduction in chip cost, it comes with a severe disadvantage - accessing the flash memory tends to halt the execution of the main CPU and, in many designs, even impedes the core's ability to service interrupts. An external flash memory chip connected via SPI also eliminates this problem.


Creating an Interface Board

GigaDevice offers various form factors for the GD25. Sadly, none of them is compatible with a breadboard, which leads to complications. Yours Truly once designed a module in the Seeed Grove form factor and also used a Proto Advantage / Chip Quik adaptor board (part number PA0010) once.

The resulting designs are shown in the two figures accompanying this text.

Creating an interface board GD25
Figure. 1

GigaDevice GD25 with an Arduino project
Figure. 2

A slight complication concerns the format used for communication: while SPI achieves high data-transfer rates, parallelization yields even better results. Due to this, GigaDevice implements a bus standard called Quad I/O. It can use up to four lines for communication, which leads to the pinout shown in the figure taken from the datasheet.

GigaDevice implements a bus standard called Quad I/O
Figure. 3

Fortunately, the parts can also be used in normal simple SPI mode. In that case, a pinout similar to the connections shown in the table is required.

Chip Arduino

SCLK SPI Clock

IO1 MISO

IO0 MOSI

CS GPIO 2

Connecting to an Arduino

Given that the voltage supply range supported by the GD25 ranges from 2.7 to 3.6, the venerable Arduino Uno and its 5V microcontroller cannot be used. Yours truly will, instead, use an Arduino Duo - it is based on an ATMEL CPU, which is supported by most available libraries.

Speaking of libraries, the following steps will use the SPIMemory library developed by Marzogh - find it at the URL https://github.com/Marzogh/SPIMemory/, and deploy it via the library manager in the Arduino IDE.

Performing the actual link-up is simple: use the pins connected to the SPI engine. While SPI signals can get relatively fast, our example will work happily via a DuPont wire. When high frequencies are involved, HF PCB design rules are to be observed religiously - if not, data-transmission errors can occur leading to stability problems.

The main remaining issue involves the selection of the GPIO pin which will be used for the chip select signal. Chip select signals, in general, are uncritical - they get enabled for peripheral selection. After that, we can proceed to the bring-up. The runtime interface to the flash chip takes the shape of the following variable:

#include<SPIMemory.h>

SPIFlash myIF(2);


Understanding JEDEC Flash

NOR flash memory is a highly standardized product: many, if not most, chips available follow various standards set out by the JEDEC consortium. Due to this, our GD25 flash chip implements various reflection methods that let the microcontroller receive information about the flash memory chip at hand.

Reading these attributes out makes for a smoke test that verifies the connection between the Arduino and the GD25:
bool getID() { 
uint32_t JEDEC = myIF.getJEDECID();
if (!JEDEC) {
Serial.println("No comms. Check wiring. Is chip supported? If unable to fix, raise an issue on Github");
return false;
}

The first part of the routine tries to determine the JEDEC ID. This is one of the most essential bits of information and can be answered by almost every flash chip - if this test fails, we are either dealing with non-compliant memory or (in the case of the GD25) with a catastrophic connection failure.
If the return of the ID is successful, additional attributes are obtained to enable further reflection as per the following snippet:

else { 
Serial.print("JEDEC ID: 0x");
Serial.println(JEDEC, HEX);
Serial.print("Man ID: 0x");
Serial.println(uint8_t(JEDEC >> 16), HEX);
Serial.print("Memory ID: 0x");
Serial.println(uint8_t(JEDEC >> 8), HEX);
Serial.print("Capacity: ");
Serial.println(myIF.getCapacity());
Serial.print("Max Pages: ");
Serial.println(myIF.getMaxPage());
}
return true;
}

Bringing this method to life requires an empty loop. In setup, the following elements are needed:

void setup() 
{ while (!Serial);
Serial.begin(9600);
delay(500); myIF.begin(MB(16));
getID();
}

While the begin method of the library can auto-detect many chips, practical experience says that passing in the dimensions via the macro MB yields more reliable results. After the initialization of the interface, the invocation of getID() ensures that test data will be emitted. Running the program yields the output of a wide variety of status information.

Reading and Writing Information

Flash memory has a limited amount of write cycles - in the case of the GD25 part used here, the datasheet contains the cycle-robustness information shown in the figure.


Reading and writing information in Flash memory - writing cycles on GD25

Figure.

For test code, this is significant as reducing the write load on individual cells is highly recommended. One way to achieve this is shown in the following steps - we determine a randomized address every time the program is run:

void charTest() {
uint32_t addr;
int8_t _data, _d;
_d = -255;

addr = random(0, 0xFFFFF);

After that, a test run is performed. It starts by erasing a sector, then stores information and finally reads it back:

Serial.print ("Char test write"); 
myIF.eraseSector(addr);
Serial.println( myIF.writeChar(addr, _d, true));
delay(100);

Serial.println(myIF.error(true));
Serial.println ("Char test write done");
_data = myIF.readChar(addr);

Serial.print ("Char test did");
if (_data == _d) {
Serial.println ("PASS");
}
else {
Serial.println ("FAIL");
}

}

Executing this code requires a small modification to the setup method as per the following:

void setup() { 
while (!Serial);
Serial.begin(9600);
delay(500);
Serial.println("entering ");
myIF.begin(MB(16));
Serial.println("sniffing");
getID();

charTest();
}

If all connections are correct, the program is ready to run at this point. It will output PASS to the Arduino serial monitor, informing you that the test transmission against the GD 25 flash memory chip was a complete success.

Learn More

Should you feel inspired to experiment further with GigaDevice NOR flash memory, first consult the resources found at https://www.hackster.io/ing-tam-hanna/seeedstudio-grove-module-with-gd25-flash-memory-a44ec4 - this is the more detailed write-up, which also goes into detail in the PCB layout and provides Gerber files for one of the boards shown in the figure above. Furthermore, the video found at https://www.youtube.com/watch?v=emncseKUq8A provides a deep dive into the demands flash memory PCB layout places upon the design.

Conclusion and Outlook

Should a process-computer or microcontroller need additional remanent memory, a flash chip such as the GD25 is ideally suited. Their low price and the standard adherence mean that integration should not be a significant challenge.