It has been a week and would I hope I got a lot done. Well not so much. Got side tracked by tasks on other things. What I have been working on here is integrating the scripting language. The PIC32MX470F512 that I am using does not have any EEPROM data storage. So I have been testing different methods for storing the program script in Program FLASH.
While this should be straight forward, it was not for me. Looking at the forums, several other people got tangled up in virtual memory, physical memory, cached and non-cached. I wanted something simple. In the Brick Buddy, I had written a simple interface to the Data EEPROM. The cell endurance was a minimum of 100K erase/writes. Given the average usage, I did not see this as a concern. In this PIC32, the FLASH cell endurance is 20K. A lot, but low enough to have to consider it. Also I could erase and write individual bytes previously, in the PIC32 FLASH the minimum erase is 4096 and the minimum write is 512. I have decided to manipulate and run the script out of RAM, there is lots of this. The new script storage process, from a high level, looks like this:
- Read the stored script from FLASH into a buffer in RAM
- Modify, Add, Delete and run from the buffer
- Store the script in FLASH only on command, either USB or Bluetooth
The script will always be on the PC or the Bluetooth device (a change that is now needed). Unless someone is extreme on the saving, 20K cycles will last 3- 5 years ( 5 years-> 4000/year or more than 10 a day). I can also alocate a second 4096 block of Program FLASH and double the life. There will also be lots of Program FLASH when I am done. One big upside with 4096 as the minimum erase, is that means there is space for over 1000 instructions. Now I need space for other variables, but I can see setting the limit at 1000.
I took one of the Harmony examples, flash_modify, and did some major surgery by manually merging it into a project that had a system console and system debug. The biggest issue was dealing with the 512 byte write size, the flash addresses being in bytes and the script buffer being in unsigned longs (32 bit). The final issue was the virtual vs physical addresses and cached vs non-cached. I ended up using 0xBD070000 as the address and that works. Once I finish testing tomorrow, I will post the project for everyone to use on the MyMakerTools website. Look down the page for Other Goodies.
UPDATE:There was much discussion in the forums about using KSEG1 (non-cached) addresses instead of KSEG0 (cached) addresses. I tested it both ways and this code seems to work either way. Tracing through shows the low level PLIB routines use the compiler macro, KVA_TO_PA to ensure that the correct addresses are used.
Here is the basic state machine.
/**********************************************************
* FLASH NVM tasks routine. This function implements the
* FLASH NVM state machine.
***********************************************************/
void APP_NVM_Tasks ( void )
{
unsigned int x;
uint32_t bufOffset, addrOffset;
x = APP_DATABUFF_SIZE;
switch(appData.nvmState)
{
case APP_STATE_NVM_INIT:
appData.nvmHandle = DRV_FLASH_Open(DRV_FLASH_INDEX_0, intent);
appData.nvmState = APP_STATE_NVM_FILL_DATABUF_AND_ERASE_STATE;
appData.nvmRowCount = APP_DEVICE_ROW_COUNT;
PrintBuffer(true);
break;
case APP_STATE_NVM_FILL_DATABUF_AND_ERASE_STATE:
for (x = 0; x < APP_DATABUFF_SIZE; x++)
{
scriptBuffer[x] = x;
}
LED_BLOff();
LED_YLOff();
PrintBuffer(false);
/* Erase the page which consist of the row to be written */
DRV_FLASH_ErasePage(appData.nvmHandle, APP_PROGRAM_FLASH_BASE_ADDRESS);
appData.nvmCurrentRow = 0; // set row value to zero
appData.nvmState = APP_STATE_NVM_ERASE_COMPLETION_CHECK;
break;
case APP_STATE_NVM_ERASE_COMPLETION_CHECK:
if(!DRV_FLASH_IsBusy(appData.nvmHandle))
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
break;
case APP_STATE_NVM_WRITE_START:
/* Erase Success */
/* Write a row of data to PROGRAM_FLASH_BASE_ADDRESS, using databuff array as the source */
//PrintBuffer(true);
bufOffset = appData.nvmCurrentRow * APP_DEVICE_ROW_SIZE_DIVIDED_BY_4;
addrOffset = appData.nvmCurrentRow * APP_DEVICE_ROW_SIZE;
DRV_FLASH_WriteRow(appData.nvmHandle, APP_PROGRAM_FLASH_BASE_ADDRESS + addrOffset, &scriptBuffer[bufOffset]);
appData.nvmCurrentRow++;
appData.nvmState = APP_STATE_NVM_WRITE_COMPLETION_CHECK_AND_VERIFY_CHECK;
break;
case APP_STATE_NVM_WRITE_COMPLETION_CHECK_AND_VERIFY_CHECK:
if(!DRV_FLASH_IsBusy(appData.nvmHandle))
{
PrintBuffer(true);
/* Verify that data written to flash memory is valid (databuff array read from kseg1) */
if (!memcmp(&scriptBuffer[bufOffset], (void *)(APP_PROGRAM_FLASH_BASE_ADDRESS_VALUE + addrOffset), DRV_FLASH_ROW_SIZE))
{
appData.nvmState = APP_STATE_NVM_SUCCESS_STATE;
}
else
{
appData.nvmState = APP_STATE_NVM_ERROR_STATE;
}
}
break;
case APP_STATE_NVM_ERROR_STATE:
/*stay here, nvm had a failure*/
LED_BLOff();
LED_YLOn();
if (appData.nvmCurrentRow < APP_DEVICE_ROW_COUNT)
{
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
else
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_IDLE_STATE;
}
break;
case APP_STATE_NVM_SUCCESS_STATE:
LED_BLOn();
LED_YLOff();
if (appData.nvmCurrentRow < APP_DEVICE_ROW_COUNT)
{
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
else
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_IDLE_STATE;
}
break;
case APP_STATE_NVM_IDLE_STATE:
LED_BLToggle();
LED_YLOff();
break;
}
}
* FLASH NVM tasks routine. This function implements the
* FLASH NVM state machine.
***********************************************************/
void APP_NVM_Tasks ( void )
{
unsigned int x;
uint32_t bufOffset, addrOffset;
x = APP_DATABUFF_SIZE;
switch(appData.nvmState)
{
case APP_STATE_NVM_INIT:
appData.nvmHandle = DRV_FLASH_Open(DRV_FLASH_INDEX_0, intent);
appData.nvmState = APP_STATE_NVM_FILL_DATABUF_AND_ERASE_STATE;
appData.nvmRowCount = APP_DEVICE_ROW_COUNT;
PrintBuffer(true);
break;
case APP_STATE_NVM_FILL_DATABUF_AND_ERASE_STATE:
for (x = 0; x < APP_DATABUFF_SIZE; x++)
{
scriptBuffer[x] = x;
}
LED_BLOff();
LED_YLOff();
PrintBuffer(false);
/* Erase the page which consist of the row to be written */
DRV_FLASH_ErasePage(appData.nvmHandle, APP_PROGRAM_FLASH_BASE_ADDRESS);
appData.nvmCurrentRow = 0; // set row value to zero
appData.nvmState = APP_STATE_NVM_ERASE_COMPLETION_CHECK;
break;
case APP_STATE_NVM_ERASE_COMPLETION_CHECK:
if(!DRV_FLASH_IsBusy(appData.nvmHandle))
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
break;
case APP_STATE_NVM_WRITE_START:
/* Erase Success */
/* Write a row of data to PROGRAM_FLASH_BASE_ADDRESS, using databuff array as the source */
//PrintBuffer(true);
bufOffset = appData.nvmCurrentRow * APP_DEVICE_ROW_SIZE_DIVIDED_BY_4;
addrOffset = appData.nvmCurrentRow * APP_DEVICE_ROW_SIZE;
DRV_FLASH_WriteRow(appData.nvmHandle, APP_PROGRAM_FLASH_BASE_ADDRESS + addrOffset, &scriptBuffer[bufOffset]);
appData.nvmCurrentRow++;
appData.nvmState = APP_STATE_NVM_WRITE_COMPLETION_CHECK_AND_VERIFY_CHECK;
break;
case APP_STATE_NVM_WRITE_COMPLETION_CHECK_AND_VERIFY_CHECK:
if(!DRV_FLASH_IsBusy(appData.nvmHandle))
{
PrintBuffer(true);
/* Verify that data written to flash memory is valid (databuff array read from kseg1) */
if (!memcmp(&scriptBuffer[bufOffset], (void *)(APP_PROGRAM_FLASH_BASE_ADDRESS_VALUE + addrOffset), DRV_FLASH_ROW_SIZE))
{
appData.nvmState = APP_STATE_NVM_SUCCESS_STATE;
}
else
{
appData.nvmState = APP_STATE_NVM_ERROR_STATE;
}
}
break;
case APP_STATE_NVM_ERROR_STATE:
/*stay here, nvm had a failure*/
LED_BLOff();
LED_YLOn();
if (appData.nvmCurrentRow < APP_DEVICE_ROW_COUNT)
{
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
else
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_IDLE_STATE;
}
break;
case APP_STATE_NVM_SUCCESS_STATE:
LED_BLOn();
LED_YLOff();
if (appData.nvmCurrentRow < APP_DEVICE_ROW_COUNT)
{
appData.nvmState = APP_STATE_NVM_WRITE_START;
}
else
{
PrintBuffer(true);
appData.nvmState = APP_STATE_NVM_IDLE_STATE;
}
break;
case APP_STATE_NVM_IDLE_STATE:
LED_BLToggle();
LED_YLOff();
break;
}
}