Issues with debugging QSPI Flash

I’m having inconsistent issues writing to QSPI flash. If I watch the calls to Erase and Write (to the DSY_QSPI_BSS memory mapped buffer) in the debugger I can see sometimes the memory doesn’t appear to change. If I verify (with code) that the mapped flash memory matches the data I tried to write it fails, BUT, if I restart the debug session the memory has correctly updated. It’s like the memory mapped data is lagging behind. Do I need to flush the SPI somehow? There doesn’t appear to be an interface to do this.

Here’s a small code example that demonstrates the problem. To give some context, this is taken from a much larger program which uses the flash storage to save preset information. An 8 byte mask is written at the beginning of the stored data to signify if the data is valid.

#include "daisy_seed.h"
#include "daisysp.h"

using namespace daisy;

DaisySeed       hw;

constexpr size_t padded_size(size_t size, size_t segment_size)
{
    size_t padded_size = (size / segment_size) * segment_size;
    if( size % segment_size > 0 )
    {
        padded_size += segment_size;
    }
    
    return padded_size;
}

// store a single block of data, with valid data flag at the beginning, and preset data following
static constexpr uint32_t preset_valid_data_mask_base = 0x55555555; // alternate bits set (with space for version)
static constexpr uint32_t firmware_version = 3;
constexpr size_t preset_valid_data_flag_size = sizeof(preset_valid_data_mask_base);    
constexpr size_t preset_save_buffer_size = 104;
constexpr size_t flash_segment_size = 4*1024;
constexpr size_t preset_save_buffer_size_padding = padded_size(preset_save_buffer_size, flash_segment_size);

uint32_t DSY_QSPI_BSS _preset_save_buffer[preset_save_buffer_size_padding/sizeof(uint32_t)]; 

bool preset_flag_valid()
{
    const uint32_t preset_flag_1 = _preset_save_buffer[0];
    const uint32_t preset_flag_2 = _preset_save_buffer[1];

    return preset_flag_1 == preset_valid_data_mask_base && preset_flag_2 == firmware_version;
}

uint8_t* preset_save_buffer()
{
    return reinterpret_cast<uint8_t*>(&(_preset_save_buffer[0]));
}

bool save_to_flash(const uint8_t* src_data, uint8_t* dest_data, size_t size)
{
    auto verify = [](const uint8_t* src_data, uint8_t* dest_data, size_t size)
    {
        const uint8_t* src_data_end = src_data + size;
        while (src_data != src_data_end)
        {
            if( *src_data != *dest_data )
            {
                return false;
            }
            ++src_data;
            ++dest_data;
        }
        
        return true;
    };

    const size_t dest_addr = reinterpret_cast<size_t>(dest_data);
    const daisy::QSPIHandle::Result er_result = hw.qspi.Erase(dest_addr, dest_addr + padded_size(size,flash_segment_size));
    const daisy::QSPIHandle::Result wr_result = hw.qspi.Write(dest_addr, size, const_cast<uint8_t*>(src_data));

    if( er_result == daisy::QSPIHandle::Result::OK && wr_result == daisy::QSPIHandle::Result::OK )
    {
        if( verify(src_data, dest_data, size) )
        {
            return true;
        } 
    }

    return false;        
}

void Callback(AudioHandle::InputBuffer  in,
              AudioHandle::OutputBuffer out,
              size_t                    size)
{

}

int main(void)
{
    hw.Configure();
    hw.Init();
    hw.SetAudioBlockSize(4);

    hw.StartAudio(Callback); 

    hw.StartLog(true);

    // write an 8 byte flag to determine with the preset data is up to date
    uint32_t valid_flag[2];
    valid_flag[0] = preset_valid_data_mask_base;
    valid_flag[1] = firmware_version;

    bool success = save_to_flash(reinterpret_cast<uint8_t*>(&(valid_flag[0])), preset_save_buffer(), sizeof(valid_flag));

    if(success)
    {
        daisy::DaisySeed::PrintLine("Preset flag success");
    }
    else
    {
        daisy::DaisySeed::PrintLine("Preset flag fail");
    }

    // for test purposes write over the entire preset buffer with zero
    static uint32_t dummy_preset_data[preset_save_buffer_size/sizeof(uint32_t)];
    for( uint32_t i = 0; i < preset_save_buffer_size/sizeof(uint32_t); ++i )
    {
        dummy_preset_data[i] = 0;
    }

    const uint8_t* dummy_data_8 = reinterpret_cast<uint8_t*>(&(dummy_preset_data[0]));
    success = save_to_flash(dummy_data_8, preset_save_buffer(), preset_save_buffer_size);

    if(success)
    {
        daisy::DaisySeed::PrintLine("Preset success");
    }
    else
    {
        daisy::DaisySeed::PrintLine("Preset fail");
    }      
}

Output is:

Daisy is online

===============

Preset flag success

Preset fail

Most likely what you need to flush is cache that contains previous values. This can be done using this function. Despite it’s name, it doesn’t do anything related to DMA and simply invalidates cache, aligning requested addresses to cache line width.

2 Likes

I will try that, thanks!

That appears to have solved my issue, thanks!