From 7bbfc23c1af00a8673822dc3e69b9a450ffed1d5 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 15 Oct 2012 22:00:29 -0700 Subject: [PATCH] Added untested support for erasing a specific portion of the SIMM (in 256 KB increments). Won't be able to test it until I implement it in the control software as well, but I think it should work. --- external_mem.c | 90 +++++++++++++++++++++++++++++++++++++++++ external_mem.h | 2 + programmer_protocol.h | 20 ++++++++- usb_serial/usb_serial.c | 87 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 197 insertions(+), 2 deletions(-) diff --git a/external_mem.c b/external_mem.c index 0d75d1e..d3a9241 100644 --- a/external_mem.c +++ b/external_mem.c @@ -256,6 +256,91 @@ void ExternalMem_EraseChips(uint8_t chipsMask) ExternalMem_WaitCompletion(chipsMask); } +bool ExternalMem_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask) +{ + bool result = false; + + // Make sure the area requested to be erased is on 64 KB boundaries. + // True, the 2 MB SIMM doesn't require 64 KB boundaries, but I'm going to + // keep it to 2 MB boundaries to simplify everything. +#define ERASABLE_SECTOR_SIZE (64*1024UL*1024UL) + if ((address & ERASABLE_SECTOR_SIZE) || + (length & ERASABLE_SECTOR_SIZE)) + { + return false; + } + + // We're good to go. Let's do it. + + if (curChipType == ChipType8BitData_4MBitSize) + { +#define SECTOR_SIZE_4MBIT (128) + // This chip sucks because you have to erase each sector with its own + // complete erase unlock command, which can take a while. At least + // individual erase operations are much faster on this chip... + while (length) + { + // Start the erase command + ExternalMem_UnlockChips(chipsMask); + ExternalMem_WriteCycle(0x55555555UL, 0x80808080UL); + ExternalMem_UnlockChips(chipsMask); + + // Now provide a sector address, but only one. Then the whole + // unlock sequence has to be done again after this sector is done. + ExternalMem_WriteCycle(address, 0x20202020UL); + + address += SECTOR_SIZE_4MBIT; + length -= SECTOR_SIZE_4MBIT; + + // Wait for completion of this individual erase operation before + // we can start a new erase operation. + ExternalMem_WaitCompletion(chipsMask); + } + + result = true; + } + else if (curChipType == ChipType8Bit16BitData_16MBitSize) + { +#define SECTOR_SIZE_16MBIT (64*1024UL*1024UL) + // This chip is nicer because it can take all the sector addresses at + // once and then do the final erase operation in one fell swoop. + + // Start the erase command + ExternalMem_UnlockChips(chipsMask); + ExternalMem_WriteCycle(0xAAAAAAAAUL, 0x80808080UL); + ExternalMem_UnlockChips(chipsMask); + + // Now provide as many sector addresses as needed to erase. + + // The first address is a bit of a special case because the boot sector + // actually has finer granularity for sector sizes. + if (address == 0) + { + ExternalMem_WriteCycle(0x00000000UL, 0x30303030UL); + ExternalMem_WriteCycle(0x00004000UL, 0x30303030UL); + ExternalMem_WriteCycle(0x00006000UL, 0x30303030UL); + ExternalMem_WriteCycle(0x00008000UL, 0x30303030UL); + address += SECTOR_SIZE_16MBIT; + length -= SECTOR_SIZE_16MBIT; + } + + // The remaining sectors can use a more generic algorithm + while (length) + { + ExternalMem_WriteCycle(address, 0x30303030UL); + address += SECTOR_SIZE_16MBIT; + length -= SECTOR_SIZE_16MBIT; + } + + // Wait for completion of the entire erase operation + ExternalMem_WaitCompletion(chipsMask); + + result = true; + } + + return result; +} + void ExternalMem_WaitCompletion(uint8_t chipsMask) { // Mark the chips not requested as already completed, @@ -408,3 +493,8 @@ void ExternalMem_SetChipType(ChipType type) { curChipType = type; } + +ChipType ExternalMem_GetChipType(void) +{ + return curChipType; +} diff --git a/external_mem.h b/external_mem.h index ba19daa..e19f662 100644 --- a/external_mem.h +++ b/external_mem.h @@ -94,6 +94,7 @@ void ExternalMem_IdentifyChips(struct ChipID *chips); // Erases the chips requested void ExternalMem_EraseChips(uint8_t chipsMask); +bool ExternalMem_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask); // Writes a byte to the chips requested at the specified address void ExternalMem_WriteByteToChips(uint32_t address, uint32_t data, uint8_t chipsMask); @@ -107,5 +108,6 @@ uint8_t ExternalMem_Write(uint32_t startAddress, uint32_t *buf, uint32_t len, ui // Tells which flash command protocol to use void ExternalMem_SetChipType(ChipType type); +ChipType ExternalMem_GetChipType(void); #endif /* EXTERNAL_MEM_H_ */ diff --git a/programmer_protocol.h b/programmer_protocol.h index 4dd6fb0..ef7ff3f 100644 --- a/programmer_protocol.h +++ b/programmer_protocol.h @@ -45,7 +45,8 @@ typedef enum ProgrammerCommand SetSIMMTypePLCC32_2MB, SetSIMMTypeLarger, SetVerifyWhileWriting, - SetNoVerifyWhileWriting + SetNoVerifyWhileWriting, + ErasePortion } ProgrammerCommand; // After a command is sent, the programmer will always respond with @@ -166,4 +167,21 @@ typedef enum ComputerBootloaderEraseWriteRequest ComputerBootloaderCancel } ComputerBootloaderEraseWriteRequest; +// ------------------------- ERASE PORTION OF CHIP PROTOCOL ------------------------- +// If the command is ErasePortion, the programmer will reply CommandReplyOK. +// Next, the program will send the beginning position to erase as a 4-byte little +// endian integer, followed by the length to erase as a 4-byte little endian +// integer. The programmer will reply with ProgrammerErasePortionOK to signify +// that the erase is beginning, followed by ProgrammerErasePortionFinished when +// everything is done. +// The length and position to erase must be on 256 KB boundaries and shouldn't +// go past the end of the selected type of chip. If any error occurs, it will +// reply with ProgrammerErasePortionError instead. +typedef enum ProgrammerErasePortionOfChipReply +{ + ProgrammerErasePortionOK = 0, + ProgrammerErasePortionError, + ProgrammerErasePortionFinished +}; + #endif /* PROGRAMMER_PROTOCOL_H_ */ diff --git a/usb_serial/usb_serial.c b/usb_serial/usb_serial.c index 57e11af..fbc6933 100644 --- a/usb_serial/usb_serial.c +++ b/usb_serial/usb_serial.c @@ -34,6 +34,7 @@ #define MAX_CHIP_SIZE (2UL * 1024UL * 1024UL) #define READ_CHUNK_SIZE_BYTES 1024UL #define WRITE_CHUNK_SIZE_BYTES 1024UL +#define ERASE_SECTOR_SIZE_BYTES (256UL * 1024UL) #if ((READ_CHUNK_SIZE_BYTES % 4) != 0) #error Read chunk size should be a multiple of 4 bytes #endif @@ -54,7 +55,8 @@ typedef enum ProgrammerCommandState ReadingChipsReadLength, ReadingChips, ReadingChipsUnableSendError, - WritingChips + WritingChips, + ErasePortionReadingPosLength } ProgrammerCommandState; static ProgrammerCommandState curCommandState = WaitingForCommand; @@ -66,6 +68,8 @@ static uint8_t readLengthByteIndex; static int16_t writePosInChunk = -1; static uint16_t curWriteIndex = 0; static bool verifyDuringWrite = false; +static uint32_t erasePosition; +static uint32_t eraseLength; // Private functions void USBSerial_HandleWaitingForCommandByte(uint8_t byte); @@ -74,6 +78,7 @@ void USBSerial_HandleReadingChipsReadLengthByte(uint8_t byte); void USBSerial_SendReadDataChunk(void); void USBSerial_HandleWritingChipsByte(uint8_t byte); void USBSerial_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2); +void USBSerial_HandleErasePortionReadPosLengthByte(uint8_t byte); // Read/write to USB serial macros -- easier than retyping // CDC_Device_XXX(&VirtualSerial_CDC_Interface...) every time @@ -107,6 +112,9 @@ void USBSerial_Check(void) case WritingChips: USBSerial_HandleWritingChipsByte((uint8_t)recvByte); break; + case ErasePortionReadingPosLength: + USBSerial_HandleErasePortionReadPosLengthByte((uint8_t)recvByte); + break; } } } @@ -227,6 +235,13 @@ void USBSerial_HandleWaitingForCommandByte(uint8_t byte) verifyDuringWrite = false; SendByte(CommandReplyOK); break; + case ErasePortion: + readLengthByteIndex = 0; + eraseLength = 0; + erasePosition = 0; + curCommandState = ErasePortionReadingPosLength; + SendByte(CommandReplyOK); + break; // We don't know what this command is, so reply that it was invalid. default: SendByte(CommandReplyInvalid); @@ -416,6 +431,76 @@ void USBSerial_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2) SendByte(index2); } +// If we're figuring out the position/length to erase, parse it here. +void USBSerial_HandleErasePortionReadPosLengthByte(uint8_t byte) +{ + // Read in the position and length to erase + if (readLengthByteIndex < 4) + { + erasePosition |= (((uint32_t)byte) << (8*readLengthByteIndex)); + } + else + { + eraseLength |= (((uint32_t)byte) << (8*(readLengthByteIndex - 4))); + } + + if (++readLengthByteIndex >= 8) + { + ChipType chipType = ExternalMem_GetChipType(); + bool eraseSuccess = false; + + // Ensure they are both within limits of sector size erasure + if (((erasePosition & ERASE_SECTOR_SIZE_BYTES) == 0) && + ((eraseLength & ERASE_SECTOR_SIZE_BYTES) == 0)) + { + uint32_t boundary = eraseLength + erasePosition; + + // Ensure they are within the limits of the chip size too + if (chipType == ChipType8BitData_4MBitSize) + { + if (boundary <= (2 * 1024UL * 1024UL)) + { + // OK! We're erasing certain sectors of a 2 MB SIMM. + SendByte(ProgrammerErasePortionOK); + CDC_Device_Flush(&VirtualSerial_CDC_Interface); + if (ExternalMem_EraseSectors(erasePosition/NUM_CHIPS, + eraseLength/NUM_CHIPS, ALL_CHIPS)) + { + eraseSuccess = true; + } + } + } + else if (chipType == ChipType8Bit16BitData_16MBitSize) + { + if (boundary <= (8 * 1024UL * 1024UL)) + { + // OK! We're erasing certain sectors of an 8 MB SIMM. + SendByte(ProgrammerErasePortionOK); + CDC_Device_Flush(&VirtualSerial_CDC_Interface); + if (ExternalMem_EraseSectors(erasePosition/NUM_CHIPS, + eraseLength/NUM_CHIPS, ALL_CHIPS)) + { + eraseSuccess = true; + } + } + } + } + + if (eraseSuccess) + { + // Not on a sector boundary for erase position and/or length + SendByte(ProgrammerErasePortionFinished); + curCommandState = WaitingForCommand; + } + else + { + // Not on a sector boundary for erase position and/or length + SendByte(ProgrammerErasePortionError); + curCommandState = WaitingForCommand; + } + } +} + // LUFA event handler for when the USB configuration changes. void EVENT_USB_Device_ConfigurationChanged(void) {