From 7c8f4a716f01f6598234fd60cd53345da4903fde Mon Sep 17 00:00:00 2001
From: Dean Camera <dean@fourwalledcubicle.com>
Date: Mon, 28 Dec 2009 07:17:21 +0000
Subject: [PATCH] Add first draft of the TPI NVM commands for reading, writing
 and erasing a target. Needs testing when physical access to a part is
 available.

---
 Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.c      | 88 ++++++++++++++++---
 Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.h      | 10 +--
 Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c     |  8 +-
 Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h     |  2 +-
 .../AVRISP-MKII/Lib/XPROG/XPROGProtocol.c     | 41 ++++++---
 .../AVRISP-MKII/Lib/XPROG/XPROGProtocol.h     |  7 +-
 Projects/AVRISP-MKII/Lib/XPROG/XPROGTarget.h  |  2 +-
 7 files changed, 120 insertions(+), 38 deletions(-)

diff --git a/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.c b/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.c
index 4292faad3..4d879bd67 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.c
+++ b/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.c
@@ -39,6 +39,16 @@
 #if defined(ENABLE_XPROG_PROTOCOL) || defined(__DOXYGEN__)
 #warning TPI Protocol support is currently incomplete and is not suitable for general use.
 
+/** Sends the given pointer address to the target's TPI pointer register */
+void TINYNVM_SendPointerAddress(const uint16_t AbsoluteAddress)
+{
+	/* Send the given 16-bit address to the target, LSB first */
+	XPROGTarget_SendByte(TPI_CMD_SSTPR | 0);
+	XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[0]);
+	XPROGTarget_SendByte(TPI_CMD_SSTPR | 1);
+	XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[1]);
+}
+
 /** Busy-waits while the NVM controller is busy performing a NVM operation, such as a FLASH page read.
  *
  *  \return Boolean true if the NVM controller became ready within the timeout period, false otherwise
@@ -57,17 +67,52 @@ bool TINYNVM_WaitWhileNVMBusBusy(void)
 	return false;
 }
 
+/** Waits while the target's NVM controller is busy performing an operation, exiting if the
+ *  timeout period expires.
+ *
+ *  \return Boolean true if the NVM controller became ready within the timeout period, false otherwise
+ */
+bool TINYNVM_WaitWhileNVMControllerBusy(void)
+{
+	/* Poll the STATUS register to check to see if NVM access has been enabled */
+	while (TimeoutMSRemaining)
+	{
+		/* Send the SIN command to read the TPI STATUS register to see the NVM bus is active */
+		XPROGTarget_SendByte(TPI_CMD_SIN | XPROG_Param_NVMCSRRegAddr);
+		if (XPROGTarget_ReceiveByte() & (1 << 7))
+		  return true;
+	}
+	
+	return false;
+}
+
 /** Reads memory from the target's memory spaces.
  *
  *  \param[in]  ReadAddress  Start address to read from within the target's address space
  *  \param[out] ReadBuffer   Buffer to store read data into
- *  \param[in]  ReadSize     Number of bytes to read
+ *  \param[in]  ReadSize     Length of the data to read from the device
  *
  *  \return Boolean true if the command sequence complete successfully
  */
-bool TINYNVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, const uint16_t ReadSize)
+bool TINYNVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize)
 {
-	// TODO
+	/* Wait until the NVM controller is no longer busy */
+	if (!(TINYNVM_WaitWhileNVMControllerBusy()))
+	  return false;
+
+	/* Set the NVM control register to the NO OP command for memory reading */
+	XPROGTarget_SendByte(TPI_CMD_SOUT | XPROG_Param_NVMCMDRegAddr);
+	XPROGTarget_SendByte(TINY_NVM_CMD_NOOP);
+	
+	/* Send the address of the location to read from */
+	TINYNVM_SendPointerAddress(ReadAddress);
+	
+	while (ReadSize--)
+	{
+		/* Read the byte of data from the target */
+		XPROGTarget_SendByte(TPI_CMD_SLD | TPI_POINTER_INDIRECT_PI);
+		*(ReadBuffer++) = XPROGTarget_ReceiveByte();
+	}
 	
 	return true;
 }
@@ -78,25 +123,46 @@ bool TINYNVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, const u
  *  \param[in]  WriteAddress  Start address to write to within the target's address space
  *  \param[in]  WriteBuffer   Buffer to source data from
  *
+ *
  *  \return Boolean true if the command sequence complete successfully
  */
-bool TINYNVM_WriteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t* WriteBuffer)
+bool TINYNVM_WriteMemory(const uint32_t WriteAddress, const uint8_t Byte)
 {
-	// TODO
+	/* Wait until the NVM controller is no longer busy */
+	if (!(TINYNVM_WaitWhileNVMControllerBusy()))
+	  return false;
+
+	/* Set the NVM control register to the WORD WRITE command for memory reading */
+	XPROGTarget_SendByte(TPI_CMD_SOUT | XPROG_Param_NVMCMDRegAddr);
+	XPROGTarget_SendByte(TINY_NVM_CMD_WORDWRITE);
+	
+	/* Send the address of the location to write to */
+	TINYNVM_SendPointerAddress(WriteAddress);
+	
+	/* Write the byte of data to the target */
+	XPROGTarget_SendByte(TPI_CMD_SST | TPI_POINTER_INDIRECT);
+	XPROGTarget_SendByte(Byte);
 	
 	return true;
 }
 
-/** Erases a specific memory space of the target.
- *
- *  \param[in] EraseCommand  NVM erase command to send to the device
- *  \param[in] Address  Address inside the memory space to erase
+/** Erases the target's memory space.
  *
  *  \return Boolean true if the command sequence complete successfully
  */
-bool TINYNVM_EraseMemory(const uint8_t EraseCommand, const uint32_t Address)
+bool TINYNVM_EraseMemory(void)
 {
-	// TODO
+	/* Wait until the NVM controller is no longer busy */
+	if (!(TINYNVM_WaitWhileNVMControllerBusy()))
+	  return false;
+
+	/* Set the NVM control register to the CHIP ERASE command to erase the target */
+	XPROGTarget_SendByte(TPI_CMD_SOUT | XPROG_Param_NVMCMDRegAddr);
+	XPROGTarget_SendByte(TINY_NVM_CMD_CHIPERASE);	
+
+	/* Wait until the NVM bus is ready again */
+	if (!(TINYNVM_WaitWhileNVMBusBusy()))
+	  return false;
 	
 	return true;
 }
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.h b/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.h
index ae0728af3..2d98c0d48 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/TINYNVM.h
@@ -56,18 +56,16 @@
 		#endif
 
 	/* Defines: */
-		#define TINY_NVM_REG_NVMCSR            0x32
-		#define TINY_NVM_REG_NVMCMD            0x33
-
 		#define TINY_NVM_CMD_NOOP              0x00
 		#define TINY_NVM_CMD_CHIPERASE         0x10
 		#define TINY_NVM_CMD_SECTIONERASE      0x14
 		#define TINY_NVM_CMD_WORDWRITE         0x1D
 
 	/* Function Prototypes: */
+		void TINYNVM_SendPointerAddress(const uint16_t AbsoluteAddress);
 		bool TINYNVM_WaitWhileNVMBusBusy(void);
-		bool TINYNVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, const uint16_t ReadSize);
-		bool TINYNVM_WriteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t* WriteBuffer);
-		bool TINYNVM_EraseMemory(const uint8_t EraseCommand, const uint32_t Address);
+		bool TINYNVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadLength);
+		bool TINYNVM_WriteMemory(const uint32_t WriteAddress, const uint8_t Byte);
+		bool TINYNVM_EraseMemory(void);
 
 #endif
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
index 0bdafc2ac..ea140be7b 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.c
@@ -196,12 +196,12 @@ bool XMEGANVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, uint16
 /** Writes byte addressed memory to the target's memory spaces.
  *
  *  \param[in]  WriteCommand  Command to send to the device to write each memory byte
- *  \param[in]  WriteAddress  Start address to write to within the target's address space
- *  \param[in]  WriteBuffer   Buffer to source data from
+ *  \param[in]  WriteAddress  Address to write to within the target's address space
+ *  \param[in]  Byte          Byte to write to the target
  *
  *  \return Boolean true if the command sequence complete successfully
  */
-bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t* WriteBuffer)
+bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t Byte)
 {
 	/* Wait until the NVM controller is no longer busy */
 	if (!(XMEGANVM_WaitWhileNVMControllerBusy()))
@@ -215,7 +215,7 @@ bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAd
 	/* Send new memory byte to the memory to the target */
 	XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2));
 	XMEGANVM_SendAddress(WriteAddress);
-	XPROGTarget_SendByte(*(WriteBuffer++));
+	XPROGTarget_SendByte(Byte);
 	
 	return true;
 }
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
index bc5772657..608f5ab79 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
@@ -110,7 +110,7 @@
 		bool XMEGANVM_WaitWhileNVMControllerBusy(void);
 		bool XMEGANVM_GetMemoryCRC(const uint8_t CRCCommand, uint32_t* const CRCDest);
 		bool XMEGANVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize);
-		bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t* WriteBuffer);
+		bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t Byte);
 		bool XMEGANVM_WritePageMemory(const uint8_t WriteBuffCommand, const uint8_t EraseBuffCommand,
 		                              const uint8_t WritePageCommand, const uint8_t PageMode, const uint32_t WriteAddress,
 		                              const uint8_t* WriteBuffer, uint16_t WriteSize);
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
index f3f888e1c..39f82a0df 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.c
@@ -37,11 +37,17 @@
 #include "XPROGProtocol.h"
 
 #if defined(ENABLE_XPROG_PROTOCOL) || defined(__DOXYGEN__)
-/** Base absolute address for the target's NVM controller */
+/** Base absolute address for the target's NVM controller for PDI programming */
 uint32_t XPROG_Param_NVMBase    = 0x010001C0;
 
 /** Size in bytes of the target's EEPROM page */
-uint32_t XPROG_Param_EEPageSize;
+uint16_t XPROG_Param_EEPageSize;
+
+/** Address of the TPI device's NVMCMD register for TPI programming */
+uint8_t  XPROG_Param_NVMCMDRegAddr;
+
+/** Address of the TPI device's NVMCSR register for TPI programming */
+uint8_t  XPROG_Param_NVMCSRRegAddr;
 
 /** Currently selected XPROG programming protocol */
 uint8_t  XPROG_SelectedProtocol = XPRG_PROTOCOL_PDI;
@@ -222,7 +228,9 @@ static void XPROGProtocol_Erase(void)
 	}
 	else
 	{
-		// TODO
+		/* Erase the target memory, indicate timeout if ocurred */
+		if (!(TINYNVM_EraseMemory()))
+		  ReturnStatus = XPRG_ERR_TIMEOUT;
 	}
 	
 	Endpoint_Write_Byte(CMD_XPROG);
@@ -294,18 +302,20 @@ static void XPROGProtocol_WriteMemory(void)
 		}
 		
 		/* Send the appropriate memory write commands to the device, indicate timeout if occurred */
-		if ((PagedMemory && !XMEGANVM_WritePageMemory(WriteBuffCommand, EraseBuffCommand, WriteCommand, 
+		if ((PagedMemory && !(XMEGANVM_WritePageMemory(WriteBuffCommand, EraseBuffCommand, WriteCommand, 
 													   WriteMemory_XPROG_Params.PageMode, WriteMemory_XPROG_Params.Address,
-													   WriteMemory_XPROG_Params.ProgData, WriteMemory_XPROG_Params.Length)) ||
-		   (!PagedMemory && !XMEGANVM_WriteByteMemory(WriteCommand, WriteMemory_XPROG_Params.Address,
-													   WriteMemory_XPROG_Params.ProgData)))
+													   WriteMemory_XPROG_Params.ProgData, WriteMemory_XPROG_Params.Length))) ||
+		   (!PagedMemory && !(XMEGANVM_WriteByteMemory(WriteCommand, WriteMemory_XPROG_Params.Address,
+													   WriteMemory_XPROG_Params.ProgData[0]))))
 		{
 			ReturnStatus = XPRG_ERR_TIMEOUT;
 		}
 	}
 	else
 	{
-		// TODO
+		/* Send write command to the TPI device, indicate timeout if occurred */
+		if (!(TINYNVM_WriteMemory(WriteMemory_XPROG_Params.Address, WriteMemory_XPROG_Params.ProgData[0])))
+		  ReturnStatus = XPRG_ERR_TIMEOUT;
 	}
 	
 	Endpoint_Write_Byte(CMD_XPROG);
@@ -339,13 +349,15 @@ static void XPROGProtocol_ReadMemory(void)
 	
 	if (XPROG_SelectedProtocol == XPRG_PROTOCOL_PDI)
 	{
-		/* Read the target's memory, indicate timeout if occurred */
+		/* Read the PDI target's memory, indicate timeout if occurred */
 		if (!(XMEGANVM_ReadMemory(ReadMemory_XPROG_Params.Address, ReadBuffer, ReadMemory_XPROG_Params.Length)))
 		  ReturnStatus = XPRG_ERR_TIMEOUT;
 	}
 	else
 	{
-		// TODO
+		/* Read the TPI target's memory, indicate timeout if occurred */
+		if (!(TINYNVM_ReadMemory(ReadMemory_XPROG_Params.Address, ReadBuffer, ReadMemory_XPROG_Params.Length)))
+		  ReturnStatus = XPRG_ERR_TIMEOUT;
 	}
 
 	Endpoint_Write_Byte(CMD_XPROG);
@@ -428,9 +440,12 @@ static void XPROGProtocol_SetParam(void)
 		case XPRG_PARAM_EEPPAGESIZE:
 			XPROG_Param_EEPageSize = Endpoint_Read_Word_BE();
 			break;
-		case XPRG_PARAM_UNDOC_1:
-		case XPRG_PARAM_UNDOC_2:
-			break; // Undocumented TPI parameter, just accept and discard
+		case XPRG_PARAM_NVMCMD:
+			XPROG_Param_NVMCMDRegAddr = Endpoint_Read_Byte();
+			break;
+		case XPRG_PARAM_NVMCSR:
+			XPROG_Param_NVMCSRRegAddr = Endpoint_Read_Byte();
+			break;
 		default:
 			ReturnStatus = XPRG_ERR_FAILED;
 			break;
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
index e42e50cec..a95e5b851 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XPROGProtocol.h
@@ -97,8 +97,8 @@
 
 		#define XPRG_PARAM_NVMBASE                  0x01
 		#define XPRG_PARAM_EEPPAGESIZE              0x02
-		#define XPRG_PARAM_UNDOC_1                  0x03
-		#define XPRG_PARAM_UNDOC_2                  0x04
+		#define XPRG_PARAM_NVMCMD                   0x03
+		#define XPRG_PARAM_NVMCSR                   0x04
 		
 		#define XPRG_PROTOCOL_PDI                   0x00
 		#define XPRG_PROTOCOL_JTAG                  0x01
@@ -109,6 +109,9 @@
 	
 	/* External Variables: */
 		extern uint32_t XPROG_Param_NVMBase;
+		extern uint16_t XPROG_Param_EEPageSize;
+		extern uint8_t  XPROG_Param_NVMCSRRegAddr;
+		extern uint8_t  XPROG_Param_NVMCMDRegAddr;
 		
 	/* Function Prototypes: */
 		void XPROGProtocol_SetMode(void);
diff --git a/Projects/AVRISP-MKII/Lib/XPROG/XPROGTarget.h b/Projects/AVRISP-MKII/Lib/XPROG/XPROGTarget.h
index a892829c1..f4c3b7b1d 100644
--- a/Projects/AVRISP-MKII/Lib/XPROG/XPROGTarget.h
+++ b/Projects/AVRISP-MKII/Lib/XPROG/XPROGTarget.h
@@ -99,8 +99,8 @@
 		#define PDI_CTRL_REG               2
 		
 		#define PDI_STATUS_NVM             (1 << 1)
-		#define PDI_RESET_KEY              0x59
 
+		#define PDI_RESET_KEY              0x59
 		#define PDI_NVMENABLE_KEY          (uint8_t[]){0x12, 0x89, 0xAB, 0x45, 0xCD, 0xD8, 0x88, 0xFF}
 
 		#define PDI_DATSIZE_1BYTE          0
-- 
GitLab