So I think I tracked down the reason for the fault occurring, and being more of an embedded C than C++ person I have some more digging and understanding to do.
Essentially the main.cpp declaration of the octal SPI block device causes the constructor code code to run before a array declarator, allocator and initializer in OSPIFBlockDevice runs before entry to main().
/* Init function to initialize Different Devices CS static list */
static PinName *generate_initialized_active_ospif_csel_arr();
// Static Members for different devices csel
// _devices_mutex is used to lock csel list - only one OSPIFBlockDevice instance per csel is allowed
SingletonPtr<PlatformMutex> OSPIFBlockDevice::_devices_mutex;
int OSPIFBlockDevice::_number_of_active_ospif_flash_csel = 0;
PinName *OSPIFBlockDevice::_active_ospif_flash_csel_arr = generate_initialized_active_ospif_csel_arr();
/********* Public API Functions *********/
/****************************************/
OSPIFBlockDevice::OSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, PinName sclk, PinName csel, PinName dqs,
int clock_mode, int freq)
: _ospi(io0, io1, io2, io3, io4, io5, io6, io7, sclk, csel, dqs, clock_mode), _csel(csel), _freq(freq),
_init_ref_count(0),
_is_initialized(false)
{
_unique_device_status = add_new_csel_instance(csel);
Essentially, I have found that the functionally allocated and initialized “active_ospif_flash_csel_arr” global variable gets called after the OSPIFBlockDevice constructor code gets executed. The following is the
static PinName *generate_initialized_active_ospif_csel_arr()
{
PinName *init_arr = new PinName[OSPIF_MAX_ACTIVE_FLASH_DEVICES];
for (int i_ind = 0; i_ind < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) {
init_arr[i_ind] = NC;
}
return init_arr;
}
int OSPIFBlockDevice::add_new_csel_instance(PinName csel)
{
int status = 0;
//!!!!
if(NULL == _active_ospif_flash_csel_arr) {
error("ERROR: OSPIFBlockDev.cpp _active_ospif_flash_csel_arr=0x%08x",
(unsigned)_active_ospif_flash_csel_arr);
}
//!!!!
_devices_mutex->lock();
if (_number_of_active_ospif_flash_csel >= OSPIF_MAX_ACTIVE_FLASH_DEVICES) {
status = -2;
goto exit_point;
}
// verify the device is unique(no identical csel already exists)
for (int i_ind = 0; i_ind < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) {
if (_active_ospif_flash_csel_arr[i_ind] == csel) {
status = -1;
goto exit_point;
}
}
// Insert new csel into existing device list
for (int i_ind = 0; i_ind < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) {
if (_active_ospif_flash_csel_arr[i_ind] == NC) {
_active_ospif_flash_csel_arr[i_ind] = csel;
break;
}
}
_number_of_active_ospif_flash_csel++;
exit_point:
_devices_mutex->unlock();
return status;
}
You will notice the my added NULL check and error() diagnostic in add_new_csel_instance() which prints and halts at that point. If I bypass the guts of add_new_csel_instance() where the NULL array pointer cause the initial fault, the generate_initialized_active_ospif_csel_arr() initialization function is subsequently called. I tried hard allocating and initializing the array with “NC” entries which gets me past the block device declaration. This defeats OSPIF_MAX_ACTIVE_FLASH_DEVICES mechanism.
The init() and parameters gleaned from the block device are as expected and writing and reading of blocks appears to work and check out. Declaring a LittleFileSystem on it and formatting seems to produce the “.” and “…” entries. Immediately rebooting produces a -9968 error on file system mount() which is the ENOENT or no entry error, so evidentially there is more issues.
Again, not being an experienced C++ developer I’m not sure what to make of this and how many other places this mechanism could be affecting the code. Functional initialization of global variables is not something I have any experience with.
The QSPIFBlockDevice seems to use the same mechanism. I have another project that uses QSPI but doesn’t exhibit this fault. It uses a custom target and an STM32H743 so I also don’t have access to a debugger.