In last week, bearSSL server in CORS thread and bearSSL client in OTA thread can already run simultaneously except the bitstream download & firmware download part as both of them write to flash memory at different addresses (the board gets rebooted). Therefore, a mutex (mutual exclusion) check is needed to make sure only one instance of flash is running over all threads at one time. Since the multithread process is provided by freeRTOS, we need to use mutex API of freeRTOS as well to manage the threads.
To use a mutex:
- Define a semaphor handle, it should be a global variable since we need it in different functions and threads.
SemaphoreHandle_t flashMutex = NULL;
- Create the mutex semaphor, instead of a binary semaphor(which is very similar but usually is used to implement synchronization)
flashMutex = xSemaphoreCreateMutex();
- Take the mutex token in flash open function, this will return pdFALSE if the mutex token cannot be obtained(acquired by other thread) even after a wait set by timeout, pdTRUE will be returned otherwise.
if( xSemaphoreTake( flashMutex, timeout ) != pdTRUE ) return error;
- Give back the mutex token in flash close function
How does it works? There is only one mutex token in all threads. A call to xSemaphoreTake() will try to obtain the mutex token, pdTRUE will be returned if success, then the process continues. If the mutex token is already acquired by other thread, then this thread will be blocked (this thread stuck right there, freeRTOS will run other thread) until the token is released by other thread or for a time duration timeout. If it can’t get the token after timeout, pdFALSE is returned. A call to xSemaphoreGive() will release the mutex token so that other thread/function can acquire it and continue. Hence, by taking the mutex token in flash open function and releasing it in flash close function, we made sure only one instance of flash is running at one time. A second call to flash open function before flash close will cause the second thread to be blocked until flash close is done in first thread.
After the mutex check is implemented, the confliction between bitstream download in CORS and firmware download in OTA is solved, these two threads can run at same time.
Now, let’s move on to the firmware update at run-time. The previous method is:
- Ask the tftp server if there is a new update available, if so, download it via tftp.
- Write the newly downloaded firmware on upper aliased program flash.
- Checksum to make sure the downloaded firmware is intact. If not, just skip all the following step and wait for next time the task is run.
- Duplicate other data stored in upper aliased program flash to the corresponding address in lower aliased program flash.
- Swap flash, the upper aliased program flash become lower aliased and vice versa.
- Restart the software which should be the newly downloaded firmware.
But this previous implementation has a problem. The PFSWAP bit is cleared on POR (Power on Reset). Meaning at start, the code in PFM1 will be run. If the current firmware is in PFM2, then new firmware will be placed in PFM1. If this new firmware is somehow corrupted, then at restart it would not be able to swap flash and run the code in PFM2.
Therefore, I was assigned to try to work on a bootloader that resides in BFM (Boot Flash Memory) which run after startup code and before main() in PFM to decide which PFM to be lower aliased.
In order to utilize the BFM, I will have to build a new project with a customized linker script to generate the hex file that places my code on the BFM, and then, add this project as loadable into our RED6 project, customize RED6’s linker script as well so that there is no confliction over the memory space, especially the startup code which MPLABX generate for us by default, to use our own bootloader, I need to disable the default startup code.
Project Properties -> xc32-ld -> Option categories = Libraries -> ‘Do not link startup code’
*the default linker script is located at microchip/xc32/v2.05/pic32mx/lib/proc/32MZ2048EFG064/p32MZ2048EFG064.ld
One guy who also don’t like the idea of having two project mark all his bootloader related function with __attribute__ ((section (“.bootload”))) so that these codes will lies on the area he specified in linker script, but the problem is the “real” main function will lay on random address as the linker put it. Hence, he utilizes an empty IVT to keep the address of his main function so that he can jump to it.
It is discussed in this link : https://www.microchip.com/forums/m624720.aspx
After studies about it, the customized linker script and startup code for a few days, I built a project that run in BFM, I read the flash execution memory on MPLABX to make sure the code lies on where I want. But I think it introduces too much of complexity into our project, which is definitely not a good thing especially as this is not a one-man project.
After talking with Dr. Shawn, we decided to go with another approach.
- Always keep the working code in PFM1.
- The downloaded firmware always writes on PFM2.
- After the firmware is downloaded and checksum is OK, do swap flash to run the new firmware on PFM2. If checksum is faulty, then the firmware in PFM2 is corrupted, but it is ok since we will not run it.
- During initialisation we check the PFSWAP bit, if it is set, then we are running on PFM2. Thus, we duplicate the firmware in PFM2 to PFM1, and then swap flash again, then we are running the new firmware in PFM1.
Besides solving the issue mentioned before, this approach does not require the duplicate of data we stored in flash, which means the corresponding space in PFM1 is freed (512Kbyte) to serve for other purposes.