Commit f3e4fbe5 authored by Dean Camera's avatar Dean Camera
Browse files

Use the PDI REPEAT instruction in the PDI programmer code to reduce protocol...

Use the PDI REPEAT instruction in the PDI programmer code to reduce protocol overhead and greatly improve transfer throughput. Switch bit-bang USART in the AVRISP project to Timer 1, so that Timer 0 can be used for hardware timeouts while waiting for the NVM bus or controller to become ready.
parent 48e50b6b
......@@ -40,8 +40,10 @@
void NVMTarget_SendNVMRegAddress(uint8_t Register)
{
/* Determine the absolute register address from the NVM base memory address and the NVM register address */
uint32_t Address = XPROG_Param_NVMBase | Register;
/* Send the calculated 32-bit address to the target, LSB first */
PDITarget_SendByte(Address & 0xFF);
PDITarget_SendByte(Address >> 8);
PDITarget_SendByte(Address >> 16);
......@@ -50,38 +52,30 @@ void NVMTarget_SendNVMRegAddress(uint8_t Register)
void NVMTarget_SendAddress(uint32_t AbsoluteAddress)
{
/* Send the given 32-bit address to the target, LSB first */
PDITarget_SendByte(AbsoluteAddress & 0xFF);
PDITarget_SendByte(AbsoluteAddress >> 8);
PDITarget_SendByte(AbsoluteAddress >> 16);
PDITarget_SendByte(AbsoluteAddress >> 24);
}
bool NVMTarget_WaitWhileNVMBusBusy(void)
bool NVMTarget_WaitWhileNVMControllerBusy(void)
{
uint8_t AttemptsRemaining = 255;
TCNT0 = 0;
/* Poll the STATUS register to check to see if NVM access has been enabled */
while (AttemptsRemaining--)
{
PDITarget_SendByte(PDI_CMD_LDCS | PDI_STATUS_REG);
if (PDITarget_ReceiveByte() & PDI_STATUS_NVM)
return true;
}
return false;
}
void NVMTarget_WaitWhileNVMControllerBusy(void)
{
/* Poll the NVM STATUS register while the NVM controller is busy */
for (;;)
while (TCNT0 < NVM_BUSY_TIMEOUT_MS)
{
/* Send a LDS command to read the NVM STATUS register to check the BUSY flag */
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendNVMRegAddress(NVM_REG_STATUS);
/* Check to see if the BUSY flag is still set */
if (!(PDITarget_ReceiveByte() & (1 << 7)))
return;
return true;
}
return false;
}
uint32_t NVMTarget_GetMemoryCRC(uint8_t MemoryCommand)
......@@ -101,18 +95,20 @@ uint32_t NVMTarget_GetMemoryCRC(uint8_t MemoryCommand)
PDITarget_SendByte(1 << 0);
/* Wait until the NVM bus and controller is no longer busy */
NVMTarget_WaitWhileNVMBusBusy();
PDITarget_WaitWhileNVMBusBusy();
NVMTarget_WaitWhileNVMControllerBusy();
/* Read the three bytes generated CRC value */
/* Read the first generated CRC byte value */
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendNVMRegAddress(NVM_REG_DAT0);
MemoryCRC = PDITarget_ReceiveByte();
/* Read the second generated CRC byte value */
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendNVMRegAddress(NVM_REG_DAT1);
MemoryCRC |= ((uint16_t)PDITarget_ReceiveByte() << 8);
/* Read the third generated CRC byte value */
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendNVMRegAddress(NVM_REG_DAT2);
MemoryCRC |= ((uint32_t)PDITarget_ReceiveByte() << 16);
......@@ -123,17 +119,33 @@ uint32_t NVMTarget_GetMemoryCRC(uint8_t MemoryCommand)
void NVMTarget_ReadMemory(uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize)
{
NVMTarget_WaitWhileNVMControllerBusy();
/* Send the READNVM command to the NVM controller for reading of an aribtrary location */
PDITarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendNVMRegAddress(NVM_REG_CMD);
PDITarget_SendByte(NVM_CMD_READNVM);
/* TODO: Optimize via REPEAT and buffer orientated commands */
for (uint16_t i = 0; i < ReadSize; i++)
/* Send the address of the first location to read from - this also primes the internal address
* counters so that we can use the REPEAT command later to save on overhead for multiple bytes */
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendAddress(ReadAddress);
*(ReadBuffer++) = PDITarget_ReceiveByte();
/* Check to see if we are reading more than a single byte */
if (ReadSize > 1)
{
PDITarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2));
NVMTarget_SendAddress(ReadAddress++);
*(ReadBuffer++) = PDITarget_ReceiveByte();
/* Decrement the ReadSize counter as we have already read once byte of memory */
ReadSize--;
/* Send the REPEAT command with the specified number of bytes remaining to read */
PDITarget_SendByte(PDI_CMD_REPEAT | PDI_DATSIZE_2BYTES);
PDITarget_SendByte(ReadSize & 0xFF);
PDITarget_SendByte(ReadSize >> 8);
/* Send a LD command with indirect access and postincrement to read out the remaining bytes */
PDITarget_SendByte(PDI_CMD_LD | (PDI_POINTER_INDIRECT_PI << 2) | PDI_DATSIZE_1BYTE);
for (uint16_t i = 1; i < ReadSize; i++)
*(ReadBuffer++) = PDITarget_ReceiveByte();
}
}
......@@ -145,6 +157,7 @@ void NVMTarget_EraseMemory(uint8_t EraseCommand, uint32_t Address)
NVMTarget_SendNVMRegAddress(NVM_REG_CMD);
PDITarget_SendByte(EraseCommand);
/* Chip erase is handled seperately, since it's procedure is different to other erase types */
if (EraseCommand == NVM_CMD_CHIPERASE)
{
/* Set CMDEX bit in NVM CTRLA register to start the chip erase */
......@@ -160,7 +173,8 @@ void NVMTarget_EraseMemory(uint8_t EraseCommand, uint32_t Address)
PDITarget_SendByte(0x00);
}
NVMTarget_WaitWhileNVMBusBusy();
/* Wait until both the NVM bus and NVM controller are ready again */
PDITarget_WaitWhileNVMBusBusy();
NVMTarget_WaitWhileNVMControllerBusy();
}
......
......@@ -56,12 +56,7 @@
#endif
/* Defines: */
#define FLASH_BASE 0x00800000
#define EPPROM_BASE 0x008C0000
#define FUSE_BASE 0x008F0020
#define DATAMEM_BASE 0x01000000
#define PROD_SIGNATURE_BASE 0x008E0200
#define USER_SIGNATURE_BASE 0x008E0400
#define NVM_BUSY_TIMEOUT_MS 200
#define NVM_REG_ADDR0 0x00
#define NVM_REG_ADDR1 0x01
......@@ -113,8 +108,7 @@
/* Function Prototypes: */
void NVMTarget_SendNVMRegAddress(uint8_t Register);
void NVMTarget_SendAddress(uint32_t AbsoluteAddress);
bool NVMTarget_WaitWhileNVMBusBusy(void);
void NVMTarget_WaitWhileNVMControllerBusy(void);
bool NVMTarget_WaitWhileNVMControllerBusy(void);
uint32_t NVMTarget_GetMemoryCRC(uint8_t MemoryCommand);
void NVMTarget_ReadMemory(uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize);
void NVMTarget_EraseMemory(uint8_t EraseCommand, uint32_t Address);
......
......@@ -114,7 +114,7 @@ static void PDIProtocol_EnterXPROGMode(void)
PDITarget_SendByte(PDI_NVMENABLE_KEY[i - 1]);
/* Wait until the NVM bus becomes active */
bool NVMBusEnabled = NVMTarget_WaitWhileNVMBusBusy();
bool NVMBusEnabled = PDITarget_WaitWhileNVMBusBusy();
Endpoint_Write_Byte(CMD_XPROG);
Endpoint_Write_Byte(XPRG_CMD_ENTER_PROGMODE);
......
......@@ -44,7 +44,7 @@ volatile bool IsSending;
volatile uint16_t SoftUSART_Data;
volatile uint8_t SoftUSART_BitCount;
ISR(TIMER0_COMPA_vect, ISR_BLOCK)
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
/* Toggle CLOCK pin in a single cycle (see AVR datasheet) */
BITBANG_PDICLOCK_PIN |= BITBANG_PDICLOCK_MASK;
......@@ -112,11 +112,10 @@ void PDITarget_EnableTargetPDI(void)
asm volatile ("NOP"::);
asm volatile ("NOP"::);
/* Fire timer compare ISR every 50 cycles to manage the software USART */
OCR0A = 50;
TCCR0A = (1 << WGM01);
TCCR0B = (1 << CS00);
TIMSK0 = (1 << OCIE0A);
/* Fire timer compare ISR every 100 cycles to manage the software USART */
OCR1A = 100;
TCCR1B = (1 << WGM12) | (1 << CS10);
TIMSK1 = (1 << OCIE1A);
PDITarget_SendBreak();
PDITarget_SendBreak();
......@@ -275,4 +274,20 @@ void PDITarget_SendBreak(void)
#endif
}
bool PDITarget_WaitWhileNVMBusBusy(void)
{
TCNT0 = 0;
/* Poll the STATUS register to check to see if NVM access has been enabled */
while (TCNT0 < PDI_NVM_TIMEOUT_MS)
{
/* Send the LDCS command to read the PDI STATUS register to see the NVM bus is active */
PDITarget_SendByte(PDI_CMD_LDCS | PDI_STATUS_REG);
if (PDITarget_ReceiveByte() & PDI_STATUS_NVM)
return true;
}
return false;
}
#endif
......@@ -69,6 +69,8 @@
#define BITS_IN_FRAME 12
#define PDI_NVM_TIMEOUT_MS 200
#define PDI_CMD_LDS 0x00
#define PDI_CMD_LD 0x20
#define PDI_CMD_STS 0x40
......@@ -102,5 +104,6 @@
void PDITarget_SendByte(uint8_t Byte);
uint8_t PDITarget_ReceiveByte(void);
void PDITarget_SendBreak(void);
bool PDITarget_WaitWhileNVMBusBusy(void);
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment