void write_flash_row() { size_t offset; uint8_t i; uint32_t prog_addr = write_address; NVMCON = 0x4001; TBLPAG = prog_addr >> 16; offset = prog_addr & 0xffff; /* Write the data provided */ for (i = 0; i < write_length; i++) { __builtin_tblwtl(offset, prog_buf[i]); __builtin_tblwth(offset, prog_buf[++i]); offset += 2; } /* Pad the rest of the row out with 0xff */ for (; i < BUFFER_LENGTH; i += 2) { __builtin_tblwtl(offset, 0xffff); __builtin_tblwth(offset, 0xffff); offset += 2; } asm("DISI #5"); __builtin_write_NVM(); while (NVMCONbits.WR == 1) ; }
void flashProgramInstructionRow ( u32 * wordAddress, const u32 * instructions ) { s16 i; u32 temp = *wordAddress; u32 offset; DWORD_VAL value; temp = FLASH_ROW( temp ); /* normalize to beginning of row */ offset = temp & FLASH_TABLE_PAGE_SIZE_MASK; /* offset into table page */ TBLPAG = temp >> FLASH_TABLE_PAGE_SIZE_SHIFT; /* select table page */ /* a complete row is written -> update wordAddress */ *wordAddress = temp + FLASH_ROW_SIZE_IN_WORDS; /* perform write of complete row */ for ( i = 0; i < FLASH_ROW_SIZE_IN_BYTES; i += 4, instructions++, offset += 2 ) { value.Val = *instructions; __builtin_tblwtl( offset, value.word.LW ); __builtin_tblwth( offset, value.word.HW ); } NVMCON = FLASH_ACCESS_ROW_PROGRAM; asm("DISI #5"); __builtin_write_NVM(); while(NVMCONbits.WR == 1); }
void WriteFlashSubBlock(void) //Use word writes to write code chunks less than a full 64 byte block size. { unsigned int i = 0; DWORD_VAL Address; NVMCON = 0x4003; //Perform WORD write next time WR gets set = 1. while(BufferedDataIndex > 0) //While data is still in the buffer. { Address.Val = ProgrammedPointer - BufferedDataIndex; TBLPAG = Address.word.HW; __builtin_tblwtl(Address.word.LW, ProgrammingBuffer[i]); //Write the low word to the latch __builtin_tblwth(Address.word.LW, ProgrammingBuffer[i + 1]); //Write the high word to the latch (8 bits of data + 8 bits of "phantom data") i = i + 2; asm("DISI #16"); //Disable interrupts for next few instructions for unlock sequence __builtin_write_NVM(); while(NVMCONbits.WR == 1){} BufferedDataIndex = BufferedDataIndex - 2; //Used up 2 (16-bit) words from the buffer. } NVMCONbits.WREN = 0; //Good practice to clear WREN bit anytime we are not expecting to do erase/write operations, further reducing probability of accidental activation. }
void flashErasePage ( u32 wordAddress ) { u32 offset = wordAddress & FLASH_TABLE_PAGE_SIZE_MASK; /* offset into table page */ TBLPAG = wordAddress >> FLASH_TABLE_PAGE_SIZE_SHIFT; /* select table page */ __builtin_tblwtl( offset, 0x0000 /* value is don't care */ ); NVMCON = FLASH_ACCESS_PAGE_ERASE; asm volatile ( "disi #5" ); __builtin_write_NVM(); /* unlock sequence + start erase cycle */ while(NVMCONbits.WR == 1); }
/********************************************************************* * Function: unsigned int NVMWriteWord(UINT32 address, UINT32 data) * * Description: The word at the location pointed to by NVMADDR is programmed. * * PreCondition: None * * Inputs: address: Destination address to write. * data: Word to write. * * Output: '0' if operation completed successfully. * * Example: NVMWriteWord(0xBD000000, 0x12345678) ********************************************************************/ UINT NVMemWriteWord(UINT32 address, UINT32 data) { DWORD_VAL writeAddress; DWORD_VAL writeData; writeAddress.Val = address; writeData.Val = data; NVMCON = 0x4001; // Perform WORD write next time WR gets set = 1. NVMADRU = writeAddress.word.HW; NVMADR = writeAddress.word.LW; // Set the table address of "Latch". The data is programmed into the FLASH from a temporary latch. TBLPAG = 0xFA; // The smallest block of data that can be programmed in // a single operation is 2 instruction words (6 Bytes + 2 Phantom Bytes). // Mask the high or low instruction words depending on the address and write either high or low instruction word. if (address % 4) { __builtin_tblwtl(0, 0xFFFF); // Mask the low word of 1-st instruction into the latch. __builtin_tblwth(1, 0x00FF); // Mask the high word of 1-st instruction into the latch. (8 bits of data + 8 bits of "phantom data" (phantom byte is always 0)) __builtin_tblwtl(2, writeData.word.LW); // Write the low word of 2-nd instruction into the latch __builtin_tblwth(3, writeData.word.HW); // Write the high word of 2-nd instruction into the latch } else { __builtin_tblwtl(0, writeData.word.LW); // Write the low word of 1-st instruction into the latch __builtin_tblwth(1, writeData.word.HW); // Write the high word of 1-st instruction into the latch __builtin_tblwtl(2, 0xFFFF); // Mask the low word of 2-nd instruction into the latch. __builtin_tblwth(3, 0x00FF); // Mask the high word of 2-nd instruction into the latch. (8 bits of data + 8 bits of "phantom data" (phantom byte is always 0)) } INTCON2bits.GIE = 0; // Disable interrupts for next few instructions for unlock sequence __builtin_write_NVM(); while(NVMCONbits.WR == 1){} INTCON2bits.GIE = 1; // Re-enable the interrupts (if required). return NVMCONbits.WRERR; // Return WRERR state. }
void write_freq(WORD newData) { WORD offset; NVMCON = 0x4058; TBLPAG = __builtin_tblpage(&dat); offset = __builtin_tbloffset(&dat); __builtin_tblwtl(offset,0); asm volatile("disi #5"); __builtin_write_NVM(); while(NVMCONbits.WR == 1); NVMCON = 0x4004; TBLPAG = __builtin_tblpage(&dat); offset = __builtin_tbloffset(&dat); __builtin_tblwtl(offset,newData); asm volatile("disi #5"); __builtin_write_NVM(); while(NVMCONbits.WR == 1); }
BOOL FlashErasePage(DWORD address) { assert((address & 0x3FF) == 0); DWORD_VAL a = { address }; NVMCON = 0x4042; // Erase page TBLPAG = a.byte.UB; __builtin_tblwtl(a.word.LW, 0xFFFF); asm("DISI #5"); __builtin_write_NVM(); while(NVMCONbits.WR); NVMCONbits.WREN = 0; return NVMCONbits.WRERR == 0; }
void EraseFlash(void) { DWORD_VAL MemAddressToErase = {0x00000000}; MemAddressToErase.Val = (((DWORD)ErasePageTracker) << 10); NVMCON = 0x4042; //Erase page on next WR TBLPAG = MemAddressToErase.byte.UB; __builtin_tblwtl(MemAddressToErase.word.LW, 0xFFFF); asm("DISI #16"); //Disable interrupts for next few instructions for unlock sequence __builtin_write_NVM(); while(NVMCONbits.WR == 1){} // EECON1bits.WREN = 0; //Good practice now to clear the WREN bit, as further protection against any future accidental activation of self write/erase operations. }
BOOL FlashWriteDWORD(DWORD address, DWORD value) { assert((address & 0x1) == 0); DWORD_VAL a = { address }; DWORD_VAL v = { value }; NVMCON = 0x4003; // Word write TBLPAG = a.word.HW; __builtin_tblwtl(a.word.LW, v.word.LW); //Write the low word to the latch __builtin_tblwth(a.word.LW, v.word.HW); //Write the high word to the latch (8 bits of data + 8 bits of "phantom data") asm("DISI #5"); __builtin_write_NVM(); while (NVMCONbits.WR); NVMCONbits.WREN = 0; return NVMCONbits.WRERR == 0; }
/********************************************************************* * Function: unsigned int NVMErasePage(void* address) * * Description: A page erase will erase a single page of program flash, * which equates to 1k instructions (3KBytes). The page to * be erased is selected using NVMADDR. The lower bytes of * the address given by NVMADDR are ignored in page selection. * * PreCondition: None * * Inputs: address: Destination page address to Erase. * * Output: '0' if operation completed successfully. * * Example: NVMemErasePage(UINT32 0xBD000000) ********************************************************************/ UINT NVMemErasePage(UINT32 address) { DWORD_VAL eraseAddress; eraseAddress.Val = address; TBLPAG = eraseAddress.byte.UB; NVMADRU = eraseAddress.word.HW; NVMADR = eraseAddress.word.LW; __builtin_tblwtl(eraseAddress.word.LW, 0xFFFF); NVMCON = 0x4003; // Erase page on next WR INTCON2bits.GIE = 0; // Disable interrupts for next few instructions for unlock sequence __builtin_write_NVM(); while(NVMCONbits.WR == 1) {} INTCON2bits.GIE = 1; // Re-enable the interrupts (if required). return NVMCONbits.WRERR; // Return WRERR state. }
void flashProgram1Instruction ( u32 * wordAddress, u32 instruction ) { DWORD_VAL value; u32 temp = *wordAddress; u32 offset = temp & FLASH_TABLE_PAGE_SIZE_MASK; /* offset into table page */ TBLPAG = temp >> FLASH_TABLE_PAGE_SIZE_SHIFT; /* select table page */ /* a pic 24 word is 16-bits, we write 3-Bytes + phantom = 2 16-bit words */ *wordAddress += 2; value.Val = instruction; __builtin_tblwtl( offset, value.word.LW ); __builtin_tblwth( offset, value.word.HW ); NVMCON = FLASH_ACCESS_WORD_PROGRAM; asm("DISI #5"); __builtin_write_NVM(); while(NVMCONbits.WR == 1); }
void clear_flash() { uint32_t prog_addr = USER_REGION_BASE; size_t offset; /* Clear each flash block. TBLPAG/offset is set to the * base address (lowest address) of each block. */ while (prog_addr < USER_REGION_TOP) { TBLPAG = prog_addr >> 16; offset = prog_addr & 0xffff; __builtin_tblwtl(offset, 0x00); NVMCON = 0x4042; asm("DISI #5"); __builtin_write_NVM(); while (NVMCONbits.WR == 1) ; prog_addr += FLASH_BLOCK_SIZE; } }
BOOL FlashWriteBlock(DWORD address, BYTE block[192]) { assert((address & 0x7F) == 0); unsigned int i = 0; DWORD_VAL a = { address }; DWORD_VAL v; NVMCON = 0x4001; // Block write TBLPAG = a.word.HW; while (i < 192) { v.byte.LB = block[i++]; v.byte.HB = block[i++]; v.byte.UB = block[i++]; __builtin_tblwtl(a.word.LW, v.word.LW); // Write the low word to the latch __builtin_tblwth(a.word.LW, v.word.HW); // Write the high word to the latch (8 bits of data + 8 bits of "phantom data") a.word.LW += 2; } asm("DISI #5"); __builtin_write_NVM(); while (NVMCONbits.WR); NVMCONbits.WREN = 0; return NVMCONbits.WRERR == 0; }
/******************************************************************** * Function: void HandleCommand() * * Precondition: data in buffer * * Input: None. * * Output: None. * * Side Effects: None. * * Overview: Handles commands received from host * * Note: None. ********************************************************************/ void HandleCommand() { BYTE Command; BYTE length; //variables used in EE and CONFIG read/writes #if (defined(DEV_HAS_EEPROM) || defined(DEV_HAS_CONFIG_BITS)) WORD i=0; WORD_VAL temp; WORD bytesRead = 0; #endif Command = buffer[0]; //get command from buffer length = buffer[1]; //get data length from buffer //RESET Command if(length == 0x00){ UxMODEbits.UARTEN = 0; //Disable UART ResetDevice(userReset.Val); } //get 24-bit address from buffer sourceAddr.v[0] = buffer[2]; sourceAddr.v[1] = buffer[3]; sourceAddr.v[2] = buffer[4]; sourceAddr.v[3] = 0; #ifdef USE_RUNAWAY_PROTECT writeKey1 |= (WORD)sourceAddr.Val; // Modify keys to ensure proper program flow writeKey2 = writeKey2 << 1; #endif //Handle Commands switch(Command) { case RD_VER: //Read version buffer[2] = MINOR_VERSION; buffer[3] = MAJOR_VERSION; responseBytes = 4; //set length of reply break; case RD_FLASH: //Read flash memory ReadPM(length, sourceAddr); responseBytes = length*PM_INSTR_SIZE + 5; //set length of reply break; case WT_FLASH: //Write flash memory #ifdef USE_RUNAWAY_PROTECT writeKey1 -= length; // Modify keys to ensure proper program flow writeKey2 += Command; #endif WritePM(length, sourceAddr); responseBytes = 1; //set length of reply break; case ER_FLASH: //Erase flash memory #ifdef USE_RUNAWAY_PROTECT writeKey1 += length; // Modify keys to ensure proper program flow writeKey2 -= Command; #endif ErasePM(length, sourceAddr); responseBytes = 1; //set length of reply break; #ifdef DEV_HAS_EEPROM case RD_EEDATA: //Read EEPROM //if device has onboard EEPROM, allow EE reads //Read length words of EEPROM while(i < length*2){ temp.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW); buffer[5+i++] = temp.v[0]; buffer[5+i++] = temp.v[1]; sourceAddr.Val += 2; } responseBytes = length*2 + 5; //set length of reply break; case WT_EEDATA: //Write EEPROM #ifdef USE_RUNAWAY_PROTECT writeKey1 -= length; // Modify keys to ensure proper program flow writeKey2 += Command; #endif //Write length words of EEPROM while(i < length*2){ temp.byte.LB = buffer[5+i++]; //load data to write temp.byte.HB = buffer[5+i++]; WriteLatch(sourceAddr.word.HW,sourceAddr.word.LW, 0, temp.Val); //write data to latch #ifdef USE_RUNAWAY_PROTECT writeKey1++; writeKey2--; //setup program flow protection test keys keyTest1 = (((0x0009 | (WORD)(sourceAddr.Val-i)) - length) + i/2) - 5; keyTest2 = (((0x557F << 1) + WT_EEDATA) - i/2) + 6; //initiate write sequence WriteMem(EE_WORD_WRITE); writeKey1 += 5; // Modify keys to ensure proper program flow writeKey2 -= 6; #else //initiate write sequence bypasssing runaway protection WriteMem(EE_WORD_WRITE); #endif sourceAddr.Val +=2; } responseBytes = 1; //set length of reply break; #endif #ifdef DEV_HAS_CONFIG_BITS case RD_CONFIG: //Read config memory //Read length bytes from config memory while(bytesRead < length) { //read flash temp.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW); buffer[bytesRead+5] = temp.v[0]; //put read data onto buffer bytesRead++; sourceAddr.Val += 2; //increment addr by 2 } responseBytes = length + 5; break; case WT_CONFIG: //Write Config mem //Write length bytes of config memory while(i < length){ temp.byte.LB = buffer[5+i++]; //load data to write temp.byte.HB = 0; #ifdef USE_RUNAWAY_PROTECT writeKey1++; writeKey2--; #endif //Make sure that config write is inside implemented configuration space if(sourceAddr.Val >= CONFIG_START && sourceAddr.Val <= CONFIG_END) { TBLPAG = sourceAddr.byte.UB; __builtin_tblwtl(sourceAddr.word.LW,temp.Val); #ifdef USE_RUNAWAY_PROTECT //setup program flow protection test keys keyTest1 = (((0x0009 | (WORD)(sourceAddr.Val-i*2)) - length) + i) - 5; keyTest2 = (((0x557F << 1) + WT_CONFIG) - i) + 6; //initiate write sequence WriteMem(CONFIG_WORD_WRITE); writeKey1 += 5; // Modify keys to ensure proper program flow writeKey2 -= 6; #else //initiate write sequence bypasssing runaway protection WriteMem(CONFIG_WORD_WRITE); #endif }//end if(sourceAddr.Val...) sourceAddr.Val +=2; }//end while(i < length) responseBytes = 1; //set length of reply break; #endif case VERIFY_OK: #ifdef USE_RUNAWAY_PROTECT writeKey1 -= 1; // Modify keys to ensure proper program flow writeKey2 += Command; #endif WriteTimeout(); responseBytes = 1; //set length of reply break; default: break; }// end switch(Command) }//end HandleCommand()
BYTE MDD_IntFlash_SectorWrite(DWORD sector_addr, BYTE* buffer, BYTE allowWriteToZero) { #if !defined(INTERNAL_FLASH_WRITE_PROTECT) WORD i; BYTE j; WORD offset; DWORD flashAddress; WORD TBLPAGSave; //First, error check the resulting address, to make sure the MSD host isn't trying //to erase/program illegal LBAs that are not part of the designated MSD volume space. if(sector_addr >= MDD_INTERNAL_FLASH_TOTAL_DISK_SIZE) { return FALSE; } TBLPAGSave = TBLPAG; #if defined (__dsPIC33E__) || defined (__PIC24E__) // First, save the contents of the entire erase page. // To do this, we need to get a pointer to the start of the erase page. // AND mask 0xFFFFF800 is to clear the lower bits, // so we go back to the start of the erase page. flashAddress = ((DWORD)FILES_ADDRESS + (DWORD)(sector_addr*MEDIA_SECTOR_SIZE)) & (DWORD)0xFFFFF800; //Now save all of the contents of the erase page. TBLPAG = (BYTE)(flashAddress >> 16); for(i = 0; i < ERASE_BLOCK_SIZE;i++) { file_buffer[i] = __builtin_tblrdl((WORD)flashAddress + (2 * i)); } // Now we want to overwrite the file_buffer[] contents // for the sector that we are trying to write to. // The lower 2 bits of the helps to determine this. offset = 0x200 * (BYTE)(sector_addr & 0x3); //Overwrite the file_buffer[] RAM contents for the sector that we are trying to write to. for(i = 0; i < MEDIA_SECTOR_SIZE; i++) { *((unsigned char *)file_buffer + offset + i) = *buffer++; } #else //First, save the contents of the entire erase page. To do this, we need to get a pointer to the start of the erase page. flashAddress = ((DWORD)FILES_ADDRESS + (DWORD)(sector_addr*MEDIA_SECTOR_SIZE)) & (DWORD)0xFFFFFC00; //AND mask 0xFFFFFC00 is to clear the lower bits, so we go back to the start of the erase page. //Now save all of the contents of the erase page. for(i = 0; i < ERASE_BLOCK_SIZE;) { TBLPAG = (BYTE)(flashAddress >> 16); *(WORD*)&file_buffer[i] = __builtin_tblrdl((WORD)flashAddress); flashAddress += 2u; //Skipping upper word. Don't care about the implemented byte/don't use it when programming or reading from the sector. i += 2u; } //Now we want to overwrite the file_buffer[] contents for the sector that we are trying to write to. //Need to figure out if the buffer[] data goes in the upper sector or the lower sector of the file_buffer[] if(sector_addr & 0x00000001) { //Odd sector address, must be the high file_buffer[] sector offset = MEDIA_SECTOR_SIZE; } else { offset = 0; } //Overwrite the file_buffer[] RAM contents for the sector that we are trying to write to. for(i = 0; i < MEDIA_SECTOR_SIZE; i++) { file_buffer[offset + i] = *buffer++; } #endif #if defined(__dsPIC33E__) || defined (__PIC24E__) INT gieBkUp; //Now erase the entire erase page of flash memory. //First we need to calculate the actual flash memory //address of the erase page. gieBkUp = INTCON2bits.GIE; INTCON2bits.GIE = 0; // Disable interrupts NVMADRU = (WORD)(flashAddress >> 16); NVMADR = (WORD)(flashAddress & 0xFFFF); NVMCON = 0x4003; // This value will erase a page. __builtin_write_NVM(); INTCON2bits.GIE = gieBkUp; // Enable interrupts //Now reprogram the erase page with previously obtained contents of the file_buffer[] //We only write to the even flash word addresses, the odd word addresses are left blank. //Therefore, we only store 2 bytes of application data for every 2 flash memory word addresses. //This "wastes" 1/3 of the flash memory, but it provides extra protection from accidentally executing //the data. It also allows quick/convenient PSV access when reading back the flash contents. TBLPAG = 0xFA; j = 0; for(i = 0; i < ERASE_BLOCK_SIZE;i++) { // __builtin_tblwtl((j * 2), file_buffer[i]); __builtin_tblwth((j * 2), 0); j ++; //Check if we have reached a program block size boundary. If so, program the last 128 //useful bytes (192 bytes total, but 64 of those are filled with '0' filler bytes). if(j >= 128u) { j = j - 128u; NVMADRU = (WORD)(flashAddress >> 16); NVMADR = (WORD)(flashAddress & 0xFFFF); NVMCON = 0x4002; gieBkUp = INTCON2bits.GIE; INTCON2bits.GIE = 0; // Disable interrupts __builtin_write_NVM(); INTCON2bits.GIE = gieBkUp; // Enable interrupts flashAddress += 256; } }