Introduction
This article is a small explosion worth revisiting in the future. First, we will thoroughly understand the code flash and data flash of the A4T1 platform and provide a unified encapsulated function for operations.(We will not touch the OTP area as it is very dangerous according to official warnings.) In the future, operations on other platform flashes, such as Winbond SPI FLASH, can also be done this way.Then we will introduce a common embedded issue: changing blocking operations to non-blocking ones.Using a state machine + callback to turn it into asynchronous task execution.
Prerequisite Knowledge

Renesas A4T1 Internal Flash Operations
The code flash address is shown in the figure below, totaling 128K, which is (8*8K) + (2*32K).
The red box in the figure below is the experimental address for this article.

The data flash address is shown in the figure below, totaling 4K, which is (64*64 bytes).
The red box in the figure below is the experimental address for this article.

Direct library function operations on code flash.Direct library function operations on data flash.The test environment for this article is the above-mentionedD:\OTA\BOOT project The hardware only needs to connect MY89003 with JLINK.
Encapsulated Functions
Three library functions will be encapsulated once, meaning that the APP will no longer call the library functions but will call the encapsulated functions as follows:
jq_ra4t1_inner_flash_read
jq_ra4t1_inner_flash_write
jq_ra4t1_inner_flash_erase
It must be noted that the three functions opened this time can be considered to have a low level of abstraction (they are blocking). They only solve the universal write issue. Further improvements can be made to turn them into asynchronous calls (non-blocking).

From memcpy to jq_ra4t1_inner_flash_read


From R_FLASH_HP_Erase to jq_ra4t1_inner_flash_erase


From R_FLASH_HP_Write to jq_ra4t1_inner_flash_write

The blue box indicates blocking. It can be omitted in actual tests, but logically it is better to write it.
Real Machine Testing

Open the above-mentionedD:\OTA\BOOT project
Modify according to the figure below. The complete code is at the end of the article.
First, test code flash successfully (testing the last 8K block).What is granularity? Two types>
> One is Erase, which means erasing 8K or 32K (after erasing, it is expected to be all F, but not necessarily).
> The other is Write, which means calling R_FLASH_HP_Write must pass parameters that are multiples of 128.
This troublesome matter is encapsulated in jq_ra4t1_inner_flash_write.
Then test data flash, which fails (testing the first 64-byte block).This was actually expected 
Because it has been known that data flash cannot disable interrupts. It requires interrupts + callbacks to work together. The test code (at the end of the article) is in the style of code flash, which is blocking and disables interrupts, so the failure is quite normal!To unify the style, we found an astonishing setting! Renesas is really meticulous!The key is shown in the figure below: Disable the background operation mode of data flash.
This does not allow data flash to run in the background, which means it will work in a blocking synchronous mode, just like code flash.What is granularity? Two types>
> One is Erase, which means erasing 64 bytes (after erasing, it is expected to be all F, but not necessarily).
> The other is Write, which means calling R_FLASH_HP_Write must pass parameters that are multiples of 4.
This troublesome matter is encapsulated in jq_ra4t1_inner_flash_write.
Followed Follow Replay Share Like Watch MoreEngineer Jinhua
0/0
00:00/04:26Progress bar, 0 percentPlay00:00/04:2604:26Fullscreen Playing at 0.5x speed 0.5x 0.75x 1.0x 1.5x 2.0x HD Smooth
Continue Watching
Renesas A4T1 Internal Flash Operations – Unified API Layer Packaging
Reprint, Renesas A4T1 Internal Flash Operations – Unified API Layer PackagingEngineer JinhuaAdded to Top StoriesEnter comment Video Details
Summary
This article has completed the encapsulation of library functions (mainly writing).And the operation style is unified (mainly the data flash style should be the same as code flash).Please compare the code at the end of the article.The current similar effect of this article is the flash universal write function.

Future Upgrade Plans
The situation is as follows: The effect demonstrated in this article is that writing flash is blocking.
However, in many scenarios, the APP does not want blocking due to real-time requirements.
For example, can you write flash in an interrupt?
It is not possible to call the encapsulated functions in this article because they are blocking! How to solve this?
Please see the topic #Asynchronous
Transform synchronous calls into asynchronous calls – then use mpsc.h to write flash.
Resolve the issue of the timer linked list not being able to printf.
This article can also be modified accordingly.
Moreover, the code below has already been pre-embedded with callbacks.
The main idea is:
> Create a task A that runs a state machine. If there is data in the FIFO, it works; if there is no data, it rests.
> The APP or interrupt needs to write flash by throwing data or seq into the FIFO.
> When task A’s state machine completes a data or seq, it will callback to notify the APP.
This article will not practice this for now, and currently, it is also a bare metal connection without opening the hardware timer, which is inconvenient to say.

In simple terms,
After the modification, the exposed API functions
are no longer library functions
nor the three functions of this article
but rather an API function that you abstract and encapsulate one more layer, which is asynchronous and non-blocking!
Code Display

Code for testing code flash
typedef void (*jq_flash_complete_callback_t)(void *user_data, int result);
void flash_init(void){ /* Open the flash lp instance. */ err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg); assert(FSP_SUCCESS == err);}
/* * @param rAddr read address * @param rBuf read buffer * @param rLen read len * @param cb read complete callback function * @param user_data user data * @return int 0 success, other failed */ static int jq_ra4t1_inner_flash_read ( size_t rAddr, void *rBuf, size_t rLen, jq_flash_complete_callback_t cb, void *user_data) {
memcpy (rBuf, rAddr, rLen); if (cb) cb (user_data,0);
return 0; }
/** * @brief flash write function * * * @param wAddr write address * @param wBuf write buffer * @param wLen write len * @param cb write complete callback function * @param user_data user data * @return int 0 success, other failed */static int jq_ra4t1_inner_flash_write ( size_t wAddr, void *wBuf, size_t wLen, jq_flash_complete_callback_t cb, void *user_data){ int err = 0; int i = 0; uint32_t programCount; uint32_t programEndLen; uint32_t off; uint32_t len = 0; flash_status_t status;
#define MIN_WRITE_NUM 128 uint8_t alignBuf[MIN_WRITE_NUM];
/** 128byte align */ programCount = wLen / MIN_WRITE_NUM; programEndLen = wLen % MIN_WRITE_NUM; off = 0; __disable_irq(); if (programCount) { len = programCount * MIN_WRITE_NUM;//len must be a multiple of MIN_WRITE_NUM! err = R_FLASH_HP_Write(&g_flash0_ctrl, wBuf, wAddr, len);
/* Actually, it can be omitted. Wait until the current flash operation completes. */do { err = R_FLASH_HP_StatusGet(&g_flash0_ctrl, &status); } while ((FSP_SUCCESS == err) && (FLASH_STATUS_BUSY == status));
off += len; }
if ((err == 0) && programEndLen) { len = MIN_WRITE_NUM;//len must be a multiple of MIN_WRITE_NUM! memcpy (alignBuf,wBuf + off,programEndLen); err = R_FLASH_HP_Write(&g_flash0_ctrl, alignBuf, wAddr + off, len);
/* Actually, it can be omitted. Wait until the current flash operation completes. */do { err = R_FLASH_HP_StatusGet(&g_flash0_ctrl, &status); } while ((FSP_SUCCESS == err) && (FLASH_STATUS_BUSY == status));
} __enable_irq(); if (0 == err) { if (cb) cb(user_data,0); return 0; }
}
/** * @brief flash erase function * * * @param wAddr erase address * @param wLen erase len * @param cb write complete callback function * @param user_data user data * @return int 0 success, other failed */static int jq_ra4t1_inner_flash_erase( size_t eAddr, size_t eLen, jq_flash_complete_callback_t cb, void *user_data){ int err = -1; uint8_t block_num;
block_num = (eLen + FLASH_HP_CF_BLOCK_SIZE_8KB - 1) / FLASH_HP_CF_BLOCK_SIZE_8KB; LOG("jq_ra4t1_inner_flash_erase block_num=%d\r\n",block_num); __disable_irq(); err = R_FLASH_HP_Erase( &g_flash0_ctrl, (eAddr / FLASH_HP_CF_BLOCK_SIZE_8KB) * FLASH_HP_CF_BLOCK_SIZE_8KB, block_num); __enable_irq(); if (0 == err) { if (cb) cb(user_data,0); return 0; }
return err;}
void x_callback (void *user_data, int result){ LOG("x_callback result=%d\r\n",result);}
uint8_t g_read_uint8[128]={0};uint8_t g_write_uint8[4]={0x11,0x22,0x33,0x44};void flash_test(void){
#define TEST_ADDRESS FLASH_HP_CF_BLOCK_7//Initialize OPEN flash_init();//Read jq_ra4t1_inner_flash_read (TEST_ADDRESS, g_read_uint8, 128, x_callback, NULL);
LOG_ARRY("g_src_uint8",g_read_uint8,10);
//Erase jq_ra4t1_inner_flash_erase (TEST_ADDRESS, 128, x_callback, NULL);
//Write jq_ra4t1_inner_flash_write (TEST_ADDRESS, g_write_uint8, 4,//---Here cannot be 128! Because it is encapsulated internally! x_callback, NULL);
//Read jq_ra4t1_inner_flash_read (TEST_ADDRESS, g_read_uint8, 128, x_callback, NULL);
LOG_ARRY("g_src_uint8",g_read_uint8,10);
}
Code for testing data flash
typedef void (*jq_flash_complete_callback_t)(void *user_data, int result);
void flash_init(void){ /* Open the flash lp instance. */ err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg); assert(FSP_SUCCESS == err);}
/* * @param rAddr read address * @param rBuf read buffer * @param rLen read len * @param cb read complete callback function * @param user_data user data * @return int 0 success, other failed */ static int jq_ra4t1_inner_flash_read ( size_t rAddr, void *rBuf, size_t rLen, jq_flash_complete_callback_t cb, void *user_data) {
memcpy (rBuf, rAddr, rLen); if (cb) cb (user_data,0);
return 0; }
/** * @brief flash write function * * * @param wAddr write address * @param wBuf write buffer * @param wLen write len * @param cb write complete callback function * @param user_data user data * @return int 0 success, other failed */static int jq_ra4t1_inner_flash_write ( size_t wAddr, void *wBuf, size_t wLen, jq_flash_complete_callback_t cb, void *user_data){ int err = 0; int i = 0; uint32_t programCount; uint32_t programEndLen; uint32_t off; uint32_t len = 0; flash_status_t status;
#define MIN_WRITE_NUM 4 uint8_t alignBuf[MIN_WRITE_NUM];
/** 4byte align */ programCount = wLen / MIN_WRITE_NUM; programEndLen = wLen % MIN_WRITE_NUM; off = 0; __disable_irq(); if (programCount) { len = programCount * MIN_WRITE_NUM;//len must be a multiple of MIN_WRITE_NUM! err = R_FLASH_HP_Write(&g_flash0_ctrl, wBuf, wAddr, len);
/* Actually, it can be omitted. Wait until the current flash operation completes. */do { err = R_FLASH_HP_StatusGet(&g_flash0_ctrl, &status); } while ((FSP_SUCCESS == err) && (FLASH_STATUS_BUSY == status));
off += len; }
if ((err == 0) && programEndLen) { len = MIN_WRITE_NUM;//len must be a multiple of MIN_WRITE_NUM! memcpy (alignBuf,wBuf + off,programEndLen); err = R_FLASH_HP_Write(&g_flash0_ctrl, alignBuf, wAddr + off, len);
/* Actually, it can be omitted. Wait until the current flash operation completes. */do { err = R_FLASH_HP_StatusGet(&g_flash0_ctrl, &status); } while ((FSP_SUCCESS == err) && (FLASH_STATUS_BUSY == status));
} __enable_irq(); if (0 == err) { if (cb) cb(user_data,0); return 0; }
}
/** * @brief flash erase function * * * @param wAddr erase address * @param wLen erase len * @param cb write complete callback function * @param user_data user data * @return int 0 success, other failed */static int jq_ra4t1_inner_flash_erase( size_t eAddr, size_t eLen, jq_flash_complete_callback_t cb, void *user_data){ int err = -1; uint8_t block_num;
block_num = (eLen + FLASH_HP_DF_BLOCK_SIZE_64B - 1) / FLASH_HP_DF_BLOCK_SIZE_64B; LOG("jq_ra4t1_inner_flash_erase block_num=%d\r\n",block_num); __disable_irq(); err = R_FLASH_HP_Erase( &g_flash0_ctrl, (eAddr / FLASH_HP_DF_BLOCK_SIZE_64B) * FLASH_HP_DF_BLOCK_SIZE_64B, block_num); __enable_irq(); if (0 == err) { if (cb) cb(user_data,0); return 0; }
return err;}
void x_callback (void *user_data, int result){ LOG("x_callback result=%d\r\n",result);}
uint8_t g_read_uint8[64]={0};uint8_t g_write_uint8[5]={0x55,0x66,0x77,0x88};void flash_test(void){
#define TEST_ADDRESS FLASH_HP_DF_BLOCK_0//Initialize OPEN flash_init();//Read jq_ra4t1_inner_flash_read (TEST_ADDRESS, g_read_uint8, 64, x_callback, NULL);
LOG_ARRY("g_src_uint8",g_read_uint8,10);
//Erase jq_ra4t1_inner_flash_erase (TEST_ADDRESS, 64, x_callback, NULL);
//Write jq_ra4t1_inner_flash_write (TEST_ADDRESS, g_write_uint8, 5,//---Here cannot be 64! Because it is encapsulated internally! x_callback, NULL);
//Read jq_ra4t1_inner_flash_read (TEST_ADDRESS, g_read_uint8, 128, x_callback, NULL);
LOG_ARRY("g_src_uint8",g_read_uint8,10);
}