Lesson 010: QSPI Flash

In today’s lesson, we will be covering the use of the QPSI Flash module on the SK-S7G2.

First, we will need to configure the switches on the development kit.

Set all switches on the board to OFF, then turn on the following:

S5-1: DRAM
S5-2: QPSI
S5-4: PMOD
S5-6: JTAG

Create a new synergy project and the following modules:

QPSI Driver
UART Driver

You can leave the defaults for the QPSI Driver, but you need to configure the UART driver to work properly.

  1. Remove the DTC Driver for both transmission and reception.
  2. Click on the SCI Common and enable Asynchronous Mode
  3. Click on the UART Driver and change:
    1. Module Name: g_uart
    2. ICU: All to Priority 15
  4. Click on Generate Project

In order to get printf working, we also need to configure the project.

  1. Right click on the new project and select properties
  2. Open the C/C++ build
  3. Click on Settings
  4. Under Cross ARM C Linker, check the following:
    1. Use newlib-nano
    2. Use float with nano printf
    3. Use float with nano scanf
  5. Click OK

We will need a couple of defines and a global variable for printf. Add the following code below the first #include

#include <stdio.h>
#include <string.h>

// Buffer Size
#define OUTPUT_BUFFER_SIZE 1024

// Buffers
char outputBuffer[OUTPUT_BUFFER_SIZE];

// Flags
volatile bool transmitComplete;

Next, to get printf to print out to the serial port, add the following code:

int _write(int file, char *buffer, int count);
int _write(int file, char *buffer, int count)
{
    // As far as I know, there isn't a way to retrieve how many
    // bytes were send on using the uart->write function if it does not return
    // SSP_SUCCESS (unless we want to use the tx interrupt function and a global counter
    // so, we will send each character one by one instead.
    int bytesTransmitted = 0;

    for (int i = 0; i < count; i++)     {         // Start Transmission         transmitComplete = false;         g_uart.p_api->write (g_uart.p_ctrl, (uint8_t const *) (buffer + i), 1);
        while (!transmitComplete)
        {
        }

        bytesTransmitted++;
    }

    return bytesTransmitted;
}

// Callback Function for UART interrupts
void user_uart_callback(uart_callback_args_t * p_args)
{
    // Get Event Type
    switch (p_args->event)
    {
        // Transmission Complete
        case UART_EVENT_TX_COMPLETE:
            transmitComplete = true;
        break;
        default:
        break;
    }
}

The hal_entry function will need to do several things.

First, we will create and initialize variables to hold the data we will be reading and writing to the QPSI flash. We will also need to open the UART and QPSI flash drivers.

Finally we can read and write the the QSPI flash.

Here’s the full code. See below the code for an explanation.

void hal_entry(void)
{
    uint8_t *deviceAddress = (uint8_t *) 0x60000000;

    bool isWriting;
    uint8_t writeData[] = "The quick brown fox jumps over the lazy dog.";
    uint8_t readData[sizeof(writeData)];
    uint8_t initialData[1024];

    // Open UART
    g_uart.p_api->open (g_uart.p_ctrl, g_uart.p_cfg);

    // Disable Output Buffering
    setvbuf ( stdout, NULL, _IONBF, OUTPUT_BUFFER_SIZE);

    // Open QSPI
    g_qspi.p_api->open (g_qspi.p_ctrl, g_qspi.p_cfg);

    // Use TTY100 commands to clear screen and reset screen pointer
    printf ("\033[2J"); // Clear Screen
    printf ("\033[H"); // Return Home
    printf ("\033[3J"); // Clear Back Buffer

    // Print Header
    printf ("Lesson 010: QSPI Flash\r\n\r\n");

    // Read Initial Flash Data
    g_qspi.p_api->read (g_qspi.p_ctrl, deviceAddress, (uint8_t *) initialData, sizeof(initialData));
    printf ("Initial Flash Data: '%s'\r\n", initialData);

    // Erasing Flash
    printf ("Erasing Flash\r\n");
    g_qspi.p_api->sectorErase (g_qspi.p_ctrl, deviceAddress);

    // Wait for flash to be idle (not writing)
    printf ("Waiting for Flash to be idle...");
    while (true)
    {
        g_qspi.p_api->statusGet (g_qspi.p_ctrl, &isWriting);
        if (isWriting)
            continue;
        break;
    }
    printf ("OK!\r\n\r\n");

    // Write data to flash
    printf ("Writing '%s' to Flash.\r\n\r\n", writeData);
    g_qspi.p_api->pageProgram (g_qspi.p_ctrl, deviceAddress, writeData, sizeof(writeData));

    // Wait for flash to be idle (not writing)
    printf ("Waiting for Flash to be idle...");
    while (true)
    {
        g_qspi.p_api->statusGet (g_qspi.p_ctrl, &isWriting);
        if (isWriting)
            continue;
        break;
    }
    printf ("OK!\r\n\r\n");

    // Read Flash Data
    g_qspi.p_api->read (g_qspi.p_ctrl, deviceAddress, (uint8_t *) readData, sizeof(readData));
    printf ("Flash Data: '%s'\r\n", readData);

    // Close Flash
    g_qspi.p_api->close (g_qspi.p_ctrl);

    // Endless Loop
    while (true)
    {
    }

}

Initially, the variable deviceAddress doesn’t seem to make a whole lot of sense, especially when you learn that this address will actually write the address 0 in the QPSI flash.

The SSP code seems to be written with the idea of using Execute In Place (XIP) on the flash. XIP basically lets you write very large programs and execute them from an external flash device instead of the microcontroller. For a better description of XIP, please see the Wikipedia article https://en.wikipedia.org/wiki/Execute_in_place.

So, the device address is declared as a pointer that points to a uint8_t and is set to memory address 0x60000000. What’s at memory address 0x60000000? Who knows! And we don’t care. If you look at the generated code in r_qpsi.c for any of the QPSI read/write operations, you can see that they do some pointer math. Basically they subtract BSP_PRV_QSPI_DEVICE_PHYSICAL_ADDRESS (which is defined as 0x60000000). So, if you wanted to write to the flash address 0, you would set the deviceAddress to 0x60000000.

Writing to address 1, you would set the device address to 0x60000001, etc. Make sure you understand what’s going on before you delve deeper into writing to flash!

After creating some variables to hold the data we want to write, as well as to buffers to hold the data read back from the flash (the data before we write to the flash, and the data we read after writing to the flash), we open the UART, disable printf output buffering, open the QPSI, and then start working with the flash.

 // Read Initial Flash Data
 g_qspi.p_api->read (g_qspi.p_ctrl, deviceAddress, (uint8_t *) initialData, sizeof(initialData));
 printf ("Initial Flash Data: '%s'\r\n", initialData);

These lines read 1024 bytes from the Flash, starting at address 0 and store them into the initialData variable.

We then print out the contents of the variable as a string. Since we don’t know what data is in the flash, the first time running this example will probably give you weird printf results.

The line is here so that after we write data to the flash, we can power cycle the board. Since flash is non-volatile, it will store the data after power cycling.

The next step is to perform an erase on the address we want to write to. This flash device does not allow you to write over data. Instead, you much perform an erase operation. The SSP API call erases an entire sector (64KB). This is important, as it means that all data within that 64KB will be erased. If you want to write to a subsection of the 64KB, you must first read the 64KB, perform an erase on the sector, overwrite the data inside the memory buffer, then write the sector.

For our example, we don’t want to preserve any data, so we just perform an erase.

While the flash device is writing data or erasing data, you cannot perform any other write operations. Do make sure the device is ready to be written too, we must check the write status of the flash.

The following lines of code perform the erase operation and wait until the flash device is ready for the next write command.

    // Write data to flash
    printf ("Writing '%s' to Flash.\r\n\r\n", writeData);
    g_qspi.p_api->pageProgram (g_qspi.p_ctrl, deviceAddress, writeData, sizeof(writeData));

    // Wait for flash to be idle (not writing)
    printf ("Waiting for Flash to be idle...");
    while (true)
    {
        g_qspi.p_api->statusGet (g_qspi.p_ctrl, &isWriting);
        if (isWriting)
            continue;
        break;
    }
    printf ("OK!\r\n\r\n");

Finally, we can read the data back and display it to the UART device.

    // Read Flash Data
    g_qspi.p_api->read (g_qspi.p_ctrl, deviceAddress, (uint8_t *) readData, sizeof(readData));
    printf ("Flash Data: '%s'\r\n", readData);

    // Close Flash
    g_qspi.p_api->close (g_qspi.p_ctrl);

The last code segment is to sit in an endless loop.

    // Endless Loop
    while (true)
    {
    }

Full code is available at the GitHub (https://github.com/lycannon/ajwrs)

Advertisements

2 thoughts on “Lesson 010: QSPI Flash

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s