diff --git a/.cproject b/.cproject
index 7532b36..48e4975 100644
--- a/.cproject
+++ b/.cproject
@@ -41,6 +41,9 @@
+
@@ -85,7 +88,7 @@
-
+
@@ -113,6 +116,9 @@
+
@@ -151,6 +157,7 @@
+
@@ -163,16 +170,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -203,56 +200,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -265,16 +212,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -305,58 +242,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -368,16 +255,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -408,57 +285,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/delay.c b/delay.c
deleted file mode 100644
index f3990d5..0000000
--- a/delay.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * delay.c
- *
- * Created on: Dec 4, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "delay.h"
diff --git a/drivers/mcp23s17.c b/drivers/mcp23s17.c
new file mode 100644
index 0000000..6311ac0
--- /dev/null
+++ b/drivers/mcp23s17.c
@@ -0,0 +1,195 @@
+/*
+ * mcp23s17.c
+ *
+ * Created on: Nov 25, 2011
+ * Author: Doug
+ */
+
+#include "mcp23s17.h"
+#include "hardware.h"
+
+/// Maximum SPI clock rate = 10 MHz
+#define MCP23S17_MAX_CLOCK 10000000UL
+
+static void MCP23S17_WriteBothRegs(MCP23S17 *mcp, uint8_t addrA, uint16_t value);
+static uint16_t MCP23S17_ReadBothRegs(MCP23S17 *mcp, uint8_t addrA);
+
+/** Initializes an MCP23S17 object
+ *
+ * @param mcp The MCP23S17 object to initialize
+ * @param resetPin The GPIO pin hooked to the reset input
+ */
+void MCP23S17_Init(MCP23S17 *mcp, GPIOPin resetPin)
+{
+ SPI_InitDevice(&mcp->spi, MCP23S17_MAX_CLOCK, SPI_MODE_0);
+
+ // Do a reset pulse if we need to. No need to save the reset pin
+ // after that.
+ if (!GPIO_IsNull(resetPin))
+ {
+ GPIO_SetDirection(resetPin, true);
+ GPIO_SetOff(resetPin);
+ DelayUS(1);
+ GPIO_SetOn(resetPin);
+ DelayUS(1);
+ }
+}
+
+/** Begins a set of transactions with the MCP23S17.
+ *
+ * @param mcp The MCP23S17 to talk with
+ *
+ * You have to call this before using any of the MCP23S17 functions other than
+ * init. This takes control of the SPI bus. If the MCP23S17 is the only device
+ * on the bus, you can just call this once in the program. Otherwise, you should
+ * release it as soon as you're done so other SPI devices can use the bus instead.
+ */
+void MCP23S17_Begin(MCP23S17 *mcp)
+{
+ SPI_RequestBus(&mcp->spi);
+}
+
+/** Ends a set of transactions with the MCP23S17.
+ *
+ * @param mcp The MCP23S17 to release
+ *
+ * You should call this when you're done talking to the MCP23S17 to free up the
+ * SPI bus for other devices.
+ */
+void MCP23S17_End(MCP23S17 *mcp)
+{
+ SPI_ReleaseBus(&mcp->spi);
+}
+
+/** Sets the data direction register in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @param ddr Bitmask representing direction of the 16 GPIO pins (1 = output, 0 = input)
+ */
+void MCP23S17_SetDDR(MCP23S17 *mcp, uint16_t ddr)
+{
+ // The MCP23S17's DDR is backwards from most other chips. I like dealing
+ // with it so it behaves like other MCUs, so I invert any DDR values in this
+ // driver. In other words, when you set or get the DDR through this driver,
+ // the 1s and 0s are backwards from what the MCP23S17's datasheet says, but
+ // they are consistent with most MCUs. I value the consistency more.
+ MCP23S17_WriteBothRegs(mcp, MCP23S17_IODIRA, ~ddr);
+}
+
+/** Reads the data direction register in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @return Bitmask representing direction of the 16 GPIO pins (1 = output, 0 = input)
+ */
+uint16_t MCP23S17_DDR(MCP23S17 *mcp)
+{
+ // As I mentioned above, DDR bits are inverted from what the MCP23S17's
+ // datasheet says, but consistent with most MCUs
+ return ~MCP23S17_ReadBothRegs(mcp, MCP23S17_IODIRA);
+}
+
+/** Sets the output values in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @param data Bitmask representing output state of the 16 GPIO pins (1 = high, 0 = low)
+ */
+void MCP23S17_SetOutputs(MCP23S17 *mcp, uint16_t data)
+{
+ MCP23S17_WriteBothRegs(mcp, MCP23S17_GPIOA, data);
+}
+
+/** Gets the current output values in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @return Bitmask representing output state of the 16 GPIO pins (1 = high, 0 = low)
+ */
+uint16_t MCP23S17_Outputs(MCP23S17 *mcp)
+{
+ return MCP23S17_ReadBothRegs(mcp, MCP23S17_OLATA);
+}
+
+/** Reads the current input values in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @return Bitmask representing input state of the 16 GPIO pins (1 = high, 0 = low)
+ */
+uint16_t MCP23S17_ReadInputs(MCP23S17 *mcp)
+{
+ return MCP23S17_ReadBothRegs(mcp, MCP23S17_GPIOA);
+}
+
+/** Enables/disables pullups in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @param pullups Bitmask representing which input pins should have pullups enabled.
+ */
+void MCP23S17_SetPullups(MCP23S17 *mcp, uint16_t pullups)
+{
+ MCP23S17_WriteBothRegs(mcp, MCP23S17_GPPUA, pullups);
+}
+
+/** Reads the current pullup state in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * return Bitmask representing which input pins have pullups enabled.
+ */
+uint16_t MCP23S17_Pullups(MCP23S17 *mcp)
+{
+ return MCP23S17_ReadBothRegs(mcp, MCP23S17_GPPUA);
+}
+
+/** Helper function that writes two consecutive registers in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @param addrA The address of the first ("A") register
+ * @param value The value to write to the A and B registers. High byte = A, low byte = B
+ */
+static void MCP23S17_WriteBothRegs(MCP23S17 *mcp, uint8_t addrA, uint16_t value)
+{
+ // addrA should contain the address of the "A" register.
+ // the chip should also be in "same bank" mode.
+ SPI_Assert(&mcp->spi);
+
+ // Start off the communication by telling the MCP23S17 that we are writing to a register
+ SPI_RWByte(&mcp->spi, MCP23S17_CONTROL_WRITE(0));
+
+ // Tell it the first register we're writing to (the "A" register)
+ SPI_RWByte(&mcp->spi, addrA);
+
+ // Write the first byte of the register
+ SPI_RWByte(&mcp->spi, (uint8_t)((value >> 8) & 0xFF));
+
+ // It should auto-increment to the "B" register, now write that
+ SPI_RWByte(&mcp->spi, (uint8_t)(value & 0xFF));
+
+ SPI_Deassert(&mcp->spi);
+}
+
+/** Helper function that reads two consecutive registers in the MCP23S17
+ *
+ * @param mcp The MCP23S17
+ * @param addrA The address of the first ("A") register
+ * @return The value read back from the A and B registers. High byte = A, low byte = B
+ */
+static uint16_t MCP23S17_ReadBothRegs(MCP23S17 *mcp, uint8_t addrA)
+{
+ uint16_t returnVal;
+
+ SPI_Assert(&mcp->spi);
+
+ // Start off the communication by telling the MCP23S17 that we are reading from a register
+ SPI_RWByte(&mcp->spi, MCP23S17_CONTROL_READ(0));
+
+ // Tell it which register we're reading from (the "A" register)
+ SPI_RWByte(&mcp->spi, addrA);
+
+ // Read the first byte of the register
+ returnVal = (((uint16_t)SPI_RWByte(&mcp->spi, 0)) << 8);
+
+ // It should auto-increment to the "B" register, now read that
+ returnVal |= SPI_RWByte(&mcp->spi, 0);
+
+ SPI_Deassert(&mcp->spi);
+
+ return returnVal;
+}
diff --git a/drivers/mcp23s17.h b/drivers/mcp23s17.h
new file mode 100644
index 0000000..7a33cbb
--- /dev/null
+++ b/drivers/mcp23s17.h
@@ -0,0 +1,59 @@
+/*
+ * mcp23s17.h
+ *
+ * Created on: Nov 25, 2011
+ * Author: Doug
+ */
+
+#ifndef DRIVERS_MCP23S17_H_
+#define DRIVERS_MCP23S17_H_
+
+#include "../hal/spi.h"
+
+/// A few register defines. Ordinarily I wouldn't put these in the header file
+/// because users of this module shouldn't care...but I have had to do some
+/// optimizations with the AVR to bypass the hardware abstraction layer.
+#define MCP23S17_CONTROL_WRITE(address) (0x40 | (address << 1))
+#define MCP23S17_CONTROL_READ(address) (0x40 | (address << 1) | 1)
+#define MCP23S17_IODIRA 0x00
+#define MCP23S17_IODIRB 0x01
+#define MCP23S17_IPOLA 0x02
+#define MCP23S17_IPOLB 0x03
+#define MCP23S17_GPINTENA 0x04
+#define MCP23S17_GPINTENB 0x05
+#define MCP23S17_DEFVALA 0x06
+#define MCP23S17_DEFVALB 0x07
+#define MCP23S17_INTCONA 0x08
+#define MCP23S17_INTCONB 0x09
+#define MCP23S17_IOCON 0x0A
+#define MCP23S17_IOCON_AGAIN 0x0B
+#define MCP23S17_GPPUA 0x0C
+#define MCP23S17_GPPUB 0x0D
+#define MCP23S17_INTFA 0x0E
+#define MCP23S17_INTFB 0x0F
+#define MCP23S17_INTCAPA 0x10
+#define MCP23S17_INTCAPB 0x11
+#define MCP23S17_GPIOA 0x12
+#define MCP23S17_GPIOB 0x13
+#define MCP23S17_OLATA 0x14
+#define MCP23S17_OLATB 0x15
+
+/// Struct representing a single MCP23S17 device
+typedef struct MCP23S17
+{
+ /// The SPI device representing this MCP23S17
+ SPIDevice spi;
+} MCP23S17;
+
+void MCP23S17_Init(MCP23S17 *mcp, GPIOPin resetPin);
+void MCP23S17_Begin(MCP23S17 *mcp);
+void MCP23S17_End(MCP23S17 *mcp);
+void MCP23S17_SetDDR(MCP23S17 *mcp, uint16_t ddr);
+uint16_t MCP23S17_DDR(MCP23S17 *mcp);
+void MCP23S17_SetOutputs(MCP23S17 *mcp, uint16_t data);
+uint16_t MCP23S17_Outputs(MCP23S17 *mcp);
+uint16_t MCP23S17_ReadInputs(MCP23S17 *mcp);
+void MCP23S17_SetPullups(MCP23S17 *mcp, uint16_t pullups);
+uint16_t MCP23S17_Pullups(MCP23S17 *mcp);
+
+#endif /* DRIVERS_MCP23S17_H_ */
diff --git a/drivers/parallel_flash.c b/drivers/parallel_flash.c
new file mode 100644
index 0000000..cdbef60
--- /dev/null
+++ b/drivers/parallel_flash.c
@@ -0,0 +1,412 @@
+/*
+ * parallel_flash.c
+ *
+ * Created on: Nov 25, 2011
+ * Author: Doug
+ */
+
+#include "parallel_flash.h"
+#include "../util.h"
+
+/// Erasable sector size in SST39SF040
+#define SECTOR_SIZE_SST39SF040 (4*1024UL)
+/// Erasable sector size in M29F160FB5AN6E2, 8-bit mode
+#define SECTOR_SIZE_M29F160FB5AN6E2_8 (64*1024UL)
+
+static uint32_t ParallelFlash_MaskForChips(uint8_t chips);
+static ALWAYS_INLINE void ParallelFlash_WaitForCompletion(void);
+static ALWAYS_INLINE uint32_t ParallelFlash_UnlockAddress1(void);
+
+/// The type/arrangement of parallel flash chips we are talking to
+static ParallelFlashChipType curChipType = ParallelFlash_SST39SF040_x4;
+
+/** Sets the type/arrangement of parallel flash chips we are talking to
+ *
+ * @param type The type/arrangement of flash chips
+ */
+void ParallelFlash_SetChipType(ParallelFlashChipType type)
+{
+ curChipType = type;
+}
+
+/** Gets the type/arrangement of parallel flash chips we are talking to
+ *
+ * @return The current type/arrangement of flash chips
+ */
+ParallelFlashChipType ParallelFlash_ChipType(void)
+{
+ return curChipType;
+}
+
+/** Reads data from the flash chip
+ *
+ * @param startAddress The address for reading
+ * @param buf The buffer to read to
+ * @param len The number of bytes to read
+ */
+void ParallelFlash_Read(uint32_t startAddress, uint32_t *buf, uint16_t len)
+{
+ // Just forward this request directly onto the parallel bus. Nothing
+ // special is required for reading a chunk of data.
+ ParallelBus_Read(startAddress, buf, len);
+}
+
+/** Unlocks the flash chips using the special write sequence
+ *
+ * @param chipsMask The mask of which chips to unlock
+ */
+void ParallelFlash_UnlockChips(uint8_t chipsMask)
+{
+ // Use a mask so we don't unlock chips we don't want to talk with
+ uint32_t mask = ParallelFlash_MaskForChips(chipsMask);
+ uint32_t unlockAddress = ParallelFlash_UnlockAddress1();
+
+ // First part of unlock sequence:
+ // Write 0x55555555 to the address bus and 0xAA to the data bus
+ // (Some datasheets may only say 0x555 or 0x5555, but they ignore
+ // the upper bits, so writing the alternating pattern to all address lines
+ // should make it compatible with larger chips).
+ ParallelBus_WriteCycle(unlockAddress, 0xAAAAAAAAUL & mask);
+
+ // Second part of unlock sequence is the same thing, but reversed.
+ ParallelBus_WriteCycle(~unlockAddress, 0x55555555UL & mask);
+}
+
+/** Reads the ID of the chips
+ *
+ * @param Pointer to variable for storing ID info about each chip
+ */
+void ParallelFlash_IdentifyChips(ParallelFlashChipID *chips)
+{
+ // Start by writing the unlock sequence to ALL chips
+ ParallelFlash_UnlockChips(ALL_CHIPS);
+
+ // Write 0x90 to the first unlock address for the identify command...
+ ParallelBus_WriteCycle(ParallelFlash_UnlockAddress1(), 0x90909090UL);
+
+ // Now we can read the vendor and product ID from addresses 0 and 1
+ // (or 1 and 2 if we're using the M29F160FB5AN6E2 in 8-bit mode).
+ // Note: The Micron datasheet says it requires 12V to be applied to A9
+ // in order for the identification command to work properly, but in
+ // practice the identification process works fine without it.
+ uint32_t vendorAddress = curChipType != ParallelFlash_M29F160FB5AN6E2_x4 ?
+ 0 : 1;
+ uint32_t manufacturers = ParallelBus_ReadCycle(vendorAddress);
+ uint32_t devices = ParallelBus_ReadCycle(vendorAddress + 1);
+ for (int8_t i = 0; i < PARALLEL_FLASH_NUM_CHIPS; i++)
+ {
+ uint8_t manufacturer = (uint8_t)(manufacturers >> (8 * i));
+ uint8_t device = (uint8_t)(devices >> (8 * i));
+ chips[PARALLEL_FLASH_NUM_CHIPS - i - 1].manufacturer = manufacturer;
+ chips[PARALLEL_FLASH_NUM_CHIPS - i - 1].device = device;
+ }
+
+ // Exit software ID mode
+ ParallelBus_WriteCycle(0, 0xF0F0F0F0UL);
+}
+
+/** Erases the specified chips
+ *
+ * @param chipsMask The mask of which chips to erase
+ */
+void ParallelFlash_EraseChips(uint8_t chipsMask)
+{
+ uint32_t unlockAddress = ParallelFlash_UnlockAddress1();
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(unlockAddress, 0x80808080UL);
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(unlockAddress, 0x10101010UL);
+ ParallelFlash_WaitForCompletion();
+}
+
+/** Erases only the range of sectors specified in the specified chips
+ *
+ * @param address The start address to erase (must be aligned to a sector boundary)
+ * @param length The number of bytes to erase (must be aligned to a sector boundary)
+ * @param chipsMask The mask of which chips to erase
+ * @return True on success, false on failure
+ */
+bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask)
+{
+ bool result = false;
+
+ // Figure out our sector size
+ uint32_t sectorSize;
+ switch (curChipType)
+ {
+ case ParallelFlash_SST39SF040_x4:
+ default:
+ sectorSize = SECTOR_SIZE_SST39SF040;
+ break;
+ case ParallelFlash_M29F160FB5AN6E2_x4:
+ sectorSize = SECTOR_SIZE_M29F160FB5AN6E2_8;
+ break;
+ }
+
+ // Make sure the area requested to be erased is on good boundaries
+ if ((address % sectorSize) ||
+ (length % sectorSize))
+ {
+ return false;
+ }
+
+ // We're good to go. Let's do it. The process varies based on the chip type
+ if (curChipType == ParallelFlash_SST39SF040_x4)
+ {
+ // 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
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(ParallelFlash_UnlockAddress1(), 0x80808080UL);
+ ParallelFlash_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.
+ ParallelBus_WriteCycle(address, 0x30303030UL);
+
+ address += sectorSize;
+ length -= sectorSize;
+
+ // Wait for completion of this individual erase operation before
+ // we can start a new erase operation.
+ ParallelFlash_WaitForCompletion();
+ }
+
+ result = true;
+ }
+ else if (curChipType == ParallelFlash_M29F160FB5AN6E2_x4)
+ {
+ // 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
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(ParallelFlash_UnlockAddress1(), 0x80808080UL);
+ ParallelFlash_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)
+ {
+ ParallelBus_WriteCycle(0x00000000UL, 0x30303030UL);
+ ParallelBus_WriteCycle(0x00004000UL, 0x30303030UL);
+ ParallelBus_WriteCycle(0x00006000UL, 0x30303030UL);
+ ParallelBus_WriteCycle(0x00008000UL, 0x30303030UL);
+ address += sectorSize;
+ length -= sectorSize;
+ }
+
+ // The remaining sectors can use a more generic algorithm
+ while (length)
+ {
+ ParallelBus_WriteCycle(address, 0x30303030UL);
+ address += sectorSize;
+ length -= sectorSize;
+ }
+
+ // Wait for completion of the entire erase operation
+ ParallelFlash_WaitForCompletion();
+
+ result = true;
+ }
+
+ return result;
+
+}
+
+/** Writes a buffer of data to all 4 chips simultaneously
+ *
+ * @param startAddress The starting address to write in flash
+ * @param buf The buffer to write
+ * @param len The length of data to write
+ *
+ * The API may look silly to have broken into different functions like this, but
+ * it's a performance optimization. It means we don't have to check during every
+ * byte write to see the chip unlock mask. It saves a bunch of time.
+ */
+void ParallelFlash_WriteAllChips(uint32_t startAddress, uint32_t const *buf, uint16_t len)
+{
+ uint32_t unlockAddress = ParallelFlash_UnlockAddress1();
+
+ // Normal write process used by most parallel flashes
+ if (curChipType != ParallelFlash_M29F160FB5AN6E2_x4)
+ {
+ while (len--)
+ {
+ // Write this byte.
+ // Unlock...and don't use the unlock function because this one
+ // is more efficient knowing the mask is 0xFFFFFFFF
+ ParallelBus_WriteCycle(unlockAddress, 0xAAAAAAAAUL);
+ ParallelBus_WriteCycle(~unlockAddress, 0x55555555UL);
+ ParallelBus_WriteCycle(unlockAddress, 0xA0A0A0A0UL);
+ ParallelBus_WriteCycle(startAddress, *buf);
+ ParallelFlash_WaitForCompletion();
+
+ startAddress++;
+ buf++;
+ }
+ }
+ // Optimized write process available on the M29F160FB5AN6E2, requires
+ // fewer write cycles per byte if you know you're writing multiple bytes.
+ else
+ {
+ // Do an unlock bypass command so that we can write bytes faster.
+ // Writes will only require 2 write cycles instead of 4
+ ParallelBus_WriteCycle(unlockAddress, 0xAAAAAAAAUL);
+ ParallelBus_WriteCycle(~unlockAddress, 0x55555555UL);
+ ParallelBus_WriteCycle(unlockAddress, 0x20202020UL);
+
+ while (len--)
+ {
+ // Write this byte.
+ ParallelBus_WriteCycle(0, 0xA0A0A0A0UL);
+ ParallelBus_WriteCycle(startAddress, *buf);
+ ParallelFlash_WaitForCompletion();
+
+ startAddress++;
+ buf++;
+ }
+
+ // When we're all done, do "unlock bypass reset" to exit from
+ // programming mode
+ ParallelBus_WriteCycle(0, 0x90909090UL);
+ ParallelBus_WriteCycle(0, 0x00000000UL);
+ }
+}
+
+/** Writes a buffer of data to the specified chips simultaneously
+ *
+ * @param startAddress The starting address to write in flash
+ * @param buf The buffer to write
+ * @param len The length of data to write
+ * @param chipsMask The mask of which chips to write
+ */
+void ParallelFlash_WriteSomeChips(uint32_t startAddress, uint32_t const *buf, uint16_t len, uint8_t chipsMask)
+{
+ uint32_t unlockAddress = ParallelFlash_UnlockAddress1();
+
+ // Normal write process used by most parallel flashes
+ if (curChipType != ParallelFlash_M29F160FB5AN6E2_x4)
+ {
+ while (len--)
+ {
+ // Write this byte.
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(unlockAddress, 0xA0A0A0A0UL);
+ ParallelBus_WriteCycle(startAddress, *buf);
+ ParallelFlash_WaitForCompletion();
+
+ startAddress++;
+ buf++;
+ }
+ }
+ // Optimized write process available on the M29F160FB5AN6E2, requires
+ // fewer write cycles per byte if you know you're writing multiple bytes.
+ else
+ {
+ // Do an unlock bypass command so that we can write bytes faster.
+ // Writes will only require 2 write cycles instead of 4
+ ParallelFlash_UnlockChips(chipsMask);
+ ParallelBus_WriteCycle(unlockAddress, 0x20202020UL);
+
+ while (len--)
+ {
+ // Write this byte.
+ ParallelBus_WriteCycle(0, 0xA0A0A0A0UL);
+ ParallelBus_WriteCycle(startAddress, *buf);
+ ParallelFlash_WaitForCompletion();
+
+ startAddress++;
+ buf++;
+ }
+
+ // When we're all done, do "unlock bypass reset" to exit from
+ // programming mode
+ ParallelBus_WriteCycle(0, 0x90909090UL);
+ ParallelBus_WriteCycle(0, 0x00000000UL);
+ }
+}
+
+/** Calculates a 32-bit mask to use with the unlock process when unlocking chips
+ *
+ * @param chipsMask The mask of which chips to write
+ * @return A 32-bit mask that can be used on the data bus to filter out the unlock sequence
+ *
+ * For clarity, chipsMask has 1 bit per chip. The return value has 1 byte per
+ * chip. The return value masks the unlock sequence so we only supply a valid
+ * unlock sequence to the chips that we want to unlock.
+ */
+static uint32_t ParallelFlash_MaskForChips(uint8_t chips)
+{
+ // Calculates a mask so we can filter out chips we don't care about.
+
+ // Optimization because we typically don't mask the chips
+ if (chips == 0x0F)
+ {
+ return 0xFFFFFFFFUL;
+ }
+
+ // This probably looks dumb not doing this as a loop...but AVR GCC is
+ // terrible. This approach results in more optimal generated instructions.
+ uint32_t mask = 0;
+ if (chips & (1 << 0))
+ {
+ mask |= 0x000000FFUL;
+ }
+ if (chips & (1 << 1))
+ {
+ mask |= 0x0000FF00UL;
+ }
+ if (chips & (1 << 2))
+ {
+ mask |= 0x00FF0000UL;
+ }
+ if (chips & (1 << 3))
+ {
+ mask |= 0xFF000000UL;
+ }
+
+ return mask;
+}
+
+/** Waits for an erase or write operation on the flash chip to complete.
+ *
+ * We know we're done when the value we read from the chip stops changing. There
+ * is a "toggle" status bit that will stop toggling when the op is complete.
+ */
+static ALWAYS_INLINE void ParallelFlash_WaitForCompletion(void)
+{
+ uint32_t readback = ParallelBus_ReadCycle(0);
+ uint32_t next = ParallelBus_ReadCycle(0);
+ while (next != readback)
+ {
+ readback = next;
+ next = ParallelBus_ReadCycle(0);
+ }
+}
+
+/** Gets the first unlock address to use when unlocking writes on this chip
+ *
+ * @return The first unlock address.
+ *
+ * Note: The second unlock address is the bitwise NOT of this address.
+ */
+static ALWAYS_INLINE uint32_t ParallelFlash_UnlockAddress1(void)
+{
+ // Most chips use alternating bits, with A0 being a 1 bit
+ if (curChipType != ParallelFlash_M29F160FB5AN6E2_x4)
+ {
+ return 0x55555555UL;
+ }
+ // The M29F160FB5AN6E2 is weird because it's an 8-/16-bit chip. In 8-bit
+ // mode it has an "A-1" pin that we treat as A0, then the chip's A0 pin is
+ // really our A1, and so on. The unlock sequence still starts on the chip's
+ // physical pin A0, so effectively the unlock address is inverted.
+ else
+ {
+ return 0xAAAAAAAAUL;
+ }
+}
diff --git a/drivers/parallel_flash.h b/drivers/parallel_flash.h
new file mode 100644
index 0000000..cdbd50e
--- /dev/null
+++ b/drivers/parallel_flash.h
@@ -0,0 +1,66 @@
+/*
+ * parallel_flash.h
+ *
+ * Created on: Nov 25, 2011
+ * Author: Doug
+ */
+
+#ifndef DRIVERS_PARALLEL_FLASH_H_
+#define DRIVERS_PARALLEL_FLASH_H_
+
+#include "../hal/parallel_bus.h"
+
+/// The number of chips we are simultaneously addressing
+#define PARALLEL_FLASH_NUM_CHIPS 4
+
+/// Masks for functions that want a chip mask...
+#define IC1 (1 << 3)
+#define IC2 (1 << 2)
+#define IC3 (1 << 1)
+#define IC4 (1 << 0)
+#define ALL_CHIPS (IC1 | IC2 | IC3 | IC4)
+
+/// Holds info about the chip (retrieved with JEDEC standards)
+typedef struct ParallelFlashChipID
+{
+ /// The manufacturer ID
+ uint8_t manufacturer;
+ /// The device ID
+ uint8_t device;
+} ParallelFlashChipID;
+
+/// Type/layout of chips currently being addressed
+typedef enum ParallelFlashChipType
+{
+ /// Four SST39SF040 chips, 512 KB each, for a total of 2 MB
+ ParallelFlash_SST39SF040_x4,
+ /// Four M29F160FB5AN6E2 chips, 2 MB each, in 8-bit mode, for a total of 8 MB
+ ParallelFlash_M29F160FB5AN6E2_x4,
+} ParallelFlashChipType;
+
+// Tells which type of flash chip we are communicating with
+void ParallelFlash_SetChipType(ParallelFlashChipType type);
+ParallelFlashChipType ParallelFlash_ChipType(void);
+
+// Reads a set of data from all 4 chips simultaneously
+void ParallelFlash_Read(uint32_t startAddress, uint32_t *buf, uint16_t len);
+
+// Does an unlock sequence on the chips requested
+void ParallelFlash_UnlockChips(uint8_t chipsMask);
+
+// Identifies all four chips
+void ParallelFlash_IdentifyChips(ParallelFlashChipID *chips);
+
+// Erases the chips/sectors requested
+void ParallelFlash_EraseChips(uint8_t chipsMask);
+bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask);
+
+// Writes a buffer to all 4 chips simultaneously (each uint32_t contains an 8-bit portion for each chip).
+// Optimized variant of this function if we know we're writing to all 4 chips simultaneously.
+// Allows us to bypass a lot of operations involving "chipsMask".
+void ParallelFlash_WriteAllChips(uint32_t startAddress, uint32_t const *buf, uint16_t len);
+
+// Writes a buffer to a mask of requested chips (each uint32_t contains an 8-bit portion for each chip).
+void ParallelFlash_WriteSomeChips(uint32_t startAddress, uint32_t const *buf, uint16_t len, uint8_t chipsMask);
+
+#endif /* DRIVERS_PARALLEL_FLASH_H_ */
diff --git a/external_mem.c b/external_mem.c
deleted file mode 100644
index 1d8b40e..0000000
--- a/external_mem.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * external_mem.c
- *
- * Created on: Nov 25, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "external_mem.h"
-#include "ports.h"
-#include
-#include
-
-#define HIGHEST_ADDRESS_LINE 20
-
-// Default setup
-static ChipType curChipType = ChipType8BitData_4MBitSize;
-
-// Private functions
-uint32_t ExternalMem_MaskForChips(uint8_t chips);
-void ExternalMem_WaitCompletion(uint8_t chipsMask);
-
-// Allow this to be initialized more than once.
-// In case we mess with the port settings,
-// re-initializing ExternalMem should reset everything
-// to sensible defaults.
-void ExternalMem_Init(void)
-{
- // Initialize the ports connected to address/data/control lines
- Ports_Init();
-
- // Configure all address lines as outputs
- Ports_SetAddressDDR((1UL << (HIGHEST_ADDRESS_LINE + 1)) - 1);
-
- // Set all data lines as inputs
- Ports_SetDataDDR(0);
-
- // Disable all pull-ups on the data lines. They aren't needed
- // for normal operation.
- Ports_DataPullups_RMW(0, 0xFFFFFFFFUL);
-
- // Sensible defaults for address lines:
- // Write out address zero
- Ports_SetAddressOut(0);
-
- // Control lines
- Ports_SetCSDDR(1);
- Ports_SetOEDDR(1);
- Ports_SetWEDDR(1);
-
- // Default all control lines to high (de-asserted)
- ExternalMem_Deassert(SIMM_CS | SIMM_OE | SIMM_WE);
-}
-
-void ExternalMem_SetAddress(uint32_t address)
-{
- Ports_SetAddressOut(address);
-}
-
-void ExternalMem_SetData(uint32_t data)
-{
- Ports_SetDataDDR(0xFFFFFFFFUL);
- Ports_SetDataOut(data);
-}
-
-void ExternalMem_SetAddressAndData(uint32_t address, uint32_t data)
-{
- ExternalMem_SetAddress(address);
- ExternalMem_SetData(data);
-}
-
-void ExternalMem_SetDataAsInput(void)
-{
- Ports_SetDataDDR(0);
- // enable pull-ups as well -- this will give default values if a chip is
- // not responding.
- Ports_DataPullups_RMW(0xFFFFFFFFUL, 0xFFFFFFFFUL);
-}
-
-uint32_t ExternalMem_ReadData(void)
-{
- return Ports_ReadData();
-}
-
-void ExternalMem_Read(uint32_t startAddress, uint32_t *buf, uint32_t len)
-{
- // This is just a time saver if we know we will
- // be reading a complete block -- doesn't bother
- // playing with the control lines between each byte
- ExternalMem_Deassert(SIMM_WE);
- ExternalMem_SetDataAsInput();
- ExternalMem_Assert(SIMM_CS | SIMM_OE);
-
- while (len--)
- {
- ExternalMem_SetAddress(startAddress++);
- // Shouldn't need to wait here. Each clock cycle at 16 MHz is 62.5 nanoseconds, so by the time the SPI
- // read has been signaled with the SPI chip, there will DEFINITELY be good data on the data bus.
- // (Considering these chips will be in the 70 ns or 140 ns range, that's only a few clock cycles at most)
- *buf++ = ExternalMem_ReadData();
- }
-}
-
-void ExternalMem_WriteCycle(uint32_t address, uint32_t data)
-{
- ExternalMem_Assert(SIMM_CS);
- ExternalMem_Deassert(SIMM_OE | SIMM_WE);
- ExternalMem_SetAddressAndData(address, data);
- ExternalMem_Assert(SIMM_WE);
- ExternalMem_Deassert(SIMM_WE);
-}
-
-uint32_t ExternalMem_ReadCycle(uint32_t address)
-{
- ExternalMem_Deassert(SIMM_WE);
- ExternalMem_SetDataAsInput();
- ExternalMem_Assert(SIMM_CS | SIMM_OE);
- ExternalMem_SetAddress(address);
- uint32_t tmp = ExternalMem_ReadData();
- ExternalMem_Deassert(SIMM_OE);
- return tmp;
-}
-
-uint32_t ExternalMem_MaskForChips(uint8_t chips)
-{
- // This is a private function we can use to
- // ignore results from chips we don't want to address
- // (or to stop from programming them)
- uint32_t mask = 0;
-
- if (chips & (1 << 0))
- {
- mask |= 0x000000FFUL;
- }
- if (chips & (1 << 1))
- {
- mask |= 0x0000FF00UL;
- }
- if (chips & (1 << 2))
- {
- mask |= 0x00FF0000UL;
- }
- if (chips & (1 << 3))
- {
- mask |= 0xFF000000UL;
- }
-
- return mask;
-}
-
-void ExternalMem_UnlockChips(uint8_t chipsMask)
-{
- // Use a mask so we don't unlock chips we don't want to talk with
- uint32_t mask = ExternalMem_MaskForChips(chipsMask);
-
- // The unlock sequence changes depending on the chip
- if (curChipType == ChipType8BitData_4MBitSize)
- {
- // First part of unlock sequence:
- // Write 0x55555555 to the address bus and 0xAA to the data bus
- // (Some datasheets may only say 0x555 or 0x5555, but they ignore
- // the upper bits, so writing the alternating pattern to all address lines
- // should make it compatible with larger chips)
- ExternalMem_WriteCycle(0x55555555UL, 0xAAAAAAAAUL & mask);
-
- // Second part of unlock sequence is the same thing, but reversed.
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0x55555555UL & mask);
- }
- // The protocol is slightly different for 8/16-bit devices in 8-bit mode:
- else if (curChipType == ChipType8Bit16BitData_16MBitSize)
- {
- // First part of unlock sequence:
- // Write 0xAAAAAAAA to the address bus and 0xAA to the data bus
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0xAAAAAAAAUL & mask);
-
- // Second part of unlock sequence is the reversed pattern.
- ExternalMem_WriteCycle(0x55555555UL, 0x55555555UL & mask);
- }
- // shouldn't ever be a value other than those two, so I'm not writing
- // any extra code for that case.
-}
-
-void ExternalMem_IdentifyChips(struct ChipID *chips)
-{
- // Start by writing the unlock sequence to ALL chips
- // (don't bother with the chips mask in usb_serial because we're just
- // temporarily reading ALL chip states here)
- ExternalMem_UnlockChips(ALL_CHIPS);
-
- // Write 0x90 to 0x55555555 for the identify command...
- if (curChipType == ChipType8BitData_4MBitSize)
- {
- ExternalMem_WriteCycle(0x55555555UL, 0x90909090UL);
- }
- else if (curChipType == ChipType8Bit16BitData_16MBitSize)
- {
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0x90909090UL);
- }
- // shouldn't ever be a value other than those two, so I'm not writing
- // any extra code for that case.
-
- // Now we can read the vendor and product ID
- uint32_t result = ExternalMem_ReadCycle(0);
-
- chips[3].manufacturerID = (uint8_t)result;
- chips[2].manufacturerID = (uint8_t)(result >> 8);
- chips[1].manufacturerID = (uint8_t)(result >> 16);
- chips[0].manufacturerID = (uint8_t)(result >> 24);
-
- result = ExternalMem_ReadCycle(1);
-
- chips[3].deviceID = (uint8_t)result;
- chips[2].deviceID = (uint8_t)(result >> 8);
- chips[1].deviceID = (uint8_t)(result >> 16);
- chips[0].deviceID = (uint8_t)(result >> 24);
-
- // Exit software ID mode
- ExternalMem_WriteCycle(0, 0xF0F0F0F0UL);
-}
-
-void ExternalMem_EraseChips(uint8_t chipsMask)
-{
- ExternalMem_UnlockChips(chipsMask);
- if (curChipType == ChipType8BitData_4MBitSize)
- {
- ExternalMem_WriteCycle(0x55555555UL, 0x80808080UL);
- }
- else if (curChipType == ChipType8Bit16BitData_16MBitSize)
- {
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0x80808080UL);
- }
- ExternalMem_UnlockChips(chipsMask);
- if (curChipType == ChipType8BitData_4MBitSize)
- {
- ExternalMem_WriteCycle(0x55555555UL, 0x10101010UL);
- }
- else if (curChipType == ChipType8Bit16BitData_16MBitSize)
- {
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0x10101010UL);
- }
-
- 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)
- 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 (4096)
- // 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, 0x30303030UL);
-
- 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)
- // 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,
- // so we don't end up waiting for them...
- // (We probably wouldn't anyway, but this is just
- // to be safe)
- uint8_t doneChipsMask = ~chipsMask & 0x0F;
-
- // Prime the loop...
- union
- {
- uint32_t word;
- uint8_t bytes[4];
- } lastBits, tmp;
- lastBits.word = ExternalMem_ReadCycle(0);
- while (doneChipsMask != 0x0F)
- {
-#define TOGGLE_BIT 0x40
-
- tmp.word = ExternalMem_ReadCycle(0);
-
- // Note: The following assumes little endian byte ordering
- // (e.g. tmpBytes[0] is the least significant byte of tmpWord
-
- // Has this chip completed its operation? No?
- if ((doneChipsMask & (1 << 0)) == 0)
- {
- // No toggle means erase completed
- if ((tmp.bytes[0] & TOGGLE_BIT) == (lastBits.bytes[0] & TOGGLE_BIT))
- {
- doneChipsMask |= (1 << 0);
- }
- }
-
- if ((doneChipsMask & (1 << 1)) == 0)
- {
- // No toggle means erase completed
- if ((tmp.bytes[1] & TOGGLE_BIT) == (lastBits.bytes[1] & TOGGLE_BIT))
- {
- doneChipsMask |= (1 << 1);
- }
- }
-
- if ((doneChipsMask & (1 << 2)) == 0)
- {
- // No toggle means erase completed
- if ((tmp.bytes[2] & TOGGLE_BIT) == (lastBits.bytes[2] & TOGGLE_BIT))
- {
- doneChipsMask |= (1 << 2);
- }
- }
-
- if ((doneChipsMask & (1 << 3)) == 0)
- {
- // No toggle means erase completed
- if ((tmp.bytes[3] & TOGGLE_BIT) == (lastBits.bytes[3] & TOGGLE_BIT))
- {
- doneChipsMask |= (1 << 3);
- }
- }
-
- lastBits.word = tmp.word;
- }
-}
-
-void ExternalMem_WriteByteToChips(uint32_t address, uint32_t data, uint8_t chipsMask)
-{
- // Use a mask so we don't unlock chips we don't want to talk with
- uint32_t mask = ExternalMem_MaskForChips(chipsMask);
-
- ExternalMem_UnlockChips(chipsMask);
- if (curChipType == ChipType8BitData_4MBitSize)
- {
- ExternalMem_WriteCycle(0x55555555UL, 0xA0A0A0A0UL & mask);
- }
- else if (curChipType == ChipType8Bit16BitData_16MBitSize)
- {
- ExternalMem_WriteCycle(0xAAAAAAAAUL, 0xA0A0A0A0UL & mask);
- }
- ExternalMem_WriteCycle(address, data & mask);
- ExternalMem_WaitCompletion(chipsMask);
-}
-
-uint8_t ExternalMem_Write(uint32_t startAddress, uint32_t *buf, uint32_t len, uint8_t chipsMask, bool doVerify)
-{
- // Use a mask so we don't worry about chips we don't want to talk with
- uint32_t mask = ExternalMem_MaskForChips(chipsMask);
-
- while (len--)
- {
- ExternalMem_WriteByteToChips(startAddress, *buf, chipsMask);
- if (doVerify)
- {
- #define VERIFY_EXTRA_READ_TRIES 2
-
- // Read back the word we just wrote to make sure it's OK...
- uint32_t readback = ExternalMem_ReadCycle(startAddress) & mask;
- if (readback != (*buf & mask))
- {
- // We found a failure, but don't despair yet. Let's try reading
- // two more times in case it a fluke of the data toggle polling
- // algorithm.
- bool secondFailureFound = false;
- int try = 0;
-
- while ((try < VERIFY_EXTRA_READ_TRIES) && !secondFailureFound)
- {
- try++;
- readback = ExternalMem_ReadCycle(startAddress) & mask;
- if (readback != (*buf & mask))
- {
- secondFailureFound = true;
- }
- }
-
- // If we re-read it a few times and it failed again, the write
- // failed. Otherwise, it was probably just the data toggle
- // polling algorithm giving us fits.
- if (secondFailureFound)
- {
- uint8_t failMask = 0;
- // Figure out the mask of chip(s) acting up
- int x;
- for (x = 0; x < NUM_CHIPS; x++)
- {
- // Is this a chip we're working with?
- if (chipsMask & (1 << x))
- {
- if ((readback & (0xFFUL << (8*x))) != (*buf & (0xFFUL << (8*x))))
- {
- // Save the failMask in reverse order
- // (so bit 0 refers to IC1 rather than IC4)
- failMask |= (1 << ((NUM_CHIPS - 1) - x));
- }
- }
- }
- return failMask;
- }
- }
- }
-
- startAddress++;
- buf++;
- }
-
- return 0;
-}
-
-void ExternalMem_SetChipType(ChipType type)
-{
- curChipType = type;
-}
-
-ChipType ExternalMem_GetChipType(void)
-{
- return curChipType;
-}
diff --git a/external_mem.h b/external_mem.h
deleted file mode 100644
index e19f662..0000000
--- a/external_mem.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * external_mem.h
- *
- * Created on: Nov 25, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef EXTERNAL_MEM_H_
-#define EXTERNAL_MEM_H_
-
-#include
-#include
-#include "ports.h"
-
-#define NUM_CHIPS 4
-
-// Holds info about the chip (retrieved with JEDEC standards)
-struct ChipID
-{
- uint8_t manufacturerID;
- uint8_t deviceID;
-};
-
-// Masks for functions that want a chip...
-#define IC1 (1 << 3)
-#define IC2 (1 << 2)
-#define IC3 (1 << 1)
-#define IC4 (1 << 0)
-#define ALL_CHIPS (IC1 | IC2 | IC3 | IC4)
-
-// Type of SIMM currently being addressed -- determines command protocol used
-// to talk to the chips
-typedef enum ChipType
-{
- ChipType8BitData_4MBitSize, /* 512Kbit to 2Mbit flash, 8-bit */
- ChipType8Bit16BitData_16MBitSize /* 16Mbit flash, 8/16-bit in 8-bit mode */
-} ChipType;
-
-// Initializes the (bit-banged) external memory interface
-void ExternalMem_Init(void);
-
-// Sets the value outputted to the address lines
-void ExternalMem_SetAddress(uint32_t address);
-
-// Sets the value outputted to the data lines
-void ExternalMem_SetData(uint32_t data);
-
-// Sets the value outputted to the address and data lines
-void ExternalMem_SetAddressAndData(uint32_t address, uint32_t data);
-
-// Puts the data lines into input mode, for reading back stored data
-void ExternalMem_SetDataAsInput(void);
-
-// Reads back the value the chips are putting onto the data lines
-uint32_t ExternalMem_ReadData(void);
-
-// This is not the nicest-looking software engineering practice
-// in the world, but it saves needlessly wasted CPU cycles
-// that would be wasted in layers upon layers of abstraction
-#define ExternalMem_Assert(assertMask) do { Ports_ControlOff(assertMask); } while (0)
-#define ExternalMem_Deassert(assertMask) do { Ports_ControlOn(assertMask); } while (0)
-
-// Reads a set of data from all 4 chips simultaneously
-void ExternalMem_Read(uint32_t startAddress, uint32_t *buf, uint32_t len);
-
-// Performs a single write cycle on all 4 chips simultaneously
-void ExternalMem_WriteCycle(uint32_t address, uint32_t data);
-
-// Performs a single read cycle on all 4 chips simultaneously
-uint32_t ExternalMem_ReadCycle(uint32_t address);
-
-// Does an unlock sequence on the chips requested
-void ExternalMem_UnlockChips(uint8_t chipsMask);
-
-// Identifies the chips that are currently in the SIMM
-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);
-
-// Writes a buffer to the requested chips simultaneously
-// (each uint32_t contains an 8-bit portion for each chip,
-// which is masked away if the chip is not requested)
-// returns a mask representing the chips acting up (if requested with doVerify)
-// or 0 on success (or if verification was not requested)
-uint8_t ExternalMem_Write(uint32_t startAddress, uint32_t *buf, uint32_t len, uint8_t chipsMask, bool doVerify);
-
-// Tells which flash command protocol to use
-void ExternalMem_SetChipType(ChipType type);
-ChipType ExternalMem_GetChipType(void);
-
-#endif /* EXTERNAL_MEM_H_ */
diff --git a/Descriptors.c b/hal/at90usb646/Descriptors.c
similarity index 96%
rename from Descriptors.c
rename to hal/at90usb646/Descriptors.c
index 7e65eb5..712ab55 100644
--- a/Descriptors.c
+++ b/hal/at90usb646/Descriptors.c
@@ -227,12 +227,12 @@ const USB_Descriptor_String_t PROGMEM ProductString =
*/
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
const uint8_t wIndex,
- const void** const DescriptorAddress)
+ const void ** const DescriptorAddress)
{
const uint8_t DescriptorType = (wValue >> 8);
const uint8_t DescriptorNumber = (wValue & 0xFF);
- const void* Address = NULL;
+ const void *Address = NULL;
uint16_t Size = NO_DESCRIPTOR;
switch (DescriptorType)
diff --git a/Descriptors.h b/hal/at90usb646/Descriptors.h
similarity index 100%
rename from Descriptors.h
rename to hal/at90usb646/Descriptors.h
diff --git a/LUFA/CodeTemplates/DriverStubs/Buttons.h b/hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Buttons.h
similarity index 100%
rename from LUFA/CodeTemplates/DriverStubs/Buttons.h
rename to hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Buttons.h
diff --git a/LUFA/CodeTemplates/DriverStubs/Dataflash.h b/hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Dataflash.h
similarity index 100%
rename from LUFA/CodeTemplates/DriverStubs/Dataflash.h
rename to hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Dataflash.h
diff --git a/LUFA/CodeTemplates/DriverStubs/Joystick.h b/hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Joystick.h
similarity index 100%
rename from LUFA/CodeTemplates/DriverStubs/Joystick.h
rename to hal/at90usb646/LUFA/CodeTemplates/DriverStubs/Joystick.h
diff --git a/LUFA/CodeTemplates/DriverStubs/LEDs.h b/hal/at90usb646/LUFA/CodeTemplates/DriverStubs/LEDs.h
similarity index 100%
rename from LUFA/CodeTemplates/DriverStubs/LEDs.h
rename to hal/at90usb646/LUFA/CodeTemplates/DriverStubs/LEDs.h
diff --git a/LUFA/CodeTemplates/LUFAConfig.h b/hal/at90usb646/LUFA/CodeTemplates/LUFAConfig.h
similarity index 100%
rename from LUFA/CodeTemplates/LUFAConfig.h
rename to hal/at90usb646/LUFA/CodeTemplates/LUFAConfig.h
diff --git a/LUFA/CodeTemplates/makefile_template.avr8 b/hal/at90usb646/LUFA/CodeTemplates/makefile_template.avr8
similarity index 100%
rename from LUFA/CodeTemplates/makefile_template.avr8
rename to hal/at90usb646/LUFA/CodeTemplates/makefile_template.avr8
diff --git a/LUFA/CodeTemplates/makefile_template.uc3 b/hal/at90usb646/LUFA/CodeTemplates/makefile_template.uc3
similarity index 100%
rename from LUFA/CodeTemplates/makefile_template.uc3
rename to hal/at90usb646/LUFA/CodeTemplates/makefile_template.uc3
diff --git a/LUFA/CodeTemplates/makefile_template.xmega b/hal/at90usb646/LUFA/CodeTemplates/makefile_template.xmega
similarity index 100%
rename from LUFA/CodeTemplates/makefile_template.xmega
rename to hal/at90usb646/LUFA/CodeTemplates/makefile_template.xmega
diff --git a/LUFA/Common/ArchitectureSpecific.h b/hal/at90usb646/LUFA/Common/ArchitectureSpecific.h
similarity index 100%
rename from LUFA/Common/ArchitectureSpecific.h
rename to hal/at90usb646/LUFA/Common/ArchitectureSpecific.h
diff --git a/LUFA/Common/Architectures.h b/hal/at90usb646/LUFA/Common/Architectures.h
similarity index 100%
rename from LUFA/Common/Architectures.h
rename to hal/at90usb646/LUFA/Common/Architectures.h
diff --git a/LUFA/Common/Attributes.h b/hal/at90usb646/LUFA/Common/Attributes.h
similarity index 100%
rename from LUFA/Common/Attributes.h
rename to hal/at90usb646/LUFA/Common/Attributes.h
diff --git a/LUFA/Common/BoardTypes.h b/hal/at90usb646/LUFA/Common/BoardTypes.h
similarity index 100%
rename from LUFA/Common/BoardTypes.h
rename to hal/at90usb646/LUFA/Common/BoardTypes.h
diff --git a/LUFA/Common/Common.h b/hal/at90usb646/LUFA/Common/Common.h
similarity index 100%
rename from LUFA/Common/Common.h
rename to hal/at90usb646/LUFA/Common/Common.h
diff --git a/LUFA/Common/CompilerSpecific.h b/hal/at90usb646/LUFA/Common/CompilerSpecific.h
similarity index 100%
rename from LUFA/Common/CompilerSpecific.h
rename to hal/at90usb646/LUFA/Common/CompilerSpecific.h
diff --git a/LUFA/Common/Endianness.h b/hal/at90usb646/LUFA/Common/Endianness.h
similarity index 100%
rename from LUFA/Common/Endianness.h
rename to hal/at90usb646/LUFA/Common/Endianness.h
diff --git a/LUFA/Doxygen.conf b/hal/at90usb646/LUFA/Doxygen.conf
similarity index 100%
rename from LUFA/Doxygen.conf
rename to hal/at90usb646/LUFA/Doxygen.conf
diff --git a/LUFA/DoxygenPages/AboutLUFA.txt b/hal/at90usb646/LUFA/DoxygenPages/AboutLUFA.txt
similarity index 100%
rename from LUFA/DoxygenPages/AboutLUFA.txt
rename to hal/at90usb646/LUFA/DoxygenPages/AboutLUFA.txt
diff --git a/LUFA/DoxygenPages/AlternativeStacks.txt b/hal/at90usb646/LUFA/DoxygenPages/AlternativeStacks.txt
similarity index 100%
rename from LUFA/DoxygenPages/AlternativeStacks.txt
rename to hal/at90usb646/LUFA/DoxygenPages/AlternativeStacks.txt
diff --git a/LUFA/DoxygenPages/Author.jpg b/hal/at90usb646/LUFA/DoxygenPages/Author.jpg
similarity index 100%
rename from LUFA/DoxygenPages/Author.jpg
rename to hal/at90usb646/LUFA/DoxygenPages/Author.jpg
diff --git a/LUFA/DoxygenPages/BuildingLinkableLibraries.txt b/hal/at90usb646/LUFA/DoxygenPages/BuildingLinkableLibraries.txt
similarity index 100%
rename from LUFA/DoxygenPages/BuildingLinkableLibraries.txt
rename to hal/at90usb646/LUFA/DoxygenPages/BuildingLinkableLibraries.txt
diff --git a/LUFA/DoxygenPages/ChangeLog.txt b/hal/at90usb646/LUFA/DoxygenPages/ChangeLog.txt
similarity index 100%
rename from LUFA/DoxygenPages/ChangeLog.txt
rename to hal/at90usb646/LUFA/DoxygenPages/ChangeLog.txt
diff --git a/LUFA/DoxygenPages/CompileTimeTokens.txt b/hal/at90usb646/LUFA/DoxygenPages/CompileTimeTokens.txt
similarity index 100%
rename from LUFA/DoxygenPages/CompileTimeTokens.txt
rename to hal/at90usb646/LUFA/DoxygenPages/CompileTimeTokens.txt
diff --git a/LUFA/DoxygenPages/CompilingApps.txt b/hal/at90usb646/LUFA/DoxygenPages/CompilingApps.txt
similarity index 100%
rename from LUFA/DoxygenPages/CompilingApps.txt
rename to hal/at90usb646/LUFA/DoxygenPages/CompilingApps.txt
diff --git a/LUFA/DoxygenPages/ConfiguringApps.txt b/hal/at90usb646/LUFA/DoxygenPages/ConfiguringApps.txt
similarity index 100%
rename from LUFA/DoxygenPages/ConfiguringApps.txt
rename to hal/at90usb646/LUFA/DoxygenPages/ConfiguringApps.txt
diff --git a/LUFA/DoxygenPages/DevelopingWithLUFA.txt b/hal/at90usb646/LUFA/DoxygenPages/DevelopingWithLUFA.txt
similarity index 100%
rename from LUFA/DoxygenPages/DevelopingWithLUFA.txt
rename to hal/at90usb646/LUFA/DoxygenPages/DevelopingWithLUFA.txt
diff --git a/LUFA/DoxygenPages/DeviceSupport.txt b/hal/at90usb646/LUFA/DoxygenPages/DeviceSupport.txt
similarity index 100%
rename from LUFA/DoxygenPages/DeviceSupport.txt
rename to hal/at90usb646/LUFA/DoxygenPages/DeviceSupport.txt
diff --git a/LUFA/DoxygenPages/DirectorySummaries.txt b/hal/at90usb646/LUFA/DoxygenPages/DirectorySummaries.txt
similarity index 100%
rename from LUFA/DoxygenPages/DirectorySummaries.txt
rename to hal/at90usb646/LUFA/DoxygenPages/DirectorySummaries.txt
diff --git a/LUFA/DoxygenPages/Donating.txt b/hal/at90usb646/LUFA/DoxygenPages/Donating.txt
similarity index 100%
rename from LUFA/DoxygenPages/Donating.txt
rename to hal/at90usb646/LUFA/DoxygenPages/Donating.txt
diff --git a/LUFA/DoxygenPages/FutureChanges.txt b/hal/at90usb646/LUFA/DoxygenPages/FutureChanges.txt
similarity index 100%
rename from LUFA/DoxygenPages/FutureChanges.txt
rename to hal/at90usb646/LUFA/DoxygenPages/FutureChanges.txt
diff --git a/LUFA/DoxygenPages/GettingStarted.txt b/hal/at90usb646/LUFA/DoxygenPages/GettingStarted.txt
similarity index 100%
rename from LUFA/DoxygenPages/GettingStarted.txt
rename to hal/at90usb646/LUFA/DoxygenPages/GettingStarted.txt
diff --git a/LUFA/DoxygenPages/Groups.txt b/hal/at90usb646/LUFA/DoxygenPages/Groups.txt
similarity index 100%
rename from LUFA/DoxygenPages/Groups.txt
rename to hal/at90usb646/LUFA/DoxygenPages/Groups.txt
diff --git a/LUFA/DoxygenPages/LUFA.png b/hal/at90usb646/LUFA/DoxygenPages/LUFA.png
similarity index 100%
rename from LUFA/DoxygenPages/LUFA.png
rename to hal/at90usb646/LUFA/DoxygenPages/LUFA.png
diff --git a/LUFA/DoxygenPages/LUFAPoweredProjects.txt b/hal/at90usb646/LUFA/DoxygenPages/LUFAPoweredProjects.txt
similarity index 100%
rename from LUFA/DoxygenPages/LUFAPoweredProjects.txt
rename to hal/at90usb646/LUFA/DoxygenPages/LUFAPoweredProjects.txt
diff --git a/LUFA/DoxygenPages/LUFA_thumb.png b/hal/at90usb646/LUFA/DoxygenPages/LUFA_thumb.png
similarity index 100%
rename from LUFA/DoxygenPages/LUFA_thumb.png
rename to hal/at90usb646/LUFA/DoxygenPages/LUFA_thumb.png
diff --git a/LUFA/DoxygenPages/LUFAvsAtmelStack.txt b/hal/at90usb646/LUFA/DoxygenPages/LUFAvsAtmelStack.txt
similarity index 100%
rename from LUFA/DoxygenPages/LUFAvsAtmelStack.txt
rename to hal/at90usb646/LUFA/DoxygenPages/LUFAvsAtmelStack.txt
diff --git a/LUFA/DoxygenPages/LibraryApps.txt b/hal/at90usb646/LUFA/DoxygenPages/LibraryApps.txt
similarity index 100%
rename from LUFA/DoxygenPages/LibraryApps.txt
rename to hal/at90usb646/LUFA/DoxygenPages/LibraryApps.txt
diff --git a/LUFA/DoxygenPages/LibraryResources.txt b/hal/at90usb646/LUFA/DoxygenPages/LibraryResources.txt
similarity index 100%
rename from LUFA/DoxygenPages/LibraryResources.txt
rename to hal/at90usb646/LUFA/DoxygenPages/LibraryResources.txt
diff --git a/LUFA/DoxygenPages/LicenseInfo.txt b/hal/at90usb646/LUFA/DoxygenPages/LicenseInfo.txt
similarity index 100%
rename from LUFA/DoxygenPages/LicenseInfo.txt
rename to hal/at90usb646/LUFA/DoxygenPages/LicenseInfo.txt
diff --git a/LUFA/DoxygenPages/MainPage.txt b/hal/at90usb646/LUFA/DoxygenPages/MainPage.txt
similarity index 100%
rename from LUFA/DoxygenPages/MainPage.txt
rename to hal/at90usb646/LUFA/DoxygenPages/MainPage.txt
diff --git a/LUFA/DoxygenPages/MigrationInformation.txt b/hal/at90usb646/LUFA/DoxygenPages/MigrationInformation.txt
similarity index 100%
rename from LUFA/DoxygenPages/MigrationInformation.txt
rename to hal/at90usb646/LUFA/DoxygenPages/MigrationInformation.txt
diff --git a/LUFA/DoxygenPages/ProgrammingApps.txt b/hal/at90usb646/LUFA/DoxygenPages/ProgrammingApps.txt
similarity index 100%
rename from LUFA/DoxygenPages/ProgrammingApps.txt
rename to hal/at90usb646/LUFA/DoxygenPages/ProgrammingApps.txt
diff --git a/LUFA/DoxygenPages/SoftwareBootloaderJump.txt b/hal/at90usb646/LUFA/DoxygenPages/SoftwareBootloaderJump.txt
similarity index 100%
rename from LUFA/DoxygenPages/SoftwareBootloaderJump.txt
rename to hal/at90usb646/LUFA/DoxygenPages/SoftwareBootloaderJump.txt
diff --git a/LUFA/DoxygenPages/VIDAndPIDValues.txt b/hal/at90usb646/LUFA/DoxygenPages/VIDAndPIDValues.txt
similarity index 100%
rename from LUFA/DoxygenPages/VIDAndPIDValues.txt
rename to hal/at90usb646/LUFA/DoxygenPages/VIDAndPIDValues.txt
diff --git a/LUFA/DoxygenPages/WhyUseLUFA.txt b/hal/at90usb646/LUFA/DoxygenPages/WhyUseLUFA.txt
similarity index 100%
rename from LUFA/DoxygenPages/WhyUseLUFA.txt
rename to hal/at90usb646/LUFA/DoxygenPages/WhyUseLUFA.txt
diff --git a/LUFA/DoxygenPages/WritingBoardDrivers.txt b/hal/at90usb646/LUFA/DoxygenPages/WritingBoardDrivers.txt
similarity index 100%
rename from LUFA/DoxygenPages/WritingBoardDrivers.txt
rename to hal/at90usb646/LUFA/DoxygenPages/WritingBoardDrivers.txt
diff --git a/LUFA/DoxygenPages/footer.htm b/hal/at90usb646/LUFA/DoxygenPages/footer.htm
similarity index 100%
rename from LUFA/DoxygenPages/footer.htm
rename to hal/at90usb646/LUFA/DoxygenPages/footer.htm
diff --git a/LUFA/Drivers/USB/Class/CDCClass.h b/hal/at90usb646/LUFA/Drivers/USB/Class/CDCClass.h
similarity index 100%
rename from LUFA/Drivers/USB/Class/CDCClass.h
rename to hal/at90usb646/LUFA/Drivers/USB/Class/CDCClass.h
diff --git a/LUFA/Drivers/USB/Class/Common/CDCClassCommon.h b/hal/at90usb646/LUFA/Drivers/USB/Class/Common/CDCClassCommon.h
similarity index 100%
rename from LUFA/Drivers/USB/Class/Common/CDCClassCommon.h
rename to hal/at90usb646/LUFA/Drivers/USB/Class/Common/CDCClassCommon.h
diff --git a/LUFA/Drivers/USB/Class/Device/CDCClassDevice.c b/hal/at90usb646/LUFA/Drivers/USB/Class/Device/CDCClassDevice.c
similarity index 100%
rename from LUFA/Drivers/USB/Class/Device/CDCClassDevice.c
rename to hal/at90usb646/LUFA/Drivers/USB/Class/Device/CDCClassDevice.c
diff --git a/LUFA/Drivers/USB/Class/Device/CDCClassDevice.h b/hal/at90usb646/LUFA/Drivers/USB/Class/Device/CDCClassDevice.h
similarity index 100%
rename from LUFA/Drivers/USB/Class/Device/CDCClassDevice.h
rename to hal/at90usb646/LUFA/Drivers/USB/Class/Device/CDCClassDevice.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Device_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Device_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Host_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Host_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/OTG_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/OTG_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/OTG_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/OTG_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_R.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_R.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_R.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_R.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_W.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_W.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_W.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_W.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_RW.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_RW.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_RW.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Endpoint_RW.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/Template/Template_Pipe_RW.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Pipe_RW.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/Template/Template_Pipe_RW.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/Template/Template_Pipe_RW.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c
diff --git a/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h b/hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h
diff --git a/LUFA/Drivers/USB/Core/ConfigDescriptor.c b/hal/at90usb646/LUFA/Drivers/USB/Core/ConfigDescriptor.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/ConfigDescriptor.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/ConfigDescriptor.c
diff --git a/LUFA/Drivers/USB/Core/ConfigDescriptor.h b/hal/at90usb646/LUFA/Drivers/USB/Core/ConfigDescriptor.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/ConfigDescriptor.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/ConfigDescriptor.h
diff --git a/LUFA/Drivers/USB/Core/Device.h b/hal/at90usb646/LUFA/Drivers/USB/Core/Device.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/Device.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Device.h
diff --git a/LUFA/Drivers/USB/Core/DeviceStandardReq.c b/hal/at90usb646/LUFA/Drivers/USB/Core/DeviceStandardReq.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/DeviceStandardReq.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/DeviceStandardReq.c
diff --git a/LUFA/Drivers/USB/Core/DeviceStandardReq.h b/hal/at90usb646/LUFA/Drivers/USB/Core/DeviceStandardReq.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/DeviceStandardReq.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/DeviceStandardReq.h
diff --git a/LUFA/Drivers/USB/Core/Endpoint.h b/hal/at90usb646/LUFA/Drivers/USB/Core/Endpoint.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/Endpoint.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Endpoint.h
diff --git a/LUFA/Drivers/USB/Core/EndpointStream.h b/hal/at90usb646/LUFA/Drivers/USB/Core/EndpointStream.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/EndpointStream.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/EndpointStream.h
diff --git a/LUFA/Drivers/USB/Core/Events.c b/hal/at90usb646/LUFA/Drivers/USB/Core/Events.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/Events.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Events.c
diff --git a/LUFA/Drivers/USB/Core/Events.h b/hal/at90usb646/LUFA/Drivers/USB/Core/Events.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/Events.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Events.h
diff --git a/LUFA/Drivers/USB/Core/Host.h b/hal/at90usb646/LUFA/Drivers/USB/Core/Host.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/Host.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Host.h
diff --git a/LUFA/Drivers/USB/Core/HostStandardReq.c b/hal/at90usb646/LUFA/Drivers/USB/Core/HostStandardReq.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/HostStandardReq.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/HostStandardReq.c
diff --git a/LUFA/Drivers/USB/Core/HostStandardReq.h b/hal/at90usb646/LUFA/Drivers/USB/Core/HostStandardReq.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/HostStandardReq.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/HostStandardReq.h
diff --git a/LUFA/Drivers/USB/Core/OTG.h b/hal/at90usb646/LUFA/Drivers/USB/Core/OTG.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/OTG.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/OTG.h
diff --git a/LUFA/Drivers/USB/Core/Pipe.h b/hal/at90usb646/LUFA/Drivers/USB/Core/Pipe.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/Pipe.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/Pipe.h
diff --git a/LUFA/Drivers/USB/Core/PipeStream.h b/hal/at90usb646/LUFA/Drivers/USB/Core/PipeStream.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/PipeStream.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/PipeStream.h
diff --git a/LUFA/Drivers/USB/Core/StdDescriptors.h b/hal/at90usb646/LUFA/Drivers/USB/Core/StdDescriptors.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/StdDescriptors.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/StdDescriptors.h
diff --git a/LUFA/Drivers/USB/Core/StdRequestType.h b/hal/at90usb646/LUFA/Drivers/USB/Core/StdRequestType.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/StdRequestType.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/StdRequestType.h
diff --git a/LUFA/Drivers/USB/Core/USBController.h b/hal/at90usb646/LUFA/Drivers/USB/Core/USBController.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/USBController.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/USBController.h
diff --git a/LUFA/Drivers/USB/Core/USBInterrupt.h b/hal/at90usb646/LUFA/Drivers/USB/Core/USBInterrupt.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/USBInterrupt.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/USBInterrupt.h
diff --git a/LUFA/Drivers/USB/Core/USBMode.h b/hal/at90usb646/LUFA/Drivers/USB/Core/USBMode.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/USBMode.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/USBMode.h
diff --git a/LUFA/Drivers/USB/Core/USBTask.c b/hal/at90usb646/LUFA/Drivers/USB/Core/USBTask.c
similarity index 100%
rename from LUFA/Drivers/USB/Core/USBTask.c
rename to hal/at90usb646/LUFA/Drivers/USB/Core/USBTask.c
diff --git a/LUFA/Drivers/USB/Core/USBTask.h b/hal/at90usb646/LUFA/Drivers/USB/Core/USBTask.h
similarity index 100%
rename from LUFA/Drivers/USB/Core/USBTask.h
rename to hal/at90usb646/LUFA/Drivers/USB/Core/USBTask.h
diff --git a/LUFA/Drivers/USB/USB.h b/hal/at90usb646/LUFA/Drivers/USB/USB.h
similarity index 100%
rename from LUFA/Drivers/USB/USB.h
rename to hal/at90usb646/LUFA/Drivers/USB/USB.h
diff --git a/LUFA/License.txt b/hal/at90usb646/LUFA/License.txt
similarity index 100%
rename from LUFA/License.txt
rename to hal/at90usb646/LUFA/License.txt
diff --git a/LUFA/Version.h b/hal/at90usb646/LUFA/Version.h
similarity index 100%
rename from LUFA/Version.h
rename to hal/at90usb646/LUFA/Version.h
diff --git a/LUFA/makefile b/hal/at90usb646/LUFA/makefile
similarity index 100%
rename from LUFA/makefile
rename to hal/at90usb646/LUFA/makefile
diff --git a/hal/at90usb646/board.c b/hal/at90usb646/board.c
new file mode 100644
index 0000000..f988295
--- /dev/null
+++ b/hal/at90usb646/board.c
@@ -0,0 +1,34 @@
+/*
+ * board.c
+ *
+ * Created on: Nov 15, 2020
+ * Author: Doug
+ */
+
+#include "board_hw.h"
+#include
+
+/// Whether we detected that the board had a brownout event
+static bool brownout = false;
+
+/** Initializes any board hardware-specific stuff
+ *
+ */
+void Board_Init(void)
+{
+ // Figure out if a brownout occurred
+ if (MCUSR & (1 << BORF))
+ {
+ MCUSR = 0;
+ brownout = true;
+ }
+}
+
+/** Determines if a brownout was detected at startup
+ *
+ * @return True if a brownout was detected
+ */
+bool Board_BrownoutDetected(void)
+{
+ return brownout;
+}
diff --git a/hal/at90usb646/board_hw.h b/hal/at90usb646/board_hw.h
new file mode 100644
index 0000000..bad9fb3
--- /dev/null
+++ b/hal/at90usb646/board_hw.h
@@ -0,0 +1,23 @@
+/*
+ * board_hw.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_AT90USB646_BOARD_HW_H_
+#define HAL_AT90USB646_BOARD_HW_H_
+
+#include "gpio_hw.h"
+#include "../gpio.h"
+
+/** Gets the GPIO pin on the board that controls the status LED
+ *
+ * @return The status LED pin
+ */
+static inline GPIOPin Board_LEDPin(void)
+{
+ return GPIO_PIN(GPIOD, 7);
+}
+
+#endif /* HAL_AT90USB646_BOARD_HW_H_ */
diff --git a/cdc_device_definition.c b/hal/at90usb646/cdc_device_definition.c
similarity index 61%
rename from cdc_device_definition.c
rename to hal/at90usb646/cdc_device_definition.c
index c5d96e0..9c2829f 100644
--- a/cdc_device_definition.c
+++ b/hal/at90usb646/cdc_device_definition.c
@@ -25,22 +25,22 @@
#include "LUFA/Drivers/USB/USB.h"
#include "Descriptors.h"
+/// Configuration info for the USB CDC serial interface
USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
{
- .Config =
- {
- .ControlInterfaceNumber = 0,
+ .Config = {
+ .ControlInterfaceNumber = 0,
- .DataINEndpointNumber = CDC_TX_EPNUM,
- .DataINEndpointSize = CDC_TXRX_EPSIZE,
- .DataINEndpointDoubleBank = true,
+ .DataINEndpointNumber = CDC_TX_EPNUM,
+ .DataINEndpointSize = CDC_TXRX_EPSIZE,
+ .DataINEndpointDoubleBank = true,
- .DataOUTEndpointNumber = CDC_RX_EPNUM,
- .DataOUTEndpointSize = CDC_TXRX_EPSIZE,
- .DataOUTEndpointDoubleBank = true,
+ .DataOUTEndpointNumber = CDC_RX_EPNUM,
+ .DataOUTEndpointSize = CDC_TXRX_EPSIZE,
+ .DataOUTEndpointDoubleBank = true,
- .NotificationEndpointNumber = CDC_NOTIFICATION_EPNUM,
- .NotificationEndpointSize = CDC_NOTIFICATION_EPSIZE,
- .NotificationEndpointDoubleBank = false,
- },
+ .NotificationEndpointNumber = CDC_NOTIFICATION_EPNUM,
+ .NotificationEndpointSize = CDC_NOTIFICATION_EPSIZE,
+ .NotificationEndpointDoubleBank = false,
+ },
};
diff --git a/cdc_device_definition.h b/hal/at90usb646/cdc_device_definition.h
similarity index 100%
rename from cdc_device_definition.h
rename to hal/at90usb646/cdc_device_definition.h
diff --git a/hal/at90usb646/gpio.c b/hal/at90usb646/gpio.c
new file mode 100644
index 0000000..33824d5
--- /dev/null
+++ b/hal/at90usb646/gpio.c
@@ -0,0 +1,99 @@
+/*
+ * gpio.c
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#include "../gpio.h"
+#include
+
+/// Struct representing the registers belonging to an AVR's GPIO port
+typedef struct AVRGPIORegs
+{
+ /// Address of the PORT register for setting output value or enabling pullups
+ volatile uint8_t *port;
+ /// Address of the PIN register for reading input value or toggling outputs
+ volatile uint8_t *pin;
+ /// Address of the DDR register for setting whether pins are input or output
+ volatile uint8_t *ddr;
+} AVRGPIORegs;
+
+/// The GPIO ports available on the AVR
+static AVRGPIORegs const gpioRegs[] = {
+ {&PORTA, &PINA, &DDRA},
+ {&PORTB, &PINB, &DDRB},
+ {&PORTC, &PINC, &DDRC},
+ {&PORTD, &PIND, &DDRD},
+ {&PORTE, &PINE, &DDRE},
+ {&PORTF, &PINF, &DDRF},
+};
+
+/** Sets the direction of a GPIO pin.
+ *
+ * @param pin The pin
+ * @param output True if it should be an output, false if it should be an input
+ */
+void GPIO_SetDirection(GPIOPin pin, bool output)
+{
+ if (output)
+ {
+ *(gpioRegs[pin.port].ddr) |= (1 << pin.pin);
+ }
+ else
+ {
+ *(gpioRegs[pin.port].ddr) &= ~(1 << pin.pin);
+ }
+}
+
+/** Sets whether an input GPIO pin is pulled up
+ *
+ * @param pin The pin
+ * @param pullup True if it should be pulled up, false if not
+ */
+void GPIO_SetPullup(GPIOPin pin, bool pullup)
+{
+ // On the AVR, you set pullups using the PORT register that is ordinarily
+ // used for setting the output value. You just have to make sure the pin
+ // is configured as an input first. Otherwise you will modify the output.
+ GPIO_Set(pin, pullup);
+}
+
+/** Turns a GPIO pin on (sets it high)
+ *
+ * @param pin The pin
+ */
+void GPIO_SetOn(GPIOPin pin)
+{
+ *(gpioRegs[pin.port].port) |= (1 << pin.pin);
+}
+
+/** Turns a GPIO pin off (sets it low)
+ *
+ * @param pin The pin
+ */
+void GPIO_SetOff(GPIOPin pin)
+{
+ *(gpioRegs[pin.port].port) &= ~(1 << pin.pin);
+}
+
+/** Toggles a GPIO pin
+ *
+ * @param pin The pin
+ */
+void GPIO_Toggle(GPIOPin pin)
+{
+ // This is a tricky little hack the AVR provides that allows toggling
+ // without a read/modify/write operation.
+ *(gpioRegs[pin.port].pin) = (1 << pin.pin);
+}
+
+/** Reads the input status of a GPIO pin
+ *
+ * @param pin The pin
+ * @return True if it's high, false if it's low
+ */
+bool GPIO_Read(GPIOPin pin)
+{
+ return *(gpioRegs[pin.port].pin) & (1 << pin.pin);
+}
diff --git a/hal/at90usb646/gpio_hw.h b/hal/at90usb646/gpio_hw.h
new file mode 100644
index 0000000..b765728
--- /dev/null
+++ b/hal/at90usb646/gpio_hw.h
@@ -0,0 +1,22 @@
+/*
+ * gpio_hw.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_AT90USB646_GPIO_HW_H_
+#define HAL_AT90USB646_GPIO_HW_H_
+
+/// Enum representing the different GPIO ports available on the AVR.
+/// Used with the GPIOPin struct.
+enum {
+ GPIOA,
+ GPIOB,
+ GPIOC,
+ GPIOD,
+ GPIOE,
+ GPIOF
+};
+
+#endif /* HAL_AT90USB646_GPIO_HW_H_ */
diff --git a/hal/at90usb646/hardware.h b/hal/at90usb646/hardware.h
new file mode 100644
index 0000000..c6280c8
--- /dev/null
+++ b/hal/at90usb646/hardware.h
@@ -0,0 +1,49 @@
+/*
+ * hardware.h
+ *
+ * Created on: Dec 4, 2011
+ * Author: Doug
+ */
+
+#ifndef HAL_AT90USB646_HARDWARE_H_
+#define HAL_AT90USB646_HARDWARE_H_
+
+#include
+#include
+#include
+
+/** Disables interrupts
+ *
+ */
+static inline void DisableInterrupts(void)
+{
+ cli();
+}
+
+/** Enables interrupts
+ *
+ */
+static inline void EnableInterrupts(void)
+{
+ sei();
+}
+
+/** Blocks for the specified number of milliseconds
+ *
+ * @param ms The number of milliseconds to wait
+ */
+static inline void DelayMS(uint16_t ms)
+{
+ _delay_ms(ms);
+}
+
+/** Blocks for the specified number of microseconds
+ *
+ * @param us The number of microseconds to wait
+ */
+static inline void DelayUS(uint16_t us)
+{
+ _delay_us(us);
+}
+
+#endif /* HAL_AT90USB646_HARDWARE_H_ */
diff --git a/hal/at90usb646/parallel_bus.c b/hal/at90usb646/parallel_bus.c
new file mode 100644
index 0000000..c6f21a6
--- /dev/null
+++ b/hal/at90usb646/parallel_bus.c
@@ -0,0 +1,666 @@
+/*
+ * parallel_bus.c
+ *
+ * Created on: Nov 26, 2011
+ * Author: Doug
+ *
+ * For some reason, avr-gcc is super inefficient dealing with uint32_t
+ * variables. Looking at the individual bytes using a union results in much
+ * more optimized code. Every cycle counts for this. So several of these
+ * functions may seem weird with the unions, but it's faster than operating
+ * directly with the uint32_t variables.
+ *
+ * There are also a few time-critical places where I had to bypass the GPIO and
+ * SPI drivers, so it's not 100% clean. Oh well...
+ */
+
+#include "../parallel_bus.h"
+#include "../../drivers/mcp23s17.h"
+#include "../../util.h"
+#include "gpio_hw.h"
+#include
+
+/// The port object where the OE, WE, and CS pins are connected
+/// (This also happens to be where the MCP control pins are connected)
+#define FLASH_CONTROL_PORT PORTB
+/// The index of the MCP23S17 chip select pin
+#define MCP_CS_PIN 0
+/// The index of the CS pin
+#define FLASH_CS_PIN 4
+/// The index of the OE pin
+#define FLASH_OE_PIN 5
+/// The index of the WE pin
+#define FLASH_WE_PIN 6
+/// The index of the MCP23S17 reset pin
+#define MCP_RESET_PIN 7
+/// The index of the highest address line on the parallel bus
+#define PARALLEL_BUS_HIGHEST_ADDRESS_LINE 20
+
+static ALWAYS_INLINE uint8_t SPITransfer(uint8_t byte);
+static ALWAYS_INLINE void SPITransferNoRead(uint8_t byte);
+static ALWAYS_INLINE void AssertControl(uint8_t pin);
+static ALWAYS_INLINE void DeassertControl(uint8_t pin);
+
+/// The MCP23S17 device
+static MCP23S17 mcp23s17 = {
+ .spi = {
+ .csPin = {GPIOB, MCP_CS_PIN}
+ }
+};
+
+/// The reset pin for the MCP23S17
+static const GPIOPin mcpReset = {GPIOB, MCP_RESET_PIN};
+/// The /WE pin for the parallel bus
+static const GPIOPin flashWEPin = {GPIOB, FLASH_WE_PIN};
+/// The /OE pin for the parallel bus
+static const GPIOPin flashOEPin = {GPIOB, FLASH_OE_PIN};
+/// The /CS pin for the flash chip
+static const GPIOPin flashCSPin = {GPIOB, FLASH_CS_PIN};
+
+/** Initializes the 32-bit data/21-bit address parallel bus.
+ *
+ */
+void ParallelBus_Init(void)
+{
+ static bool mcpInited = false;
+ if (!mcpInited)
+ {
+ // Set up the MCP23S17
+ SPI_InitController(SPI_Controller(0));
+ mcp23s17.spi.controller = SPI_Controller(0);
+ MCP23S17_Init(&mcp23s17, mcpReset);
+ // Go ahead and let the MCP23S17 take over the SPI bus forever.
+ // There's nothing else attached to it.
+ MCP23S17_Begin(&mcp23s17);
+ mcpInited = true;
+ }
+
+ // Configure all address lines as outputs, outputting address 0
+ ParallelBus_SetAddressDir((1UL << (PARALLEL_BUS_HIGHEST_ADDRESS_LINE + 1)) - 1);
+ ParallelBus_SetAddress(0);
+
+ // Set all data lines to pulled-up inputs
+ ParallelBus_SetDataDir(0);
+ ParallelBus_SetDataPullups(0xFFFFFFFF);
+ // Note: During normal operation of read/write cycles, the pullups in the
+ // MCP23S17 will remember they are enabled, so we can do an optimization
+ // when using ParallelBus_ReadCycle/WriteCycle and assume they are already
+ // pulled up. This means we'll bypass ParallelBus_SetDataPullups.
+
+ // Control lines
+ ParallelBus_SetCSDir(true);
+ ParallelBus_SetOEDir(true);
+ ParallelBus_SetWEDir(true);
+
+ // Default to only CS asserted
+ DeassertControl(FLASH_WE_PIN);
+ DeassertControl(FLASH_OE_PIN);
+ AssertControl(FLASH_CS_PIN);
+}
+
+/** Sets the address being output on the 21-bit address bus
+ *
+ * @param address The address
+ */
+void ParallelBus_SetAddress(uint32_t address)
+{
+ // For efficiency, we talk directly to the PORT registers in this function,
+ // rather than going through the GPIO class.
+
+ // NOTE: If any of PORTA or PORTC or PORTD pins 0, 1, 4, 5, or 6 are set as
+ // inputs, this function might mess with their pull-up resistors.
+ // Only use it under normal operation when all the address pins are being
+ // used as outputs.
+ union {
+ uint32_t addr;
+ uint8_t addrBytes[4];
+ } u;
+
+ u.addr = address;
+ PORTA = u.addrBytes[0]; // A0-A7
+ PORTC = u.addrBytes[1]; // A8-A15
+ // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
+ u.addrBytes[2] = (u.addrBytes[2] & 0x03) | ((u.addrBytes[2] & 0x1C) << 2) | (PORTD & 0x8C);
+ PORTD = u.addrBytes[2];
+}
+
+/** Sets the output data on the 32-bit data bus
+ *
+ * @param data The data
+ */
+void ParallelBus_SetData(uint32_t data)
+{
+ // For efficiency, we talk directly to the PORT registers in this function,
+ // rather than going through the GPIO class.
+
+ // NOTE: If any pins of PORTE or PORTF are set as inputs, this
+ // function might mess with their pull-up resistors.
+ // Only use it under normal operation when all the data pins are being
+ // used as outputs
+ union {
+ uint32_t data;
+ uint16_t dataShorts[2];
+ uint8_t dataBytes[4];
+ } u;
+ u.data = data;
+
+ // Doing the AVR registers first makes it so we don't have to use the stack
+ // (at least according to my testing with avr-gcc)
+ PORTE = u.dataBytes[1]; // D16-D23
+ PORTF = u.dataBytes[0]; // D24-D31
+
+ // D0-D15 are part of the MCP23S17
+ MCP23S17_SetOutputs(&mcp23s17, u.dataShorts[1]);
+}
+
+/** Sets the output value of the CS pin
+ *
+ * @param high True if it should be high, false if low
+ */
+void ParallelBus_SetCS(bool high)
+{
+ GPIO_Set(flashCSPin, high);
+}
+
+/** Sets the output value of the OE pin
+ *
+ * @param high True if it should be high, false if low
+ */
+void ParallelBus_SetOE(bool high)
+{
+ GPIO_Set(flashOEPin, high);
+}
+
+/** Sets the output value of the WE pin
+ *
+ * @param high True if it should be high, false if low
+ */
+void ParallelBus_SetWE(bool high)
+{
+ GPIO_Set(flashWEPin, high);
+}
+
+/** Sets which pins on the 21-bit address bus should be outputs
+ *
+ * @param outputs Mask of pins that should be outputs. 1 = output, 0 = input
+ *
+ * Typically the address pins will be outputs. This flexibility is provided in
+ * case we want to do electrical testing.
+ */
+void ParallelBus_SetAddressDir(uint32_t outputs)
+{
+ DDRA = (outputs & 0xFF); // A0-A7
+ DDRC = ((outputs >> 8) & 0xFF); // A8-A15
+
+ // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
+ uint8_t tmp = (outputs >> 16) & 0xFF;
+ tmp = (tmp & 0x03) | ((tmp & 0x1C) << 2);
+
+ // Now, turn off the DDR bits we have to turn off,
+ // and turn on the DDR bits we have to turn on
+ // (without affecting other bits [2, 3, and 7]
+ // that we aren't supposed to touch)
+ DDRD &= (0x8C | tmp); // This should turn off all '0' bits in tmp.
+ DDRD |= tmp; // This should turn on all '1' bits in tmp
+}
+
+/** Sets which pins on the 32-bit data bus should be outputs
+ *
+ * @param outputs Mask of pins that should be outputs. 1 = output, 0 = input
+ */
+void ParallelBus_SetDataDir(uint32_t outputs)
+{
+ union {
+ uint32_t data;
+ uint16_t dataShorts[2];
+ uint8_t dataBytes[4];
+ } u;
+ u.data = outputs;
+
+ // Doing the AVR registers first makes it so we don't have to use the stack
+ DDRE = u.dataBytes[1]; // D16-D23
+ DDRF = u.dataBytes[0]; // D24-D31
+
+ // D0-D15 are part of the MCP23S17
+ MCP23S17_SetDDR(&mcp23s17, u.dataShorts[1]);
+}
+
+/** Sets the direction of the CS pin
+ *
+ * @param output True if it's an output, false if it's an input
+ *
+ * Typically this pin will be an output. This flexibility is provided in case
+ * we want to do electrical testing.
+ */
+void ParallelBus_SetCSDir(bool output)
+{
+ GPIO_SetDirection(flashCSPin, output);
+}
+
+/** Sets the direction of the OE pin
+ *
+ * @param output True if it's an output, false if it's an input
+ *
+ * Typically this pin will be an output. This flexibility is provided in case
+ * we want to do electrical testing.
+ */
+void ParallelBus_SetOEDir(bool output)
+{
+ GPIO_SetDirection(flashOEPin, output);
+}
+
+/** Sets the direction of the WE pin
+ *
+ * @param output True if it's an output, false if it's an input
+ *
+ * Typically this pin will be an output. This flexibility is provided in case
+ * we want to do electrical testing.
+ */
+void ParallelBus_SetWEDir(bool output)
+{
+ GPIO_SetDirection(flashWEPin, output);
+}
+
+/** Sets which pins on the 21-bit address bus should be pulled up (if inputs)
+ *
+ * @param pullups Mask of pins that should be pullups.
+ *
+ * This would typically only be used for testing. Under normal operation, the
+ * address bus will be outputting, so the pullups are irrelevant.
+ */
+void ParallelBus_SetAddressPullups(uint32_t pullups)
+{
+ // Pull-ups are set by writing to the data register when in input mode.
+ // MAKE SURE THE PINS ARE SET AS INPUTS FIRST! This is a cheat only
+ // possible on the AVR. Some places like SIMMElectricalTest call SetAddress
+ // followed by SetAddressPullups, which is kinda weird because it sets the
+ // same registers. But the way it's called doesn't hurt anything...
+ ParallelBus_SetAddress(pullups);
+}
+
+/** Sets which pins on the 32-bit data bus should be pulled up (if inputs)
+ *
+ * @param pullups Mask of pins that should be pullups.
+ *
+ * Typically these will be enabled in order to provide a default value if a
+ * chip isn't responding properly. Sometimes it's useful to customize it during
+ * testing though.
+ */
+void ParallelBus_SetDataPullups(uint32_t pullups)
+{
+ // NOTE: If any pins of PORTE or PORTF are set as outputs, this
+ // function might mess with their output values.
+ // Only use it when all the data pins are being used as inputs
+ union {
+ uint32_t data;
+ uint16_t dataShorts[2];
+ uint8_t dataBytes[4];
+ } u;
+ u.data = pullups;
+
+ PORTE = u.dataBytes[1]; // D16-D23
+ PORTF = u.dataBytes[0]; // D24-D31
+
+ // D0-D15 are part of the MCP23S17
+ MCP23S17_SetPullups(&mcp23s17, u.dataShorts[1]);
+}
+
+/** Sets whether the CS pin is pulled up, if it's an input.
+ *
+ * @param pullup True if the CS pin should be pulled up, false if not
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the pullup state is irrelevant.
+ */
+void ParallelBus_SetCSPullup(bool pullup)
+{
+ GPIO_SetPullup(flashCSPin, pullup);
+}
+
+/** Sets whether the OE pin is pulled up, if it's an input.
+ *
+ * @param pullup True if the OE pin should be pulled up, false if not
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the pullup state is irrelevant.
+ */
+void ParallelBus_SetOEPullup(bool pullup)
+{
+ GPIO_SetPullup(flashOEPin, pullup);
+}
+
+/** Sets whether the WE pin is pulled up, if it's an input.
+ *
+ * @param pullup True if the WE pin should be pulled up, false if not
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the pullup state is irrelevant.
+ */
+void ParallelBus_SetWEPullup(bool pullup)
+{
+ GPIO_SetPullup(flashWEPin, pullup);
+}
+
+/** Reads the current data on the address bus.
+ *
+ * @return The address bus readback
+ *
+ * This would typically only be used for testing. Under normal operation, the
+ * address bus will be outputting, so the readback is irrelevant.
+ */
+uint32_t ParallelBus_ReadAddress(void)
+{
+ uint32_t result = PINA;
+ result |= (((uint32_t)PINC) << 8);
+ uint8_t tmp = (PIND & 0x03) | ((PIND & 0x70) >> 2);
+ result |= (((uint32_t)tmp) << 16);
+
+ return result;
+}
+
+/** Reads the current data on the 32-bit data bus.
+ *
+ * @return The 32-bit data readback
+ */
+uint32_t ParallelBus_ReadData(void)
+{
+ union {
+ uint32_t data;
+ uint16_t dataShorts[2];
+ uint8_t dataBytes[4];
+ } u;
+
+ u.dataShorts[1] = MCP23S17_ReadInputs(&mcp23s17);
+
+ // Grab the other two bytes...
+ u.dataBytes[1] = PINE;
+ u.dataBytes[0] = PINF;
+
+ return u.data;
+}
+
+/** Reads the status of the CS pin, if it's set as an input.
+ *
+ * @return True if the CS pin is high, false if it's low
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the readback is irrelevant.
+ */
+bool ParallelBus_ReadCS(void)
+{
+ return GPIO_Read(flashCSPin);
+}
+
+/** Reads the status of the OE pin, if it's set as an input.
+ *
+ * @return True if the OE pin is high, false if it's low
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the readback is irrelevant.
+ */
+bool ParallelBus_ReadOE(void)
+{
+ return GPIO_Read(flashOEPin);
+}
+
+/** Reads the status of the WE pin, if it's set as an input.
+ *
+ * @return True if the WE pin is high, false if it's low
+ *
+ * This would typically only be used for testing. Under normal operation, this
+ * pin will be set as an output, so the readback is irrelevant.
+ */
+bool ParallelBus_ReadWE(void)
+{
+ return GPIO_Read(flashWEPin);
+}
+
+/** Performs a write cycle on the parallel bus.
+ *
+ * @param address The address to read from
+ * @param data The 32-bit data to write to the bus
+ *
+ * Because this function is used a lot during programming, it is super
+ * optimized and bypasses the GPIO and SPI drivers. It's a necessary evil.
+ * It makes a big difference in programming time.
+ */
+void ParallelBus_WriteCycle(uint32_t address, uint32_t data)
+{
+ // Using this union surprisingly speeds things up when assembling or
+ // interpreting a uint32_t on the AVR.
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } u;
+
+ // We should currently be in a state of "CS is asserted, OE/WE not asserted".
+ // As an optimization, operate under that assumption.
+
+ // Set address. This is basically the exact same code as ParallelBus_SetAddress,
+ // but repeated in here so we don't have any function call overhead.
+ u.word = address;
+ PORTA = u.bytes[0];
+ PORTC = u.bytes[1];
+ u.bytes[2] = (u.bytes[2] & 0x03) | ((u.bytes[2] & 0x1C) << 2) | (PORTD & 0x8C);
+ PORTD = u.bytes[2];
+
+ // Set data as outputs. Bypass the SPI/GPIO drivers for this for efficiency.
+ DDRE = 0xFF;
+ DDRF = 0xFF;
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_WRITE(0));
+ SPITransferNoRead(MCP23S17_IODIRA);
+ SPITransferNoRead(0);
+ SPITransferNoRead(0);
+ DeassertControl(MCP_CS_PIN);
+
+ // Set data. Bypass the SPI/GPIO drivers again...
+ u.word = data;
+ PORTE = u.bytes[1];
+ PORTF = u.bytes[0];
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_WRITE(0));
+ SPITransferNoRead(MCP23S17_GPIOA);
+ SPITransferNoRead(u.bytes[3]);
+ SPITransferNoRead(u.bytes[2]);
+ DeassertControl(MCP_CS_PIN);
+
+ // Assert and then deassert WE to actually do the write cycle.
+ AssertControl(FLASH_WE_PIN);
+ DeassertControl(FLASH_WE_PIN);
+
+ // Control lines are left as "CS asserted, OE/WE not asserted" here.
+}
+
+/** Performs a read cycle on the parallel bus.
+ *
+ * @param address The address to read from
+ * @return The returned 32-bit data
+ *
+ * Because this function is used a lot during programming, it is super
+ * optimized and bypasses the GPIO and SPI drivers. It's a necessary evil.
+ * It makes a big difference in programming time.
+ */
+uint32_t ParallelBus_ReadCycle(uint32_t address)
+{
+ // Using this union surprisingly speeds things up when assembling or
+ // interpreting a uint32_t on the AVR.
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } u;
+
+ // We should currently be in a state of "CS is asserted, OE/WE not asserted".
+ // As an optimization, operate under that assumption.
+
+ // Set data as inputs. Bypass the SPI/GPIO drivers for this for efficiency.
+ DDRE = 0;
+ DDRF = 0;
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_WRITE(0));
+ SPITransferNoRead(MCP23S17_IODIRA);
+ SPITransferNoRead(0xFF);
+ SPITransferNoRead(0xFF);
+ DeassertControl(MCP_CS_PIN);
+
+ // Set pull-ups on the AVR data pins so we get a default value if a chip
+ // isn't responding. We can assume the MCP23S17 has already been configured
+ // to have its inputs pulled up. On the AVR we can't assume because its
+ // pull-up state is shared by the same register used for data output.
+ PORTE = 0xFF;
+ PORTF = 0xFF;
+
+ // Assert OE so we start reading from the chip. Safe to do now that
+ // the data pins have been set as inputs.
+ AssertControl(FLASH_OE_PIN);
+
+ // Set address. This is basically the exact same code as ParallelBus_SetAddress,
+ // but repeated in here so we don't have any function call overhead.
+ u.word = address;
+ PORTA = u.bytes[0];
+ PORTC = u.bytes[1];
+ u.bytes[2] = (u.bytes[2] & 0x03) | ((u.bytes[2] & 0x1C) << 2) | (PORTD & 0x8C);
+ PORTD = u.bytes[2];
+
+ // Start the SPI read. Each clock cycle at 16 MHz is 62.5 nanoseconds. We don't want to
+ // immediately read back the data bus until the address has settled, so do some SPI
+ // preparation in the meantime.
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_READ(0));
+ SPITransferNoRead(MCP23S17_GPIOA);
+
+ // Read data. Bypass the GPIO/SPI drivers again...
+ u.bytes[1] = PINE;
+ u.bytes[0] = PINF;
+ u.bytes[3] = SPITransfer(0);
+ u.bytes[2] = SPITransfer(0);
+ DeassertControl(MCP_CS_PIN);
+
+ // Deassert OE, and we're done.
+ DeassertControl(FLASH_OE_PIN);
+
+ // Control lines are left as "CS asserted, OE/WE not asserted" here.
+
+ // Return the final value
+ return u.word;
+}
+
+/** Reads a bunch of consecutive data from the parallel bus
+ *
+ * @param startAddress The address to start reading from
+ * @param buf Buffer to store the readback
+ * @param len The number of 32-bit words to read
+ *
+ * This function is just a time saver if we know we will be reading a big block
+ * of data. It doesn't bother playing with the control lines between each byte.
+ */
+void ParallelBus_Read(uint32_t startAddress, uint32_t *buf, uint16_t len)
+{
+ // We should currently be in a state of "CS is asserted, OE/WE not asserted".
+ // As an optimization, operate under that assumption.
+
+ // Using this union surprisingly speeds things up when assembling or
+ // interpreting a uint32_t on the AVR.
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } u;
+
+ // Set data as inputs. Bypass the SPI/GPIO drivers for this for efficiency.
+ DDRE = 0;
+ DDRF = 0;
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_WRITE(0));
+ SPITransferNoRead(MCP23S17_IODIRA);
+ SPITransferNoRead(0xFF);
+ SPITransferNoRead(0xFF);
+ DeassertControl(MCP_CS_PIN);
+
+ // Set pull-ups on the AVR data pins so we get a default value if a chip
+ // isn't responding. We can assume the MCP23S17 has already been configured
+ // to have its inputs pulled up. On the AVR we can't assume because its
+ // pull-up state is shared by the same register used for data output.
+ PORTE = 0xFF;
+ PORTF = 0xFF;
+
+ // Assert OE, now the chip will start spitting out data.
+ AssertControl(FLASH_OE_PIN);
+
+ while (len--)
+ {
+ // Set address. This is basically the exact same code as ParallelBus_SetAddress,
+ // but repeated in here so we don't have any function call overhead.
+ u.word = startAddress++;
+ PORTA = u.bytes[0];
+ PORTC = u.bytes[1];
+ u.bytes[2] = (u.bytes[2] & 0x03) | ((u.bytes[2] & 0x1C) << 2) | (PORTD & 0x8C);
+ PORTD = u.bytes[2];
+
+ // Start the SPI read. Each clock cycle at 16 MHz is 62.5 nanoseconds. We don't want to
+ // immediately read back the data bus until the address has settled, so do some SPI
+ // preparation in the meantime.
+ AssertControl(MCP_CS_PIN);
+ SPITransferNoRead(MCP23S17_CONTROL_READ(0));
+ SPITransferNoRead(MCP23S17_GPIOA);
+
+ // Read data. Bypass the GPIO/SPI drivers again...
+ u.bytes[1] = PINE;
+ u.bytes[0] = PINF;
+ u.bytes[3] = SPITransfer(0);
+ u.bytes[2] = SPITransfer(0);
+ DeassertControl(MCP_CS_PIN);
+ *buf++ = u.word;
+ }
+
+ // Deassert OE once we are done
+ DeassertControl(FLASH_OE_PIN);
+
+ // Control lines are left as "CS asserted, OE/WE not asserted" here.
+}
+
+/** Writes/reads a byte to/from the MCP23S17. More optimal than using the driver.
+ *
+ * @param byte The byte to write
+ * @return The byte read back
+ */
+static ALWAYS_INLINE uint8_t SPITransfer(uint8_t byte)
+{
+ SPDR = byte;
+ while (!(SPSR & (1 << SPIF)));
+ return SPDR;
+}
+
+/** Writes a byte to the MCP23S17 without reading back the result. More optimal
+ * than using the driver.
+ *
+ * @param byte The byte to write
+ */
+static ALWAYS_INLINE void SPITransferNoRead(uint8_t byte)
+{
+ SPDR = byte;
+ while (!(SPSR & (1 << SPIF)));
+}
+
+/** Asserts a control pin
+ *
+ * @param pin Pin number of the control pin to assert
+ *
+ * This is slightly faster than using the GPIO driver because it inlines directly
+ * to a port RMW operation. Just a small optimization for performance.
+ */
+static ALWAYS_INLINE void AssertControl(uint8_t pin)
+{
+ FLASH_CONTROL_PORT &= ~(1 << pin);
+}
+
+/** Deasserts a control pin
+ *
+ * @param pin Pin number of the control pin to deassert
+ *
+ * This is slightly faster than using the GPIO driver because it inlines directly
+ * to a port RMW operation. Just a small optimization for performance.
+ */
+static ALWAYS_INLINE void DeassertControl(uint8_t pin)
+{
+ FLASH_CONTROL_PORT |= (1 << pin);
+}
diff --git a/hal/at90usb646/spi.c b/hal/at90usb646/spi.c
new file mode 100644
index 0000000..173a918
--- /dev/null
+++ b/hal/at90usb646/spi.c
@@ -0,0 +1,170 @@
+/*
+ * spi.c
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#include "../spi.h"
+#include "gpio_hw.h"
+#include
+#include
+
+/// Keep a struct of available dividers, calculate something at runtime.
+typedef struct SPIDividerInfo
+{
+ /// The divider
+ uint8_t divider;
+ /// Bit 0 = SPR0, Bit 1 = SPR1, Bit 2 = SPI2X (in SPSR)
+ uint8_t configBits;
+} SPIDividerInfo;
+
+/// List of possible SPI dividers. Must be in ascending order.
+static const SPIDividerInfo dividers[] = {
+ {2, 0b100},
+ {4, 0b000},
+ {8, 0b101},
+ {16, 0b001},
+ {32, 0b110},
+ {64, 0b010},
+ {128, 0b011}
+};
+
+/// The lone SPI controller available on this AVR
+static SPIController controller =
+{
+ .sckPin = {GPIOB, 1},
+ .mosiPin = {GPIOB, 2},
+ .misoPin = {GPIOB, 3}
+};
+
+/** Gets the SPI hardware controller at the specified index
+ *
+ * @param index The index of the controller. Only 0 is valid on this AVR.
+ * @return The SPI controller, or NULL if an invalid index is supplied
+ */
+SPIController *SPI_Controller(uint8_t index)
+{
+ // The AVR only has one SPI controller
+ return (index == 0) ? &controller : NULL;
+}
+
+/** Initializes the supplied SPI controller
+ *
+ * @param c The controller
+ */
+void SPI_InitController(SPIController *c)
+{
+ GPIO_SetDirection(c->sckPin, true);
+ GPIO_SetDirection(c->mosiPin, true);
+ GPIO_SetDirection(c->misoPin, false);
+ // Don't do anything with the rest of the registers.
+ // SPI_RequestController will handle it.
+}
+
+/** Initializes the supplied SPI device
+ *
+ * @param spi The device
+ * @param maxClock The maximum clock rate supported by the device in Hz
+ * @param mode The SPI mode (see the SPI_MODE_*, SPI_CPHA, and SPI_CPOL defines)
+ * @return True on success, false on failure
+ */
+bool SPI_InitDevice(SPIDevice *spi, uint32_t maxClock, uint8_t mode)
+{
+ GPIO_SetDirection(spi->csPin, true);
+ SPI_Deassert(spi);
+
+ // Calculate which SPI clock divider to use
+ int8_t dividerIndex = -1;
+ for (uint8_t i = 0; dividerIndex < 0 && i < sizeof(dividers)/sizeof(dividers[0]); i++)
+ {
+ if (F_CPU / (uint32_t)dividers[i].divider <= maxClock)
+ {
+ dividerIndex = (int8_t)i;
+ }
+ }
+
+ // Fill in the SPI config registers according to the requested clock
+ if (dividerIndex >= 0)
+ {
+ uint8_t dividerBits = dividers[dividerIndex].configBits;
+
+ spi->private.spcr =
+ (0 << SPIE) | // No SPI interrupts
+ (1 << SPE) | // Enable SPI
+ (0 << DORD) | // MSB first
+ (1 << MSTR) | // Master mode
+ (((mode & SPI_CPOL) ? 1 : 0) << CPOL) |
+ (((mode & SPI_CPHA) ? 1 : 0) << CPHA) |
+ ((dividerBits & 3) << SPR0);
+
+ // The only writable bit in SPSR is the SPI2X bit.
+ spi->private.spsr =
+ ((dividerBits >> 2) << SPI2X);
+
+ return true;
+ }
+ else
+ {
+ // This SPI device requires a clock slower than what we can divide down to
+ return true;
+ }
+}
+
+/** Allows an SPI device to request control of the bus.
+ *
+ * @param spi The SPI device
+ */
+void SPI_RequestBus(SPIDevice *spi)
+{
+ (void)spi;
+
+ // Set up the controller with the correct speed/mode config for this device
+ SPCR = spi->private.spcr;
+ SPSR = spi->private.spsr;
+}
+
+/** Allows an SPI device to relinquish control of the bus.
+ *
+ * @param spi The SPI device
+ */
+void SPI_ReleaseBus(SPIDevice *spi)
+{
+ (void)spi;
+}
+
+/** Asserts an SPI device's chip select pin
+ *
+ * @param spi The SPI device
+ */
+void SPI_Assert(SPIDevice *spi)
+{
+ GPIO_SetOff(spi->csPin);
+}
+
+/** Deasserts an SPI device's chip select pin
+ *
+ * @param spi The SPI device
+ */
+void SPI_Deassert(SPIDevice *spi)
+{
+ GPIO_SetOn(spi->csPin);
+}
+
+/** Transfers a single byte to/from an SPI device
+ *
+ * @param spi The SPI device
+ * @param b The byte to send
+ * @return The byte that was simultaneously received
+ */
+uint8_t SPI_RWByte(SPIDevice *spi, uint8_t b)
+{
+ // Since there's only one controller on this AVR, we don't actually care
+ // about the SPI device pointer here
+ (void)spi;
+
+ // Write the byte, wait for the write to complete, read back the result
+ SPDR = b;
+ while (!(SPSR & (1 << SPIF)));
+ return SPDR;
+}
diff --git a/hal/at90usb646/spi_private.h b/hal/at90usb646/spi_private.h
new file mode 100644
index 0000000..9bfffb9
--- /dev/null
+++ b/hal/at90usb646/spi_private.h
@@ -0,0 +1,20 @@
+/*
+ * spi_private.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_AT90USB646_SPI_PRIVATE_H_
+#define HAL_AT90USB646_SPI_PRIVATE_H_
+
+/// Private data for an SPI device on the AT90USB646
+typedef struct SPIDevicePrivate
+{
+ /// Value to write into the SPCR register (contains timing/mode info)
+ uint8_t spcr;
+ /// Value to write into the SPSR register (contains timing info)
+ uint8_t spsr;
+} SPIDevicePrivate;
+
+#endif /* HAL_AT90USB646_SPI_PRIVATE_H_ */
diff --git a/hal/at90usb646/usbcdc.c b/hal/at90usb646/usbcdc.c
new file mode 100644
index 0000000..8a9eeb8
--- /dev/null
+++ b/hal/at90usb646/usbcdc.c
@@ -0,0 +1,56 @@
+/*
+ * usbcdc.c
+ *
+ * Created on: Nov 22, 2020
+ * Author: Doug
+ */
+
+#include "../usbcdc.h"
+#include "LUFA/Drivers/USB/USB.h"
+#include "cdc_device_definition.h"
+
+/** Initializes the USB CDC device
+ *
+ */
+void USBCDC_Init(void)
+{
+ // Initialize LUFA
+ USB_Init();
+}
+
+/** Disables the USB CDC device
+ *
+ */
+void USBCDC_Disable(void)
+{
+ // Disable LUFA, this will cause us to no longer identify as a USB device
+ USB_Disable();
+}
+
+/** Main loop handler for the USB CDC device. Call from the main loop.
+ *
+ */
+void USBCDC_Check(void)
+{
+ // Do the periodic CDC and USB tasks in LUFA
+ CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
+ USB_USBTask();
+}
+
+/** LUFA event handler for when the USB configuration changes.
+ *
+ */
+void EVENT_USB_Device_ConfigurationChanged(void)
+{
+ bool ConfigSuccess = true;
+
+ ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
+}
+
+/** LUFA event handler for when a USB control request is received
+ *
+ */
+void EVENT_USB_Device_ControlRequest(void)
+{
+ CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
+}
diff --git a/hal/at90usb646/usbcdc_hw.h b/hal/at90usb646/usbcdc_hw.h
new file mode 100644
index 0000000..d92d99a
--- /dev/null
+++ b/hal/at90usb646/usbcdc_hw.h
@@ -0,0 +1,51 @@
+/*
+ * usbcdc_hw.h
+ *
+ * Created on: Nov 22, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_AT90USB646_USBCDC_HW_H_
+#define HAL_AT90USB646_USBCDC_HW_H_
+
+#include "LUFA/Drivers/USB/USB.h"
+#include "cdc_device_definition.h"
+
+/** Sends a byte over the USB CDC serial port
+ *
+ * @param byte The byte to send
+ */
+static __attribute__((always_inline)) inline void USBCDC_SendByte(uint8_t byte)
+{
+ CDC_Device_SendByte(&VirtualSerial_CDC_Interface, byte);
+}
+
+/** Sends a block of data over the USB CDC serial port
+ *
+ * @param data The data to send
+ * @param len The number of bytes
+ * @return True on success, false on failure
+ */
+static __attribute__((always_inline)) inline bool USBCDC_SendData(uint8_t const *data, uint16_t len)
+{
+ return CDC_Device_SendData(&VirtualSerial_CDC_Interface, (char const *)data, len) == ENDPOINT_RWSTREAM_NoError;
+}
+
+/** Attempts to read a byte from the USB CDC serial port
+ *
+ * @return The byte read, or -1 if there are no bytes available
+ */
+static __attribute__((always_inline)) inline int16_t USBCDC_ReadByte(void)
+{
+ return CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
+}
+
+/** Forces any transmitted data to be sent over USB immediately
+ *
+ */
+static __attribute__((always_inline)) inline void USBCDC_Flush(void)
+{
+ CDC_Device_Flush(&VirtualSerial_CDC_Interface);
+}
+
+#endif /* HAL_AT90USB646_USBCDC_HW_H_ */
diff --git a/hal/board.h b/hal/board.h
new file mode 100644
index 0000000..a4d84d8
--- /dev/null
+++ b/hal/board.h
@@ -0,0 +1,21 @@
+/*
+ * board.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_BOARD_H_
+#define HAL_BOARD_H_
+
+#include "gpio.h"
+#include "spi.h"
+
+// Commented-out functions should be static inline in each board-specific header file.
+//GPIOPin Board_LEDPin(void);
+#include "board_hw.h"
+
+void Board_Init(void);
+bool Board_BrownoutDetected(void);
+
+#endif /* HAL_BOARD_H_ */
diff --git a/hal/gpio.h b/hal/gpio.h
new file mode 100644
index 0000000..29c238b
--- /dev/null
+++ b/hal/gpio.h
@@ -0,0 +1,55 @@
+/*
+ * gpio.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_GPIO_H_
+#define HAL_GPIO_H_
+
+#include
+#include
+
+/// Creates a temporary GPIOPin struct. Used when assigning to a GPIOPin variable.
+#define GPIO_PIN(port, pin) ((GPIOPin){port, pin})
+/// A NULL GPIO pin
+#define GPIO_PIN_NULL ((GPIOPin){0xFF, 0xFF})
+
+/// The GPIO pin struct
+typedef struct GPIOPin
+{
+ /// The port the pin belongs to
+ uint8_t port;
+ /// The index of the pin on the port
+ uint8_t pin;
+} GPIOPin;
+
+void GPIO_SetDirection(GPIOPin pin, bool output);
+void GPIO_SetPullup(GPIOPin pin, bool pullup);
+void GPIO_SetOn(GPIOPin pin);
+void GPIO_SetOff(GPIOPin pin);
+void GPIO_Toggle(GPIOPin pin);
+bool GPIO_Read(GPIOPin pin);
+
+/** Sets whether a GPIO pin is outputting high or low
+ *
+ * @param pin The pin
+ * @param on True if it's high, false if it's low
+ */
+static inline void GPIO_Set(GPIOPin pin, bool on)
+{
+ on ? GPIO_SetOn(pin) : GPIO_SetOff(pin);
+}
+
+/** Determines if a GPIO pin is null
+ *
+ * @param pin The pin
+ * @return True if it's null, false if not
+ */
+static inline bool GPIO_IsNull(GPIOPin pin)
+{
+ return pin.pin == 0xFF && pin.port == 0xFF;
+}
+
+#endif /* HAL_GPIO_H_ */
diff --git a/hal/parallel_bus.h b/hal/parallel_bus.h
new file mode 100644
index 0000000..26f2244
--- /dev/null
+++ b/hal/parallel_bus.h
@@ -0,0 +1,44 @@
+/*
+ * parallel_bus.h
+ *
+ * Created on: Nov 26, 2011
+ * Author: Doug
+ */
+
+#ifndef HAL_PARALLEL_BUS_H_
+#define HAL_PARALLEL_BUS_H_
+
+#include
+#include
+
+void ParallelBus_Init(void);
+
+void ParallelBus_SetAddress(uint32_t address);
+void ParallelBus_SetData(uint32_t data);
+void ParallelBus_SetCS(bool high);
+void ParallelBus_SetOE(bool high);
+void ParallelBus_SetWE(bool high);
+
+void ParallelBus_SetAddressDir(uint32_t outputs);
+void ParallelBus_SetDataDir(uint32_t outputs);
+void ParallelBus_SetCSDir(bool output);
+void ParallelBus_SetOEDir(bool output);
+void ParallelBus_SetWEDir(bool output);
+
+void ParallelBus_SetAddressPullups(uint32_t pullups);
+void ParallelBus_SetDataPullups(uint32_t pullups);
+void ParallelBus_SetCSPullup(bool pullup);
+void ParallelBus_SetOEPullup(bool pullup);
+void ParallelBus_SetWEPullup(bool pullup);
+
+uint32_t ParallelBus_ReadAddress(void);
+uint32_t ParallelBus_ReadData(void);
+bool ParallelBus_ReadCS(void);
+bool ParallelBus_ReadOE(void);
+bool ParallelBus_ReadWE(void);
+
+void ParallelBus_WriteCycle(uint32_t address, uint32_t data);
+uint32_t ParallelBus_ReadCycle(uint32_t address);
+void ParallelBus_Read(uint32_t startAddress, uint32_t *buf, uint16_t len);
+
+#endif /* HAL_PARALLEL_BUS_H_ */
diff --git a/hal/spi.h b/hal/spi.h
new file mode 100644
index 0000000..f4bda1c
--- /dev/null
+++ b/hal/spi.h
@@ -0,0 +1,58 @@
+/*
+ * spi.h
+ *
+ * Created on: Nov 14, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_SPI_H_
+#define HAL_SPI_H_
+
+#include "gpio.h"
+#include "spi_private.h"
+
+/// CPHA bit in the SPI mode parameter of SPI_InitDevice
+#define SPI_CPHA (1 << 0)
+/// CPOL bit in the SPI mode parameter of SPI_InitDevice
+#define SPI_CPOL (1 << 1)
+
+/// Friendly names for modes 0-3 when dealing with CPHA/CPOL in SPI_InitDevice
+#define SPI_MODE_0 (0 | 0)
+#define SPI_MODE_1 (0 | SPI_CPHA)
+#define SPI_MODE_2 (SPI_CPOL | 0)
+#define SPI_MODE_3 (SPI_CPOL | SPI_CPHA)
+
+/// SPI controller
+typedef struct SPIController
+{
+ /// The serial clock pin
+ GPIOPin sckPin;
+ /// The master out/slave in pin
+ GPIOPin mosiPin;
+ /// The master in/slave out pin
+ GPIOPin misoPin;
+} SPIController;
+
+/// SPI device
+typedef struct SPIDevice
+{
+ // These two members should be filled in the struct by hand
+ /// The GPIO pin used for chip select
+ GPIOPin csPin;
+ /// The SPI controller this device belongs to
+ SPIController *controller;
+
+ // Everything below here is private
+ SPIDevicePrivate private;
+} SPIDevice;
+
+SPIController *SPI_Controller(uint8_t index);
+void SPI_InitController(SPIController *c);
+bool SPI_InitDevice(SPIDevice *spi, uint32_t maxClock, uint8_t mode);
+void SPI_RequestBus(SPIDevice *spi);
+void SPI_ReleaseBus(SPIDevice *spi);
+void SPI_Assert(SPIDevice *spi);
+void SPI_Deassert(SPIDevice *spi);
+uint8_t SPI_RWByte(SPIDevice *spi, uint8_t b);
+
+#endif /* HAL_SPI_H_ */
diff --git a/hal/usbcdc.h b/hal/usbcdc.h
new file mode 100644
index 0000000..015df28
--- /dev/null
+++ b/hal/usbcdc.h
@@ -0,0 +1,24 @@
+/*
+ * usbcdc.h
+ *
+ * Created on: Nov 22, 2020
+ * Author: Doug
+ */
+
+#ifndef HAL_USBCDC_H_
+#define HAL_USBCDC_H_
+
+#include
+#include "usbcdc_hw.h"
+
+// Note: Functions commented out should be implemented as static inline
+// functions in the board-specific header file for efficiency.
+void USBCDC_Init(void);
+void USBCDC_Disable(void);
+void USBCDC_Check(void);
+//void USBCDC_SendByte(uint8_t byte);
+//bool USBCDC_SendData(uint8_t const *data, uint16_t len);
+//int16_t USBCDC_ReadByte(void);
+//void USBCDC_Flush(void)
+
+#endif /* HAL_USBCDC_H_ */
diff --git a/led.h b/led.h
index 7d8d318..731cd24 100644
--- a/led.h
+++ b/led.h
@@ -8,12 +8,41 @@
#ifndef LED_H_
#define LED_H_
-#include
-#define PIN_MASK (1 << 7)
+#include "hal/board.h"
+#include "hal/gpio.h"
-#define LED_Init() do { DDRD |= PIN_MASK; LED_Off(); } while (0)
-#define LED_On() PORTD |= PIN_MASK
-#define LED_Off() PORTD &= ~PIN_MASK
-#define LED_Toggle() PIND = PIN_MASK
+/** Initializes the LED and turns it off
+ *
+ */
+static inline void LED_Init(void)
+{
+ GPIOPin ledPin = Board_LEDPin();
+ GPIO_SetDirection(ledPin, true);
+ GPIO_SetOff(ledPin);
+}
+
+/** Turns the LED on
+ *
+ */
+static inline void LED_On(void)
+{
+ GPIO_SetOn(Board_LEDPin());
+}
+
+/** Turns the LED off
+ *
+ */
+static inline void LED_Off(void)
+{
+ GPIO_SetOff(Board_LEDPin());
+}
+
+/** Toggles the LED
+ *
+ */
+static inline void LED_Toggle(void)
+{
+ GPIO_Toggle(Board_LEDPin());
+}
#endif /* LED_H_ */
diff --git a/main.c b/main.c
index e6245e5..75ee425 100644
--- a/main.c
+++ b/main.c
@@ -20,41 +20,51 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
+ * -----------------------------------------------------------------------------
+ *
+ * TODO: Add smarter short detection? Automatically run an electrical test at
+ * startup and leave everything in input mode if shorts are detected?
+ * I'm especially thinking about the case of SIMM control pins shorted
+ * together, like CS and OE, which will default to opposite output values.
+ * Is this even worth implementing? It's probably only useful when testing
+ * newly-built SIMMs. We would need to implement a protocol for this so
+ * the programmer software can be alerted that a short was detected.
*/
-#include
-#include
-#include
-#include "external_mem.h"
+#include "hal/board.h"
+#include "hardware.h"
+#include "hal/parallel_bus.h"
#include "tests/simm_electrical_test.h"
-#include "usb_serial/usb_serial.h"
+#include "simm_programmer.h"
#include "led.h"
+/** Main function
+ *
+ * @return Never; the main loop is an infinite loop.
+ */
int main(void)
{
- cli();
-
+ DisableInterrupts();
+ Board_Init();
LED_Init();
// If there was a brownout detected, turn on the LED momentarily
- if (MCUSR & (1 << BORF))
+ if (Board_BrownoutDetected())
{
- MCUSR = 0;
LED_On();
- _delay_ms(500);
+ DelayMS(500);
LED_Off();
}
- ExternalMem_Init();
- ExternalMem_SetAddress(0);
- ExternalMem_Assert(SIMM_CS | SIMM_OE);
- ExternalMem_Deassert(SIMM_WE);
- USBSerial_Init();
- sei();
+ // Initialize everything and turn on interrupts
+ ParallelBus_Init();
+ SIMMProgrammer_Init();
+ EnableInterrupts();
+ // Main loop
while (1)
{
- USBSerial_Check();
+ SIMMProgrammer_Check();
}
return 0;
diff --git a/mcp23s17.c b/mcp23s17.c
deleted file mode 100644
index 9b40420..0000000
--- a/mcp23s17.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * mcp23s17.c
- *
- * Created on: Nov 25, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "mcp23s17.h"
-#include
-#include
-#include
-
-static bool MCP23S17_Inited = false;
-
-// Pin definitions on PORTB
-#define SPI_CS (1 << 0)
-#define SPI_SCK (1 << 1)
-#define SPI_MOSI (1 << 2)
-#define SPI_MISO (1 << 3)
-#define MCP23S17_RESET (1 << 7)
-
-#define ASSERT_CS() PORTB &= ~SPI_CS
-#define DEASSERT_CS() PORTB |= SPI_CS;
-
-// A few register defines
-#define MCP23S17_CONTROL_WRITE(address) (0x40 | (address << 1))
-#define MCP23S17_CONTROL_READ(address) (0x40 | (address << 1) | 1)
-
-#define MCP23S17_IODIRA 0x00
-#define MCP23S17_IODIRB 0x01
-#define MCP23S17_IPOLA 0x02
-#define MCP23S17_IPOLB 0x03
-#define MCP23S17_GPINTENA 0x04
-#define MCP23S17_GPINTENB 0x05
-#define MCP23S17_DEFVALA 0x06
-#define MCP23S17_DEFVALB 0x07
-#define MCP23S17_INTCONA 0x08
-#define MCP23S17_INTCONB 0x09
-#define MCP23S17_IOCON 0x0A
-#define MCP23S17_IOCON_AGAIN 0x0B
-#define MCP23S17_GPPUA 0x0C
-#define MCP23S17_GPPUB 0x0D
-#define MCP23S17_INTFA 0x0E
-#define MCP23S17_INTFB 0x0F
-#define MCP23S17_INTCAPA 0x10
-#define MCP23S17_INTCAPB 0x11
-#define MCP23S17_GPIOA 0x12
-#define MCP23S17_GPIOB 0x13
-#define MCP23S17_OLATA 0x14
-#define MCP23S17_OLATB 0x15
-
-// Private functions
-void MCP23S17_WriteBothRegs(uint8_t addrA, uint16_t value);
-uint16_t MCP23S17_ReadBothRegs(uint8_t addrA);
-uint8_t MCP23S17_ByteRW(uint8_t b);
-
-void MCP23S17_Init()
-{
- // If it has already been initialized, no need to do it again.
- if (MCP23S17_Inited)
- {
- return;
- }
-
- // Initialize the SPI pins
- // Set MOSI, SCLK, and CS as outputs, MISO as input
- // (Also, set the MCP23S17 reset line as an output)
- DDRB |= SPI_CS | SPI_SCK | SPI_MOSI | MCP23S17_RESET;
- DDRB &= ~SPI_MISO;
-
- // Initialize the SPI peripheral
- // We can run it at 8 MHz (divider of 2 from 16 MHz system clock -- maximum speed of MCP23S17 10 MHz)
-#if ((F_CPU / 2) > 10000000)
-#error This code assumes that the CPU clock divided by 2 is less than or equal to the MCP23S17's maximum speed of 10 MHz, and in this case, it's not.
-#endif
-
- SPCR = (0 << SPIE) | // No SPI interrupts
- (1 << SPE) | // Enable SPI
- (0 << DORD) | // MSB first
- (1 << MSTR) | // Master mode
- (0 << CPOL) | // SPI mode 0,0
- (0 << CPHA) |
- (0 << SPR0); // SCK frequency = F_CPU/2 (because of SPI2X being set below
-
- SPSR = (1 << SPI2X); // Double the SPI clock rate -- allows /2 instead of /4
-
- // Leave CS deasserted
- DEASSERT_CS();
-
- // Pull the MCP23S17 out of reset (it's pulled down to GND on the board with a 100k pulldown
- // so it won't activate during AVR ISP programming)
- PORTB |= MCP23S17_RESET;
-
- _delay_ms(50);
-
- // All done!
- MCP23S17_Inited = true;
-}
-
-void MCP23S17_SetDDR(uint16_t ddr)
-{
- // The MCP23S17's DDR is backwards from the AVR's.
- // I like dealing with it so it behaves like the AVR's,
- // so I invert any DDR values in this driver.
- // In other words, when you set or get the DDR through
- // this driver, the 1s and 0s are backwards from what
- // the MCP23S17's datasheet says, but they are
- // consistent with the AVR. I value the consistency more.
- MCP23S17_WriteBothRegs(MCP23S17_IODIRA, ~ddr);
-}
-
-void MCP23S17_SetPins(uint16_t data)
-{
- MCP23S17_WriteBothRegs(MCP23S17_GPIOA, data);
-}
-
-uint16_t MCP23S17_ReadPins(void)
-{
- return MCP23S17_ReadBothRegs(MCP23S17_GPIOA);
-}
-
-void MCP23S17_SetPullups(uint16_t pullups)
-{
- MCP23S17_WriteBothRegs(MCP23S17_GPPUA, pullups);
-}
-
-// Determines the output values of output pins without reading any input pins
-uint16_t MCP23S17_GetOutputs(void)
-{
- return MCP23S17_ReadBothRegs(MCP23S17_OLATA);
-}
-
-uint16_t MCP23S17_GetDDR(void)
-{
- // As I mentioned above, DDR bits are inverted from
- // what the MCP23S17's datasheet says, but
- // consistent with what the AVR's datasheet says
- return ~MCP23S17_ReadBothRegs(MCP23S17_IODIRA);
-}
-
-uint16_t MCP23S17_GetPullups(void)
-{
- return MCP23S17_ReadBothRegs(MCP23S17_GPPUA);
-}
-
-uint8_t MCP23S17_ByteRW(uint8_t b)
-{
- SPDR = b;
- while ((SPSR & (1 << SPIF)) == 0);
- return SPDR;
-}
-
-void MCP23S17_WriteBothRegs(uint8_t addrA, uint16_t value)
-{
- // addrA should contain the address of the "A" register.
- // the chip should also be in "same bank" mode.
-
- ASSERT_CS();
-
- // Start off the communication by telling the MCP23S17 that we are writing to a register
- MCP23S17_ByteRW(MCP23S17_CONTROL_WRITE(0));
-
- // Tell it the first register we're writing to (the "A" register)
- MCP23S17_ByteRW(addrA);
-
- // Write the first byte of the register
- MCP23S17_ByteRW((uint8_t)((value >> 8) & 0xFF));
-
- // It should auto-increment to the "B" register, now write that
- MCP23S17_ByteRW((uint8_t)(value & 0xFF));
-
- DEASSERT_CS();
-}
-
-uint16_t MCP23S17_ReadBothRegs(uint8_t addrA)
-{
- uint16_t returnVal;
-
- ASSERT_CS();
-
- // Start off the communication by telling the MCP23S17 that we are reading from a register
- MCP23S17_ByteRW(MCP23S17_CONTROL_READ(0));
-
- // Tell it which register we're reading from (the "A" register)
- MCP23S17_ByteRW(addrA);
-
- // Read the first byte of the register
- returnVal = (((uint16_t)MCP23S17_ByteRW(0)) << 8);
-
- // It should auto-increment to the "B" register, now read that
- returnVal |= MCP23S17_ByteRW(0);
-
- DEASSERT_CS();
-
- return returnVal;
-}
diff --git a/mcp23s17.h b/mcp23s17.h
deleted file mode 100644
index a5d19c2..0000000
--- a/mcp23s17.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * mcp23s17.h
- *
- * Created on: Nov 25, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef MCP23S17_H_
-#define MCP23S17_H_
-
-#include
-
-void MCP23S17_Init();
-void MCP23S17_SetDDR(uint16_t ddr);
-void MCP23S17_SetPins(uint16_t data);
-uint16_t MCP23S17_ReadPins(void);
-void MCP23S17_SetPullups(uint16_t pullups);
-uint16_t MCP23S17_GetOutputs(void);
-uint16_t MCP23S17_GetDDR(void);
-uint16_t MCP23S17_GetPullups(void);
-
-#endif /* MCP23S17_H_ */
diff --git a/ports.c b/ports.c
deleted file mode 100644
index b54a3b8..0000000
--- a/ports.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * ports.c
- *
- * Created on: Nov 26, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "ports.h"
-#include "mcp23s17.h"
-
-// Save some time by not changing the register
-// unless the value has changed [SPI = relatively slow]
-static uint32_t savedDataDDR = 0;
-
-void Ports_Init(void)
-{
- // This module depends on the MPC23S17
- MCP23S17_Init();
- savedDataDDR = 0xFFFFFFFFUL;
- Ports_SetDataDDR(0);
-}
-
-void Ports_SetAddressOut(uint32_t data)
-{
- // NOTE: If any of PORTA or PORTC or PORTD pins 0, 1, 4, 5, or 6 are set as
- // inputs, this function might mess with their pull-up resistors.
- // Only use it under normal operation when all the address pins are being
- // used as outputs
-
- PORTA = (data & 0xFF); // A0-A7
- PORTC = ((data >> 8) & 0xFF); // A8-A15
-
- // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
- uint8_t tmp = (data >> 16) & 0xFF;
- tmp = (tmp & 0x03) | ((tmp & 0x1C) << 2);
-
- // Now, turn off the pins we have to turn off, and turn on the pins we have to turn on
- // (without affecting other pins [2, 3, and 7] that we aren't supposed to touch)
- PORTD &= (0x8C | tmp); // This should turn off all '0' bits in tmp.
- PORTD |= tmp; // This should turn on all '1' bits in tmp
-}
-
-void Ports_AddressOut_RMW(uint32_t data, uint32_t modifyMask)
-{
- uint32_t modifiedDataOn = data & modifyMask;
- uint32_t modifiedDataOff = data | ~modifyMask;
-
- // Turn on/off requested bits in the PORT register.
- PORTA |= ((modifiedDataOn >> 0) & 0xFF);
- PORTA &= ((modifiedDataOff >> 0) & 0xFF);
- PORTC |= ((modifiedDataOn >> 8) & 0xFF);
- PORTC &= ((modifiedDataOff >> 8) & 0xFF);
-
- // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
- uint8_t tmp = (modifiedDataOn >> 16) & 0xFF;
- tmp = (tmp & 0x03) | ((tmp & 0x1C) << 2);
-
- PORTD |= tmp;
- PORTD &= (0x8C | tmp);
-}
-
-void Ports_SetDataOut(uint32_t data)
-{
- // NOTE: If any pins of PORTE or PORTF are set as inputs, this
- // function might mess with their pull-up resistors.
- // Only use it under normal operation when all the address pins are being
- // used as outputs
-
- // Set the actual outputted values
- MCP23S17_SetPins((data >> 16) & 0xFFFF); // D0-D15
- PORTE = ((data >> 8) & 0xFF); // D16-D23
- PORTF = ((data >> 0) & 0xFF); // D24-D31
-}
-
-void Ports_DataOut_RMW(uint32_t data, uint32_t modifyMask)
-{
- uint32_t modifiedDataOn = data & modifyMask;
- uint32_t modifiedDataOff = data | ~modifyMask;
-
- // Read what's in it first...
- uint16_t outputLatches = MCP23S17_GetOutputs();
- outputLatches |= (modifiedDataOn >> 16) & 0xFFFF;
- outputLatches &= (modifiedDataOff >> 16) & 0xFFFF;
- MCP23S17_SetPins(outputLatches);
-
- // Turn on/off requested bits in the PORT register.
- PORTE |= ((modifiedDataOn >> 8) & 0xFF);
- PORTE &= ((modifiedDataOff >> 8) & 0xFF);
- PORTF |= ((modifiedDataOn >> 0) & 0xFF);
- PORTF &= ((modifiedDataOff >> 0) & 0xFF);
-}
-
-void Ports_SetAddressDDR(uint32_t ddr)
-{
- DDRA = (ddr & 0xFF); // A0-A7
- DDRC = ((ddr >> 8) & 0xFF); // A8-A15
-
- // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
- uint8_t tmp = (ddr >> 16) & 0xFF;
- tmp = (tmp & 0x03) | ((tmp & 0x1C) << 2);
-
- // Now, turn off the DDR bits we have to turn off,
- // and turn on the DDR bits we have to turn on
- // (without affecting other bits [2, 3, and 7]
- // that we aren't supposed to touch)
- DDRD &= (0x8C | tmp); // This should turn off all '0' bits in tmp.
- DDRD |= tmp; // This should turn on all '1' bits in tmp
-}
-
-void Ports_AddressDDR_RMW(uint32_t ddr, uint32_t modifyMask)
-{
- uint32_t modifiedDataOn = ddr & modifyMask;
- uint32_t modifiedDataOff = ddr | ~modifyMask;
-
- // Turn on/off requested bits in the DDR register.
- DDRA |= ((modifiedDataOn >> 0) & 0xFF);
- DDRA &= ((modifiedDataOff >> 0) & 0xFF);
- DDRC |= ((modifiedDataOn >> 8) & 0xFF);
- DDRC &= ((modifiedDataOff >> 8) & 0xFF);
-
- // A16-A20 are special because they are split up...(We use PORTD pins 0, 1, 4, 5, 6)
- uint8_t tmp = (modifiedDataOn >> 16) & 0xFF;
- tmp = (tmp & 0x03) | ((tmp & 0x1C) << 2);
-
- DDRD |= tmp;
- DDRD &= (0x8C | tmp);
-}
-
-void Ports_SetDataDDR(uint32_t ddr)
-{
- if (savedDataDDR != ddr)
- {
- MCP23S17_SetDDR((ddr >> 16) & 0xFFFF); // D0-D15
- DDRE = ((ddr >> 8) & 0xFF); // D16-D23
- DDRF = ((ddr >> 0) & 0xFF); // D24-D31
-
- savedDataDDR = ddr;
- }
-}
-
-void Ports_DataDDR_RMW(uint32_t ddr, uint32_t modifyMask)
-{
- uint32_t newSavedDataDDR;
- uint32_t modifiedDataOn = ddr & modifyMask;
- uint32_t modifiedDataOff = ddr | ~modifyMask;
-
- // If we can get away with it, don't bother reading back...
- if (((modifyMask >> 16) & 0xFFFF) == 0xFFFF)
- {
- MCP23S17_SetDDR((modifiedDataOn >> 16) & 0xFFFF);
-
- // Remember what the new DDR will be
- newSavedDataDDR = (modifiedDataOn & 0xFFFF0000UL);
- }
- else // Otherwise, we have to read what's in it first...(unless I decide to keep a local cached copy)
- {
- uint16_t outputLatches = MCP23S17_GetDDR();
- outputLatches |= (modifiedDataOn >> 16) & 0xFFFF;
- outputLatches &= (modifiedDataOff >> 16) & 0xFFFF;
- MCP23S17_SetDDR(outputLatches);
-
- // Remember what the new DDR will be
- newSavedDataDDR = ((uint32_t)outputLatches << 16);
- }
-
- // Turn on/off requested bits in the DDR register.
- DDRE |= ((modifiedDataOn >> 8) & 0xFF);
- DDRE &= ((modifiedDataOff >> 8) & 0xFF);
- DDRF |= ((modifiedDataOn >> 0) & 0xFF);
- DDRF &= ((modifiedDataOff >> 0) & 0xFF);
-
- // Remember what the new DDR will be
- newSavedDataDDR |= ((uint32_t)DDRE) << 8;
- newSavedDataDDR |= ((uint32_t)DDRF) << 0;
-
- // Save the new DDR
- savedDataDDR = newSavedDataDDR;
-}
-
-void Ports_SetCSDDR(bool ddr)
-{
- if (ddr)
- {
- DDRB |= SIMM_CS;
- }
- else
- {
- DDRB &= ~SIMM_CS;
- }
-}
-
-void Ports_SetOEDDR(bool ddr)
-{
- if (ddr)
- {
- DDRB |= SIMM_OE;
- }
- else
- {
- DDRB &= ~SIMM_OE;
- }
-}
-
-void Ports_SetWEDDR(bool ddr)
-{
- if (ddr)
- {
- DDRB |= SIMM_WE;
- }
- else
- {
- DDRB &= ~SIMM_WE;
- }
-}
-
-void Ports_AddressPullups_RMW(uint32_t pullups, uint32_t modifyMask)
-{
- // Pull-ups are set by writing to the data register when in input mode.
- // MAKE SURE THE PINS ARE SET AS INPUTS FIRST!
- Ports_AddressOut_RMW(pullups, modifyMask);
-}
-
-void Ports_DataPullups_RMW(uint32_t pullups, uint32_t modifyMask)
-{
- // Pull-ups here are a little more tricky because the MCP23S17 has
- // separate registers for pull-up enable.
- uint32_t modifiedDataOn = pullups & modifyMask;
- uint32_t modifiedDataOff = pullups | ~modifyMask;
-
- // If we can get away with it, don't bother reading back...
- if (((modifyMask >> 16) & 0xFFFF) == 0xFFFF)
- {
- MCP23S17_SetPullups((modifiedDataOn >> 16) & 0xFFFF);
- }
- else // Otherwise, we have to read what's in it first...(unless I decide to keep a local cached copy)
- {
- uint16_t outputLatches = MCP23S17_GetPullups();
- outputLatches |= (modifiedDataOn >> 16) & 0xFFFF;
- outputLatches &= (modifiedDataOff >> 16) & 0xFFFF;
- MCP23S17_SetPullups(outputLatches);
- }
-
- // Turn on/off requested bits in the PORT register for the other 16 bits.
- PORTE |= ((modifiedDataOn >> 8) & 0xFF);
- PORTE &= ((modifiedDataOff >> 8) & 0xFF);
- PORTF |= ((modifiedDataOn >> 0) & 0xFF);
- PORTF &= ((modifiedDataOff >> 0) & 0xFF);
-}
-
-void Ports_SetCSPullup(bool pullup)
-{
- if (pullup)
- {
- PORTB |= SIMM_CS;
- }
- else
- {
- PORTB &= ~SIMM_CS;
- }
-}
-
-void Ports_SetOEPullup(bool pullup)
-{
- if (pullup)
- {
- PORTB |= SIMM_OE;
- }
- else
- {
- PORTB &= ~SIMM_OE;
- }
-}
-
-void Ports_SetWEPullup(bool pullup)
-{
- if (pullup)
- {
- PORTB |= SIMM_WE;
- }
- else
- {
- PORTB &= ~SIMM_WE;
- }
-}
-
-
-uint32_t Ports_ReadAddress(void)
-{
- uint32_t result = PINA;
- result |= (((uint32_t)PINC) << 8);
- uint8_t tmp = (PIND & 0x03) | ((PIND & 0x70) >> 2);
- result |= (((uint32_t)tmp) << 16);
-
- return result;
-}
-
-uint32_t Ports_ReadData(void)
-{
- uint32_t result = (uint32_t)MCP23S17_ReadPins() << 16;
-
- // Grab the other two bytes...
- result |= (((uint32_t)PINE) << 8);
- result |= (((uint32_t)PINF) << 0);
-
- return result;
-}
-
-bool Ports_ReadCS(void)
-{
- return (PINB & SIMM_CS) != 0;
-}
-
-bool Ports_ReadOE(void)
-{
- return (PINB & SIMM_OE) != 0;
-}
-
-bool Ports_ReadWE(void)
-{
- return (PINB & SIMM_WE) != 0;
-}
diff --git a/ports.h b/ports.h
deleted file mode 100644
index 1fff456..0000000
--- a/ports.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * ports.h
- *
- * Created on: Nov 26, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef PORTS_H_
-#define PORTS_H_
-
-#include
-#include
-#include
-
-// Under normal operation, we will only be using the address pins as outputs.
-// The data pins will switch back and forth between all inputs and all outputs.
-// The CS/OE/WE pins will always be outputs.
-// The pullups will all be turned off.
-// So you should use Ports_SetAddressOut(),
-// Ports_SetDataOut(),
-// Ports_SetAddressDDR() [once at the beginning]
-// Ports_SetDataDDR(),
-// and Ports_ReadData
-//
-// The reason I have implemented all this functionality is to give me complete
-// control over all the pins for other use cases, such as a SIMM electrical test.
-// By playing with pull-ups and inputs and outputs, I should be able to detect
-// many shorted output/input scenarios. So even though these functions are overkill,
-// they will be useful for diagnostics.
-
-// I feel kind of sick making these available to the outside world, but
-// I'm doing it for efficiency because they change fairly often.
-// These are the bits on port B corresponding to the control signals.
-#define SIMM_WE (1 << 6)
-#define SIMM_OE (1 << 5)
-#define SIMM_CS (1 << 4)
-
-void Ports_Init(void);
-
-void Ports_SetAddressOut(uint32_t data);
-void Ports_AddressOut_RMW(uint32_t data, uint32_t modifyMask);
-void Ports_SetDataOut(uint32_t data);
-void Ports_DataOut_RMW(uint32_t data, uint32_t modifyMask);
-#define Ports_SetCSOut(data) do { if (data) Ports_ControlOn(SIMM_CS); else Ports_ControlOff(SIMM_CS); } while (0)
-#define Ports_SetOEOut(data) do { if (data) Ports_ControlOn(SIMM_OE); else Ports_ControlOff(SIMM_OE); } while (0)
-#define Ports_SetWEOut(data) do { if (data) Ports_ControlOn(SIMM_WE); else Ports_ControlOff(SIMM_WE); } while (0)
-
-void Ports_SetAddressDDR(uint32_t ddr);
-void Ports_AddressDDR_RMW(uint32_t ddr, uint32_t modifyMask);
-void Ports_SetDataDDR(uint32_t ddr);
-void Ports_DataDDR_RMW(uint32_t ddr, uint32_t modifyMask);
-void Ports_SetCSDDR(bool ddr);
-void Ports_SetOEDDR(bool ddr);
-void Ports_SetWEDDR(bool ddr);
-
-void Ports_AddressPullups_RMW(uint32_t pullups, uint32_t modifyMask);
-void Ports_DataPullups_RMW(uint32_t pullups, uint32_t modifyMask);
-void Ports_SetCSPullup(bool pullup);
-void Ports_SetOEPullup(bool pullup);
-void Ports_SetWEPullup(bool pullup);
-
-uint32_t Ports_ReadAddress(void);
-uint32_t Ports_ReadData(void);
-bool Ports_ReadCS(void);
-bool Ports_ReadOE(void);
-bool Ports_ReadWE(void);
-
-// These two functions are for turning control signals on and off.
-// Pass it a mask of SIMM_WE, SIMM_OE, and SIMM_CS.
-#define Ports_ControlOn(mask) do { PORTB |= (mask); } while (0)
-#define Ports_ControlOff(mask) do { PORTB &= ~(mask); } while (0)
-
-#endif /* PORTS_H_ */
diff --git a/programmer_protocol.h b/programmer_protocol.h
index df2a118..b37008e 100644
--- a/programmer_protocol.h
+++ b/programmer_protocol.h
@@ -22,13 +22,11 @@
*
*/
-
-
#ifndef PROGRAMMER_PROTOCOL_H_
#define PROGRAMMER_PROTOCOL_H_
-// When the programmer is in "waiting for command" mode,
-// you send it one of the bytes below to do something (or begin to do something)
+/// When the programmer is in "waiting for command" mode,
+/// you send it one of the bytes below to do something (or begin to do something)
typedef enum ProgrammerCommand
{
EnterWaitingMode = 0,
diff --git a/simm_programmer.c b/simm_programmer.c
new file mode 100644
index 0000000..9594e5d
--- /dev/null
+++ b/simm_programmer.c
@@ -0,0 +1,661 @@
+/*
+ * simm_programmer.c
+ *
+ * Created on: Dec 9, 2011
+ * Author: Doug
+ *
+ * Copyright (C) 2011-2012 Doug Brown
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "simm_programmer.h"
+#include "hal/usbcdc.h"
+#include "drivers/parallel_flash.h"
+#include "tests/simm_electrical_test.h"
+#include "programmer_protocol.h"
+#include "led.h"
+#include "hardware.h"
+#include
+
+/// Maximum size of an individual chip on a SIMM we read
+#define MAX_CHIP_SIZE (2UL * 1024UL * 1024UL)
+/// Number of bytes we read/write at once
+#define READ_WRITE_CHUNK_SIZE_BYTES 1024UL
+/// Make sure the chunk size is a multiple of 4 bytes, since there are 4 chips
+#if ((READ_WRITE_CHUNK_SIZE_BYTES % 4) != 0)
+#error Read/write chunk size should be a multiple of 4 bytes
+#endif
+/// The smallest granularity for sector erase that we support
+#define ERASE_SECTOR_SIZE_BYTES (256UL * 1024UL)
+
+/// Internal state so we know how to interpret the next-received byte
+typedef enum ProgrammerCommandState
+{
+ WaitingForCommand = 0, //!< No active commands
+ ReadingChipsReadLength, //!< Reading the length for reading data from the SIMM
+ ReadingChips, //!< Reading data from the SIMM
+ WritingChips, //!< Writing data to the SIMM
+ ErasePortionReadingPosLength,//!< Reading the length of SIMM data to erase
+ ReadingChipsReadStartPos, //!< Reading the start position for reading data from the SIMM
+ WritingChipsReadingStartPos, //!< Reading the start position for writing data to the SIMM
+ ReadingChipsMask, //!< Reading the bitmask of which chips should be programmed
+} ProgrammerCommandState;
+static ProgrammerCommandState curCommandState = WaitingForCommand;
+
+// State info for reading/writing
+static uint16_t curReadIndex;
+static uint32_t readLength;
+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;
+static uint8_t chipsMask = ALL_CHIPS;
+
+/// Buffers we use to store incoming/outgoing data.
+static union
+{
+ uint32_t words[READ_WRITE_CHUNK_SIZE_BYTES / PARALLEL_FLASH_NUM_CHIPS];
+ uint8_t bytes[READ_WRITE_CHUNK_SIZE_BYTES];
+} writeChunks, readChunks;
+
+// Private functions
+static void SIMMProgrammer_HandleWaitingForCommandByte(uint8_t byte);
+static void SIMMProgrammer_HandleReadingChipsByte(uint8_t byte);
+static void SIMMProgrammer_HandleReadingChipsReadLengthByte(uint8_t byte);
+static void SIMMProgrammer_SendReadDataChunk(void);
+static void SIMMProgrammer_HandleWritingChipsByte(uint8_t byte);
+static void SIMMProgrammer_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2);
+static void SIMMProgrammer_HandleErasePortionReadPosLengthByte(uint8_t byte);
+static void SIMMProgrammer_HandleReadingChipsReadStartPosByte(uint8_t byte);
+static void SIMMProgrammer_HandleWritingChipsReadingStartPosByte(uint8_t byte);
+static void SIMMProgrammer_HandleReadingChipsMaskByte(uint8_t byte);
+
+/** Initializes the SIMM programmer and prepares it for USB communication.
+ *
+ */
+void SIMMProgrammer_Init(void)
+{
+ USBCDC_Init();
+}
+
+/** Allows the SIMM programmer to do its thing. Main loop handler.
+ *
+ * Call this function during every main loop iteration.
+ */
+void SIMMProgrammer_Check(void)
+{
+ // Read as many bytes as we can and process them
+ int16_t result;
+ while ((result = USBCDC_ReadByte()) >= 0)
+ {
+ uint8_t recvByte = (uint8_t)result;
+
+ // Hand it off to the correct handler function based on the current state
+ switch (curCommandState)
+ {
+ case WaitingForCommand:
+ SIMMProgrammer_HandleWaitingForCommandByte(recvByte);
+ break;
+ case ReadingChipsReadLength:
+ SIMMProgrammer_HandleReadingChipsReadLengthByte(recvByte);
+ break;
+ case ReadingChips:
+ SIMMProgrammer_HandleReadingChipsByte(recvByte);
+ break;
+ case WritingChips:
+ SIMMProgrammer_HandleWritingChipsByte(recvByte);
+ break;
+ case ErasePortionReadingPosLength:
+ SIMMProgrammer_HandleErasePortionReadPosLengthByte(recvByte);
+ break;
+ case ReadingChipsReadStartPos:
+ SIMMProgrammer_HandleReadingChipsReadStartPosByte(recvByte);
+ break;
+ case WritingChipsReadingStartPos:
+ SIMMProgrammer_HandleWritingChipsReadingStartPosByte(recvByte);
+ break;
+ case ReadingChipsMask:
+ SIMMProgrammer_HandleReadingChipsMaskByte(recvByte);
+ break;
+ }
+ }
+
+ // And do any periodic USB CDC tasks
+ USBCDC_Check();
+}
+
+/** Handles a received byte when we are waiting for a command
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleWaitingForCommandByte(uint8_t byte)
+{
+ switch (byte)
+ {
+ // Asked to enter waiting mode -- we're already there, so say OK.
+ case EnterWaitingMode:
+ USBCDC_SendByte(CommandReplyOK);
+ curCommandState = WaitingForCommand;
+ break;
+ // Asked to do the electrical test. Reply OK, and then do the test,
+ // sending whatever replies necessary
+ case DoElectricalTest:
+ USBCDC_SendByte(CommandReplyOK);
+ // Flush out the initial "OK" reply immediately in this case so the
+ // caller gets immediate feedback that the test has started
+ USBCDC_Flush();
+ SIMMElectricalTest_Run(SIMMProgrammer_ElectricalTest_Fail_Handler);
+ USBCDC_SendByte(ProgrammerElectricalTestDone);
+ curCommandState = WaitingForCommand;
+ break;
+ // Asked to identify the chips in the SIMM. Identify them and send reply.
+ case IdentifyChips:
+ {
+ struct ParallelFlashChipID chips[PARALLEL_FLASH_NUM_CHIPS];
+ USBCDC_SendByte(CommandReplyOK);
+ ParallelFlash_IdentifyChips(chips);
+ for (int i = 0; i < PARALLEL_FLASH_NUM_CHIPS; i++)
+ {
+ USBCDC_SendByte(chips[i].manufacturer);
+ USBCDC_SendByte(chips[i].device);
+ }
+ USBCDC_SendByte(ProgrammerIdentifyDone);
+ break;
+ }
+ // Asked to read a single byte from each SIMM. Change the state and reply.
+ case ReadByte:
+ USBCDC_SendByte(CommandReplyInvalid); // not implemented yet
+ break;
+ // Asked to read all four chips. Set the state, reply with the first chunk.
+ // This will read from the BEGINNING of the SIMM every time. Use
+ // ReadChipsAt to specify a start position
+ case ReadChips:
+ curCommandState = ReadingChipsReadLength;
+ curReadIndex = 0;
+ readLengthByteIndex = 0;
+ readLength = 0;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case ReadChipsAt:
+ curCommandState = ReadingChipsReadStartPos;
+ curReadIndex = 0;
+ readLengthByteIndex = 0;
+ readLength = 0;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ // Erase the chips and reply OK. (TODO: Sometimes erase might fail)
+ case EraseChips:
+ ParallelFlash_EraseChips(chipsMask);
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ // Begin writing the chips. Change the state, reply, wait for chunk of data
+ case WriteChips:
+ curCommandState = WritingChips;
+ curWriteIndex = 0;
+ writePosInChunk = -1;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case WriteChipsAt:
+ curCommandState = WritingChipsReadingStartPos;
+ curWriteIndex = 0;
+ readLengthByteIndex = 0;
+ writePosInChunk = -1;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ // Asked for the current bootloader state. We are in the program right now,
+ // so reply accordingly.
+ case GetBootloaderState:
+ USBCDC_SendByte(CommandReplyOK);
+ USBCDC_SendByte(BootloaderStateInProgrammer);
+ break;
+ // Enter the bootloader. Wait a bit, then jump to the bootloader location.
+ case EnterBootloader:
+ USBCDC_SendByte(CommandReplyOK);
+ // Force this to be sent immediately so the programmer software knows.
+ USBCDC_Flush();
+
+ // Insert a small delay to ensure that it arrives before rebooting.
+ DelayMS(1000);
+
+ // Done with the USB interface -- the bootloader will re-initialize it.
+ USBCDC_Disable();
+
+ // Disable interrupts so nothing weird happens...
+ cli();
+
+ // Wait a little bit to let everything settle and let the program
+ // close the port after the USB disconnect
+ DelayMS(2000);
+
+ // And, of course, go into the bootloader.
+ __asm__ __volatile__ ( "jmp 0xE000" );
+ break;
+ // Enter the programmer. We're already there, so reply OK.
+ case EnterProgrammer:
+ // Already in the programmer
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ // Set the SIMM type to the older, smaller chip size (2MB and below)
+ case SetSIMMTypePLCC32_2MB:
+ ParallelFlash_SetChipType(ParallelFlash_SST39SF040_x4);
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case SetSIMMTypeLarger:
+ ParallelFlash_SetChipType(ParallelFlash_M29F160FB5AN6E2_x4);
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case SetVerifyWhileWriting:
+ verifyDuringWrite = true;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case SetNoVerifyWhileWriting:
+ verifyDuringWrite = false;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case ErasePortion:
+ readLengthByteIndex = 0;
+ eraseLength = 0;
+ erasePosition = 0;
+ curCommandState = ErasePortionReadingPosLength;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ case SetChipsMask:
+ curCommandState = ReadingChipsMask;
+ USBCDC_SendByte(CommandReplyOK);
+ break;
+ // We don't know what this command is, so reply that it was invalid.
+ default:
+ USBCDC_SendByte(CommandReplyInvalid);
+ break;
+ }
+}
+
+/** Handles a received byte when we are reading from chips
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleReadingChipsByte(uint8_t byte)
+{
+ // The byte should be a reply from the computer. It should be either:
+ // 1) ComputerReadOK -- meaning it got the chunk we just sent
+ // or
+ // 2) ComputerReadCancel -- meaning the user canceled the read
+ switch (byte)
+ {
+ case ComputerReadOK:
+ // If they have confirmed the final data chunk, let them know
+ // that they have finished, and enter command state.
+ if (curReadIndex >= readLength)
+ {
+ LED_Off();
+ USBCDC_SendByte(ProgrammerReadFinished);
+ curCommandState = WaitingForCommand;
+ }
+ else // There's more data left to read, so read it and send it to them!
+ {
+ LED_Toggle();
+ USBCDC_SendByte(ProgrammerReadMoreData);
+ SIMMProgrammer_SendReadDataChunk();
+ }
+ break;
+ case ComputerReadCancel:
+ // If they've canceled, let them know we got their request and go back
+ // to "waiting for command" state
+ USBCDC_SendByte(ProgrammerReadConfirmCancel);
+ curCommandState = WaitingForCommand;
+ break;
+ }
+}
+
+/** Handles a received byte when we are reading the length of data requested to be read
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleReadingChipsReadLengthByte(uint8_t byte)
+{
+ // There will be four bytes, so count up until we know the length. If they
+ // have sent all four bytes, send the first read chunk.
+ readLength |= (((uint32_t)byte) << (8*readLengthByteIndex));
+ if (++readLengthByteIndex >= 4)
+ {
+ // Ensure it's within limits and a multiple of 1024
+ if ((curReadIndex + readLength > PARALLEL_FLASH_NUM_CHIPS * MAX_CHIP_SIZE) ||
+ (readLength % READ_WRITE_CHUNK_SIZE_BYTES) ||
+ (curReadIndex % READ_WRITE_CHUNK_SIZE_BYTES) ||
+ (readLength == 0))// Ensure it's within limits and a multiple of 1024
+ {
+ USBCDC_SendByte(ProgrammerReadError);
+ curCommandState = WaitingForCommand;
+ }
+ else
+ {
+ // Convert the length/pos into the number of chunks we need to send
+ readLength /= READ_WRITE_CHUNK_SIZE_BYTES;
+ curReadIndex /= READ_WRITE_CHUNK_SIZE_BYTES;
+ curCommandState = ReadingChips;
+ USBCDC_SendByte(ProgrammerReadOK);
+ SIMMProgrammer_SendReadDataChunk();
+ }
+ }
+}
+
+/** Reads a chunk of data from the SIMM and sends it over the USB CDC serial port.
+ *
+ */
+static void SIMMProgrammer_SendReadDataChunk(void)
+{
+ // Read the next chunk of data, send it over USB, and make sure
+ // we sent it correctly.
+ ParallelFlash_Read(curReadIndex * (READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS),
+ readChunks.words, READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS);
+ bool retVal = USBCDC_SendData(readChunks.bytes, READ_WRITE_CHUNK_SIZE_BYTES);
+
+ // If for some reason there was an error, mark it as such. Otherwise,
+ // increment our pointer so we know the next chunk of data to send.
+ if (!retVal)
+ {
+ //curCommandState = ReadingChipsUnableSendError; // TODO: not implemented
+ curCommandState = WaitingForCommand;
+ }
+ else
+ {
+ curReadIndex++;
+ }
+}
+
+/** Handles a received byte when we are in the "writing chips" state
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleWritingChipsByte(uint8_t byte)
+{
+ // This means we have just started the entire process or just finished
+ // a chunk, so see what the computer has decided for us to do.
+ if (writePosInChunk == -1)
+ {
+ switch (byte)
+ {
+ // The computer asked to write more data to the SIMM.
+ case ComputerWriteMore:
+ writePosInChunk = 0;
+ // Make sure we don't write past the capacity of the chips.
+ if (curWriteIndex < MAX_CHIP_SIZE / (READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS))
+ {
+ USBCDC_SendByte(ProgrammerWriteOK);
+ }
+ else
+ {
+ LED_Off();
+ USBCDC_SendByte(ProgrammerWriteError);
+ curCommandState = WaitingForCommand;
+ }
+ break;
+ // The computer said that it's done writing.
+ case ComputerWriteFinish:
+ LED_Off();
+ USBCDC_SendByte(ProgrammerWriteOK);
+ curCommandState = WaitingForCommand;
+ break;
+ // The computer asked to cancel.
+ case ComputerWriteCancel:
+ LED_Off();
+ USBCDC_SendByte(ProgrammerWriteConfirmCancel);
+ curCommandState = WaitingForCommand;
+ break;
+ }
+ }
+ else // Interpret the incoming byte as data to write to the SIMM.
+ {
+ // Save the byte, and check if we've filled up an entire chunk
+ writeChunks.bytes[writePosInChunk++] = byte;
+ if (writePosInChunk >= READ_WRITE_CHUNK_SIZE_BYTES)
+ {
+ // We filled up the chunk, write it out and confirm it, then wait
+ // for the next command from the computer!
+ if (chipsMask == ALL_CHIPS)
+ {
+ ParallelFlash_WriteAllChips(curWriteIndex * (READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS),
+ writeChunks.words, READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS);
+ }
+ else
+ {
+ ParallelFlash_WriteSomeChips(curWriteIndex * (READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS),
+ writeChunks.words, READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS, chipsMask);
+ }
+
+ // Verify if we were asked to.
+ uint8_t badVerifyChipsMask = 0;
+ if (verifyDuringWrite)
+ {
+ // Read back a chunk
+ ParallelFlash_Read(curWriteIndex * (READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS),
+ readChunks.words, READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS);
+
+ // Compare the readback to what we attempted to flash.
+ // Look at each chip
+ for (uint8_t chip = 0; chip < PARALLEL_FLASH_NUM_CHIPS; chip++)
+ {
+ uint16_t bytePos = chip;
+ uint8_t thisChipMask = 1 << chip;
+ // Loop over all bytes that are on this chip
+ for (uint16_t i = 0; i < READ_WRITE_CHUNK_SIZE_BYTES/PARALLEL_FLASH_NUM_CHIPS; i++)
+ {
+ if (writeChunks.bytes[bytePos] != readChunks.bytes[bytePos])
+ {
+ badVerifyChipsMask |= thisChipMask;
+ }
+ bytePos += PARALLEL_FLASH_NUM_CHIPS;
+ }
+ }
+
+ // Filter out chips we didn't care about
+ badVerifyChipsMask &= chipsMask;
+ }
+
+ // Bail if verification failed
+ if (badVerifyChipsMask != 0)
+ {
+ // Verification failed. The mask we calculated is actually
+ // backwards. We need to reverse it when we transmit the IC
+ // status back to the programmer software. This is kind of silly
+ // but it's too late to update the protocol.
+ uint8_t actualBadMask = 0;
+ for (uint8_t i = 0; i < PARALLEL_FLASH_NUM_CHIPS; i++)
+ {
+ if (badVerifyChipsMask & (1 << i))
+ {
+ actualBadMask |= 0x80;
+ }
+ actualBadMask >>= 1;
+ }
+
+ // Uh oh -- verification failure.
+ LED_Off();
+ // Send the fail bit along with a mask of failed chips.
+ USBCDC_SendByte(ProgrammerWriteVerificationError | badVerifyChipsMask);
+ curCommandState = WaitingForCommand;
+ }
+ else
+ {
+ USBCDC_SendByte(ProgrammerWriteOK);
+ curWriteIndex++;
+ writePosInChunk = -1;
+ LED_Toggle();
+ }
+ }
+ }
+}
+
+/** Handler called during an electrical test when a short is detected
+ *
+ * @param index1 The index of the first shorted pin
+ * @param index2 The index of the second shorted pin
+ *
+ * The two pins at index1 and index2 have been detected as shorted together.
+ * The numbering is internal to the SIMM electrical test, and the programmer
+ * software knows how to interpret it.
+ */
+static void SIMMProgrammer_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2)
+{
+ USBCDC_SendByte(ProgrammerElectricalTestFail);
+ USBCDC_SendByte(index1);
+ USBCDC_SendByte(index2);
+}
+
+/** Handles a received byte when we are determining what part of the chip to erase
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_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)
+ {
+ ParallelFlashChipType chipType = ParallelFlash_ChipType();
+ 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 == ParallelFlash_SST39SF040_x4)
+ {
+ if (boundary <= (2 * 1024UL * 1024UL))
+ {
+ // OK! We're erasing certain sectors of a 2 MB SIMM.
+ USBCDC_SendByte(ProgrammerErasePortionOK);
+ // Send the response immediately, it could take a while.
+ USBCDC_Flush();
+ if (ParallelFlash_EraseSectors(erasePosition/PARALLEL_FLASH_NUM_CHIPS,
+ eraseLength/PARALLEL_FLASH_NUM_CHIPS, chipsMask))
+ {
+ eraseSuccess = true;
+ }
+ }
+ }
+ else if (chipType == ParallelFlash_M29F160FB5AN6E2_x4)
+ {
+ if (boundary <= (8 * 1024UL * 1024UL))
+ {
+ // OK! We're erasing certain sectors of an 8 MB SIMM.
+ USBCDC_SendByte(ProgrammerErasePortionOK);
+ // Send the response immediately, it could take a while.
+ USBCDC_Flush();
+ if (ParallelFlash_EraseSectors(erasePosition/PARALLEL_FLASH_NUM_CHIPS,
+ eraseLength/PARALLEL_FLASH_NUM_CHIPS, chipsMask))
+ {
+ eraseSuccess = true;
+ }
+ }
+ }
+ }
+
+ if (eraseSuccess)
+ {
+ // Not on a sector boundary for erase position and/or length
+ USBCDC_SendByte(ProgrammerErasePortionFinished);
+ curCommandState = WaitingForCommand;
+ }
+ else
+ {
+ // Not on a sector boundary for erase position and/or length
+ USBCDC_SendByte(ProgrammerErasePortionError);
+ curCommandState = WaitingForCommand;
+ }
+ }
+}
+
+/** Handles a received byte when we are determining where to start reading from the SIMM
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleReadingChipsReadStartPosByte(uint8_t byte)
+{
+ // There will be four bytes, so count up until we know the position. If they
+ // have sent all four bytes, then start reading the length
+ curReadIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
+ if (++readLengthByteIndex >= 4)
+ {
+ readLengthByteIndex = 0;
+ curCommandState = ReadingChipsReadLength;
+ }
+}
+
+/** Handles a received byte when we are determining where to start writing to the SIMM
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleWritingChipsReadingStartPosByte(uint8_t byte)
+{
+ // There will be four bytes, so count up until we know the position. If they
+ // have sent all four bytes, then confirm the write and begin
+ curWriteIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
+ if (++readLengthByteIndex >= 4)
+ {
+ // Got it...now, is it valid? If so, allow the write to begin
+ if ((curWriteIndex % READ_WRITE_CHUNK_SIZE_BYTES) ||
+ (curWriteIndex >= PARALLEL_FLASH_NUM_CHIPS * MAX_CHIP_SIZE))
+ {
+ USBCDC_SendByte(ProgrammerWriteError);
+ curCommandState = WaitingForCommand;
+ }
+ else
+ {
+ // Convert write size into an index appropriate for rest of code
+ curWriteIndex /= READ_WRITE_CHUNK_SIZE_BYTES;
+ USBCDC_SendByte(ProgrammerWriteOK);
+ curCommandState = WritingChips;
+ }
+ }
+}
+
+/** Handles a received byte when we are determining the mask of which chips to write to
+ *
+ * @param byte The received byte
+ */
+static void SIMMProgrammer_HandleReadingChipsMaskByte(uint8_t byte)
+{
+ // Single byte follows containing mask of chips we're programming
+ if (byte <= 0x0F)
+ {
+ // Mask has to be less than or equal to 0x0F because there are only
+ // four valid mask bits.
+ chipsMask = byte;
+ USBCDC_SendByte(CommandReplyOK);
+ }
+ else
+ {
+ USBCDC_SendByte(CommandReplyError);
+ }
+
+ // Done either way; now we're waiting for a command to arrive
+ curCommandState = WaitingForCommand;
+}
diff --git a/usb_serial/usb_serial.h b/simm_programmer.h
similarity index 80%
rename from usb_serial/usb_serial.h
rename to simm_programmer.h
index 2c990f7..d3fd8cb 100644
--- a/usb_serial/usb_serial.h
+++ b/simm_programmer.h
@@ -1,5 +1,5 @@
/*
- * usb_serial.h
+ * simm_programmer.h
*
* Created on: Dec 9, 2011
* Author: Doug
@@ -22,10 +22,10 @@
*
*/
-#ifndef USB_SERIAL_H_
-#define USB_SERIAL_H_
+#ifndef SIMM_PROGRAMMER_H_
+#define SIMM_PROGRAMMER_H_
-void USBSerial_Init(void);
-void USBSerial_Check(void);
+void SIMMProgrammer_Init(void);
+void SIMMProgrammer_Check(void);
-#endif /* USB_SERIAL_H_ */
+#endif /* SIMM_PROGRAMMER_H_ */
diff --git a/tests/simm_electrical_test.c b/tests/simm_electrical_test.c
index 840c6b8..9afe6bf 100644
--- a/tests/simm_electrical_test.c
+++ b/tests/simm_electrical_test.c
@@ -23,66 +23,99 @@
*/
#include "simm_electrical_test.h"
-#include "../ports.h"
-#include "../delay.h"
+#include "../hal/parallel_bus.h"
+#include "hardware.h"
-#define SIMM_HIGHEST_ADDRESS_LINE 20
-#define SIMM_ADDRESS_PINS_MASK ((1UL << (SIMM_HIGHEST_ADDRESS_LINE + 1)) - 1)
+/// The index of the highest SIMM address pin
+#define SIMM_HIGHEST_ADDRESS_LINE 20
+/// Mask that represents every SIMM address pin
+#define SIMM_ADDRESS_PINS_MASK ((1UL << (SIMM_HIGHEST_ADDRESS_LINE + 1)) - 1)
-#define SIMM_HIGHEST_DATA_LINE 31
-#define SIMM_DATA_PINS_MASK (0xFFFFFFFFUL)
+/// The index of the highest SIMM data pin
+#define SIMM_HIGHEST_DATA_LINE 31
+/// Mask that represents every SIMM data pin
+#define SIMM_DATA_PINS_MASK 0xFFFFFFFFUL
-#define DELAY_SETTLE_TIME_MS 20
+/// Milliseconds to wait before testing for shorts
+#define DELAY_SETTLE_TIME_MS 20
+/// The index reported as a short when it's a ground short
+#define GROUND_FAIL_INDEX 0xFF
+/// The index reported when A0 is shorted
+#define FIRST_ADDRESS_LINE_FAIL_INDEX 0
+/// The index reported when A20 is shorted
+#define LAST_ADDRESS_LINE_FAIL_INDEX (FIRST_ADDRESS_LINE_FAIL_INDEX + SIMM_HIGHEST_ADDRESS_LINE)
+/// The index reported when D0 is shorted
+#define FIRST_DATA_LINE_FAIL_INDEX (LAST_ADDRESS_LINE_FAIL_INDEX + 1)
+/// The index reported when D31 is shorted
+#define LAST_DATA_LINE_FAIL_INDEX (FIRST_DATA_LINE_FAIL_INDEX + SIMM_HIGHEST_DATA_LINE)
+/// The index reported when CS is shorted
+#define CS_FAIL_INDEX (LAST_DATA_LINE_FAIL_INDEX + 1)
+/// The index reported when OE is shorted
+#define OE_FAIL_INDEX (CS_FAIL_INDEX + 1)
+/// The index reported when WE is shorted
+#define WE_FAIL_INDEX (OE_FAIL_INDEX + 1)
+
+/// Enum representing the step we're on during the electrical test
typedef enum ElectricalTestStage
{
- TestingAddressLines,
- TestingDataLines,
- TestingCS,
- TestingOE,
- TestingWE,
- DoneTesting
+ TestingAddressLines,//!< We are testing an address pin
+ TestingDataLines, //!< We are testing a data pin
+ TestingCS, //!< We are testing the CS pin
+ TestingOE, //!< We are testing the OE pin
+ TestingWE, //!< We are testing the WE pin
+ DoneTesting //!< We completed the test
} ElectricalTestStage;
-// Private functions
-void SIMMElectricalTest_ResetGroundShorts(void);
-void SIMMElectricalTest_AddGroundShort(uint8_t index);
-bool SIMMElectricalTest_IsGroundShort(uint8_t index);
+static void SIMMElectricalTest_ResetGroundShorts(void);
+static void SIMMElectricalTest_AddGroundShort(uint8_t index);
+static bool SIMMElectricalTest_IsGroundShort(uint8_t index);
+/// Pin indexes that were detected as shorted to ground. They have to be saved,
+/// because they end up being detected as a short against every other pin,
+/// so we have to be able to filter them out when testing non-ground shorts.
+static uint32_t groundShorts[2];
+
+/** Runs the electrical test
+ *
+ * @param errorHandler Pointer to function to call when a short is detected.
+ * @return The number of errors we found
+ *
+ * The two parameters to errorHandler are the two indexes that are shorted.
+ * (See the _FAIL_INDEX defines at the top of this file)
+ */
int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
{
// Returns number of errors found
int numErrors = 0;
- // Pins we have determined are shorted to ground
+ // Reset the pins we have determined that are shorted to ground
// (We have to ignore them during the second phase of the test)
SIMMElectricalTest_ResetGroundShorts();
- Ports_Init();
-
- // Give everything a bit of time to settle down
- DelayMS(DELAY_SETTLE_TIME_MS);
-
- // First check for anything shorted to ground. Set all lines as inputs with a weak pull-up resistor.
- // Then read the values back and check for any zeros. This would indicate a short to ground.
- Ports_SetAddressDDR(0);
- Ports_SetDataDDR(0);
- Ports_SetCSDDR(false);
- Ports_SetOEDDR(false);
- Ports_SetWEDDR(false);
- Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, SIMM_ADDRESS_PINS_MASK);
- Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, SIMM_DATA_PINS_MASK);
- Ports_SetCSPullup(true);
- Ports_SetOEPullup(true);
- Ports_SetWEPullup(true);
+ // First check for anything shorted to ground. Set all lines as inputs wit
+ // a weak pull-up resistor. Then read the values back and check for any
+ // zeros. This would indicate a short to ground.
+ ParallelBus_SetAddressDir(0);
+ ParallelBus_SetDataDir(0);
+ ParallelBus_SetCSDir(false);
+ ParallelBus_SetOEDir(false);
+ ParallelBus_SetWEDir(false);
+ ParallelBus_SetAddressPullups(SIMM_ADDRESS_PINS_MASK);
+ ParallelBus_SetDataPullups(SIMM_DATA_PINS_MASK);
+ ParallelBus_SetCSPullup(true);
+ ParallelBus_SetOEPullup(true);
+ ParallelBus_SetWEPullup(true);
+ // Wait a brief moment...
DelayMS(DELAY_SETTLE_TIME_MS);
+ // Now loop through every pin and check it.
uint8_t curPin = 0;
uint8_t i;
- // Read the address pins back first of all
- uint32_t readback = Ports_ReadAddress();
+ // Read the address pins back first
+ uint32_t readback = ParallelBus_ReadAddress();
if (readback != SIMM_ADDRESS_PINS_MASK)
{
// Check each bit for a LOW which would indicate a short to ground
@@ -93,7 +126,10 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
{
// That means this pin is shorted to ground.
// So notify the caller that we have a ground short on this pin
- errorHandler(curPin, GROUND_FAIL_INDEX);
+ if (errorHandler)
+ {
+ errorHandler(curPin, GROUND_FAIL_INDEX);
+ }
// Add it to our internal list of ground shorts also.
SIMMElectricalTest_AddGroundShort(curPin);
@@ -109,14 +145,17 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
}
// Repeat the exact same process for the data pins
- readback = Ports_ReadData();
+ readback = ParallelBus_ReadData();
if (readback != SIMM_DATA_PINS_MASK)
{
for (i = 0; i <= SIMM_HIGHEST_DATA_LINE; i++)
{
- if (!(readback & 1)) // failure here?
+ if (!(readback & 1))
{
- errorHandler(curPin, GROUND_FAIL_INDEX);
+ if (errorHandler)
+ {
+ errorHandler(curPin, GROUND_FAIL_INDEX);
+ }
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
@@ -127,42 +166,54 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
}
// Check chip select in the same way...
- if (!Ports_ReadCS())
+ if (!ParallelBus_ReadCS())
{
- errorHandler(curPin, GROUND_FAIL_INDEX);
+ if (errorHandler)
+ {
+ errorHandler(curPin, GROUND_FAIL_INDEX);
+ }
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
curPin++;
// Output enable...
- if (!Ports_ReadOE())
+ if (!ParallelBus_ReadOE())
{
- errorHandler(curPin, GROUND_FAIL_INDEX);
+ if (errorHandler)
+ {
+ errorHandler(curPin, GROUND_FAIL_INDEX);
+ }
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
curPin++;
// Write enable...
- if (!Ports_ReadWE())
+ if (!ParallelBus_ReadWE())
{
- errorHandler(curPin, GROUND_FAIL_INDEX);
+ if (errorHandler)
+ {
+ errorHandler(curPin, GROUND_FAIL_INDEX);
+ }
SIMMElectricalTest_AddGroundShort(curPin);
numErrors++;
}
curPin++; // Doesn't need to be here, but for consistency I'm leaving it.
- // OK, now we know which lines are shorted to ground.
- // We need to keep that in mind, because those lines will now show as shorted
- // to ALL other lines...ignore them during tests to find other independent shorts
+ // OK, now we know which lines are shorted to ground. We need to keep that
+ // in mind, because those lines will now show as shorted to ALL other
+ // lines...ignore them during tests to find other independent shorts
- // Now, check each individual line vs. all other lines on the SIMM for any shorts between them
+ // Now, check each individual line vs. all other lines on the SIMM for any
+ // shorts between them
ElectricalTestStage curStage = TestingAddressLines;
- int x = 0; // Counter of what address or data pin we're on. Not used for control lines.
- uint8_t testPin = 0; // What pin we are currently testing all other pins against.
- // x is only a counter inside the address or data pins.
- // testPin is a total counter of ALL pins.
+ // Counter of what address or data pin we're on. Not used for control lines.
+ uint8_t addrDataPin = 0;
+ // What pin we are currently testing all other pins against.
+ uint8_t testPin = 0;
+ // More explanation: addrDataPin is only a counter inside the address or
+ // data pins. testPin is a total counter of ALL pins.
while (curStage != DoneTesting)
{
// Set one pin to output a 0.
@@ -170,101 +221,111 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
// Then read back all the other pins. If any of them read back as 0,
// it means they are shorted to the pin we set as an output.
- // If we're testing address lines right now, set the current address line
- // as an output (and make it output a LOW).
- // Set all other address lines as inputs with pullups.
+ // If we're testing address lines right now, set the current address
+ // line as an output (and make it output a LOW). Set all other address
+ // lines as inputs with pullups.
if (curStage == TestingAddressLines)
{
- uint32_t addressLineMask = (1UL << x); // mask of the address pin we're testing
+ // Mask of the address pin we're testing
+ uint32_t addressLineMask = (1UL << addrDataPin);
- Ports_SetAddressDDR(addressLineMask); // set it as an output and all other address pins as inputs
- Ports_AddressOut_RMW(0, addressLineMask); // set the output pin to output "0" without affecting the input pins
- Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, ~addressLineMask); // turn on the pullups on all input pins
+ // Set it as an output and all other address pins as inputs.
+ ParallelBus_SetAddressDir(addressLineMask);
+ ParallelBus_SetAddress(0);
+ ParallelBus_SetAddressPullups(~addressLineMask);
}
else
{
- // If not testing an address line, set all address pins as inputs with pullups.
- // All the other stages follow the same pattern so I won't bother commenting them.
- Ports_SetAddressDDR(0);
- Ports_AddressPullups_RMW(SIMM_ADDRESS_PINS_MASK, SIMM_ADDRESS_PINS_MASK);
+ // If not testing an address line, set all address pins as inputs
+ // with pullups. All the other stages follow the same pattern so I
+ // won't bother commenting them.
+ ParallelBus_SetAddressDir(0);
+ ParallelBus_SetAddressPullups(SIMM_ADDRESS_PINS_MASK);
}
// Do the same thing for data lines...
if (curStage == TestingDataLines)
{
- uint32_t dataLineMask = (1UL << x);
- Ports_SetDataDDR(dataLineMask);
- Ports_DataOut_RMW(0, dataLineMask);
- Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, ~dataLineMask);
+ uint32_t dataLineMask = (1UL << addrDataPin);
+ ParallelBus_SetDataDir(dataLineMask);
+ ParallelBus_SetData(0);
+ ParallelBus_SetDataPullups(~dataLineMask);
}
else
{
- Ports_SetDataDDR(0);
- Ports_DataPullups_RMW(SIMM_DATA_PINS_MASK, SIMM_DATA_PINS_MASK);
+ ParallelBus_SetDataDir(0);
+ ParallelBus_SetDataPullups(SIMM_DATA_PINS_MASK);
}
// Chip select...
if (curStage == TestingCS)
{
- Ports_SetCSDDR(true);
- Ports_SetCSOut(false);
+ ParallelBus_SetCSDir(true);
+ ParallelBus_SetCS(false);
}
else
{
- Ports_SetCSDDR(false);
- Ports_SetCSPullup(true);
+ ParallelBus_SetCSDir(false);
+ ParallelBus_SetCSPullup(true);
}
// Output enable...
if (curStage == TestingOE)
{
- Ports_SetOEDDR(true);
- Ports_SetOEOut(false);
+ ParallelBus_SetOEDir(true);
+ ParallelBus_SetOE(false);
}
else
{
- Ports_SetOEDDR(false);
- Ports_SetOEPullup(true);
+ ParallelBus_SetOEDir(false);
+ ParallelBus_SetOEPullup(true);
}
// And write enable.
if (curStage == TestingWE)
{
- Ports_SetWEDDR(true);
- Ports_SetWEOut(false);
+ ParallelBus_SetWEDir(true);
+ ParallelBus_SetWE(false);
}
else
{
- Ports_SetWEDDR(false);
- Ports_SetWEPullup(true);
+ ParallelBus_SetWEDir(false);
+ ParallelBus_SetWEPullup(true);
}
- // OK, so now we have set up all lines as needed. Exactly one pin is outputting a 0, and all other pins
- // are inputs with pull-ups enabled. Read back all the lines, and if any pin reads back as 0,
- // it means that pin is shorted to the pin we are testing (overpowering its pullup)
- // However, because we test each pin against every other pin, any short would appear twice.
- // Once as "pin 1 is shorted to pin 2" and once as "pin 2 is shorted to pin 1". To avoid
- // that annoyance, I only check each combination of two pins once. You'll see below
- // how I do this by testing to ensure curPin > testPin.
+ // OK, so now we have set up all lines as needed. Exactly one pin is
+ // outputting a 0, and all other pins are inputs with pull-ups enabled.
+ // Read back all the lines, and if any pin reads back as 0, it means
+ // that pin is shorted to the pin we are testing (overpowering its
+ // pullup). However, because we test each pin against every other pin,
+ // any short would appear twice. Once as "pin 1 is shorted to pin 2"
+ // and once as "pin 2 is shorted to pin 1". To avoid that annoyance, I
+ // only check each combination of two pins once. You'll see below how I
+ // do this by testing to ensure curPin > testPin.
+ // Allow everything to settle
DelayMS(DELAY_SETTLE_TIME_MS);
- // Now keep a count of how many pins we have actually checked during THIS test.
+ // Now keep a count of how many pins we have actually checked during
+ // THIS test iteration. This is the "fail index" of the current pin.
curPin = 0;
// Read back the address data to see if any shorts were found
- readback = Ports_ReadAddress();
+ readback = ParallelBus_ReadAddress();
// Count any shorted pins
for (i = 0; i <= SIMM_HIGHEST_ADDRESS_LINE; i++)
{
- // failure here?
+ // Failure here?
if ((curPin > testPin) && // We haven't already checked this combination of pins (don't test pin against itself either)
!(readback & 1) && // It's showing as low (which indicates a short)
!SIMMElectricalTest_IsGroundShort(curPin)) // And it's not recorded as a short to ground
{
// Send it out as an error notification and increase error counter
- errorHandler(testPin, curPin);
+ if (errorHandler)
+ {
+ errorHandler(testPin, curPin);
+ }
numErrors++;
}
@@ -274,17 +335,20 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
}
// Same thing for data pins
- readback = Ports_ReadData();
+ readback = ParallelBus_ReadData();
// Count any shorted pins
for (i = 0; i <= SIMM_HIGHEST_DATA_LINE; i++)
{
- // failure here?
+ // Failure here?
if ((curPin > testPin) && // We haven't already checked this combination of pins (don't test pin against itself either)
!(readback & 1) && // It's showing as low (which indicates a short)
!SIMMElectricalTest_IsGroundShort(curPin)) // And it's not recorded as a short to ground
{
- errorHandler(testPin, curPin);
+ if (errorHandler)
+ {
+ errorHandler(testPin, curPin);
+ }
numErrors++;
}
@@ -294,30 +358,39 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
// And chip select...
if ((curPin > testPin) &&
- !Ports_ReadCS() &&
+ !ParallelBus_ReadCS() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
- errorHandler(testPin, curPin);
+ if (errorHandler)
+ {
+ errorHandler(testPin, curPin);
+ }
numErrors++;
}
curPin++;
// Output enable...
if ((curPin > testPin) &&
- !Ports_ReadOE() &&
+ !ParallelBus_ReadOE() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
- errorHandler(testPin, curPin);
+ if (errorHandler)
+ {
+ errorHandler(testPin, curPin);
+ }
numErrors++;
}
curPin++;
// And write enable
if ((curPin > testPin) &&
- !Ports_ReadWE() &&
+ !ParallelBus_ReadWE() &&
!SIMMElectricalTest_IsGroundShort(curPin))
{
- errorHandler(testPin, curPin);
+ if (errorHandler)
+ {
+ errorHandler(testPin, curPin);
+ }
numErrors++;
}
curPin++; // Not needed, kept for consistency
@@ -327,17 +400,17 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
{
// If we've exhausted all address lines, move on to the next stage
// (and reset the pin counter to 0)
- if (++x > SIMM_HIGHEST_ADDRESS_LINE)
+ if (++addrDataPin > SIMM_HIGHEST_ADDRESS_LINE)
{
curStage++;
- x = 0;
+ addrDataPin = 0;
}
}
else if (curStage == TestingDataLines)
{
// If we've exhausted all data lines, move on to the next stage
// (don't bother resetting the pin counter -- the other stages don't use it)
- if (++x > SIMM_HIGHEST_DATA_LINE)
+ if (++addrDataPin > SIMM_HIGHEST_DATA_LINE)
{
curStage++;
}
@@ -351,59 +424,27 @@ int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t))
testPin++;
}
- // Restore to a normal state. Disable any pull-ups, return CS/OE/WE to outputs,
- // deassert them all, set data as an input, set address as an input,
- // assert CS and OE. Should be a pretty normal state.
-
- // To start, make everything an input so we can disable pullups.
- Ports_SetCSDDR(false);
- Ports_SetOEDDR(false);
- Ports_SetWEDDR(false);
- Ports_SetAddressDDR(0);
- Ports_SetDataDDR(0);
-
- // Disable pullups
-
- Ports_SetCSPullup(false);
- Ports_SetOEPullup(false);
- Ports_SetWEPullup(false);
- Ports_AddressPullups_RMW(0, SIMM_ADDRESS_PINS_MASK);
- Ports_DataPullups_RMW(0, SIMM_DATA_PINS_MASK);
-
- // Set control lines to deasserted outputs (remember ON is deasserted)
- Ports_SetCSDDR(true);
- Ports_SetOEDDR(true);
- Ports_SetWEDDR(true);
- Ports_SetCSOut(true);
- Ports_SetOEOut(true);
- Ports_SetWEOut(true);
-
- // Set address lines to outputs and set the outputted address to zero
- Ports_SetAddressDDR(SIMM_ADDRESS_PINS_MASK);
- Ports_SetAddressOut(0);
-
- // Leave data lines as inputs...(they're already inputs so do nothing...)
-
- // Now assert CS and OE so we're in normal "read" mode
- Ports_SetCSOut(false);
- Ports_SetOEOut(false);
+ // Restore to a normal state by calling ParallelBus_Init again.
+ ParallelBus_Init();
// Now that the final state is restored, return the number of errors found
return numErrors;
}
-// Stuff for handling shorts to ground
-// (They have to be remembered because the ground shorts will be repeated
-// when you test each individual pin against all other pins)
-static uint32_t groundShorts[2];
-
-void SIMMElectricalTest_ResetGroundShorts(void)
+/** Resets our list of pins that are shorted to ground
+ *
+ */
+static void SIMMElectricalTest_ResetGroundShorts(void)
{
groundShorts[0] = 0;
groundShorts[1] = 0;
}
-void SIMMElectricalTest_AddGroundShort(uint8_t index)
+/** Adds a pin to the list of ground shorts
+ *
+ * @param index The index of the pin in the electrical test
+ */
+static void SIMMElectricalTest_AddGroundShort(uint8_t index)
{
if (index < 32)
{
@@ -416,7 +457,12 @@ void SIMMElectricalTest_AddGroundShort(uint8_t index)
// None are >= 64, no further handling needed
}
-bool SIMMElectricalTest_IsGroundShort(uint8_t index)
+/** Determines if a pin has already been saved as a ground short
+ *
+ * @param index The index of the pin in the electrical test
+ * @return True if this pin has been determined as a short to GND, false if not
+ */
+static bool SIMMElectricalTest_IsGroundShort(uint8_t index)
{
if (index < 32)
{
diff --git a/tests/simm_electrical_test.h b/tests/simm_electrical_test.h
index 5370d63..8396a45 100644
--- a/tests/simm_electrical_test.h
+++ b/tests/simm_electrical_test.h
@@ -27,16 +27,6 @@
#include
-#define GROUND_FAIL_INDEX 0xFF
-
-#define FIRST_ADDRESS_LINE_FAIL_INDEX 0
-#define LAST_ADDRESS_LINE_FAIL_INDEX (FIRST_ADDRESS_LINE_FAIL_INDEX + 20)
-#define FIRST_DATA_LINE_FAIL_INDEX (LAST_ADDRESS_LINE_FAIL_INDEX + 1)
-#define LAST_DATA_LINE_FAIL_INDEX (FIRST_DATA_LINE_FAIL_INDEX + 31)
-#define CS_FAIL_INDEX (LAST_DATA_LINE_FAIL_INDEX + 1)
-#define OE_FAIL_INDEX (CS_FAIL_INDEX + 1)
-#define WE_FAIL_INDEX (OE_FAIL_INDEX + 1)
-
int SIMMElectricalTest_Run(void (*errorHandler)(uint8_t, uint8_t));
#endif /* SIMM_ELECTRICAL_TEST_H_ */
diff --git a/usb_serial/usb_serial.c b/usb_serial/usb_serial.c
deleted file mode 100644
index 22953b8..0000000
--- a/usb_serial/usb_serial.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * usb_serial.c
- *
- * Created on: Dec 9, 2011
- * Author: Doug
- *
- * Copyright (C) 2011-2012 Doug Brown
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "usb_serial.h"
-#include "../LUFA/Drivers/USB/USB.h"
-#include "../cdc_device_definition.h"
-#include "../external_mem.h"
-#include "../tests/simm_electrical_test.h"
-#include "../programmer_protocol.h"
-#include "../led.h"
-#include
-
-#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
-#if ((WRITE_CHUNK_SIZE_BYTES % 4) != 0)
-#error Write chunk size should be a multiple of 4 bytes
-#endif
-
-void USBSerial_Init(void)
-{
- USB_Init();
-}
-
-// Internal state so we know how to interpret the next-received byte
-typedef enum ProgrammerCommandState
-{
- WaitingForCommand = 0,
- //ReadingByteWaitingForAddress, // TODO
- ReadingChipsReadLength,
- ReadingChips,
- //ReadingChipsUnableSendError, // TODO
- WritingChips,
- ErasePortionReadingPosLength,
- ReadingChipsReadStartPos,
- WritingChipsReadingStartPos,
- ReadingChipsMask,
-} ProgrammerCommandState;
-static ProgrammerCommandState curCommandState = WaitingForCommand;
-
-// State info for reading/writing
-//static uint8_t byteAddressReceiveCount = 0;
-static uint16_t curReadIndex;
-static uint32_t readLength;
-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;
-static uint8_t chipsMask = ALL_CHIPS;
-
-// Private functions
-void USBSerial_HandleWaitingForCommandByte(uint8_t byte);
-void USBSerial_HandleReadingChipsByte(uint8_t byte);
-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);
-void USBSerial_HandleReadingChipsReadStartPosByte(uint8_t byte);
-void USBSerial_HandleWritingChipsReadingStartPosByte(uint8_t byte);
-void USBSerial_HandleReadingChipsMaskByte(uint8_t byte);
-
-// Read/write to USB serial macros -- easier than retyping
-// CDC_Device_XXX(&VirtualSerial_CDC_Interface...) every time
-#define SendByte(b) CDC_Device_SendByte(&VirtualSerial_CDC_Interface, b)
-#define ReadByte() CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface)
-#define SendData(d, l) CDC_Device_SendData(&VirtualSerial_CDC_Interface, d, l)
-
-// Should be called periodically in the main loop
-void USBSerial_Check(void)
-{
- // If we're configured, read a byte (if one is available) and process it
- if (USB_DeviceState == DEVICE_STATE_Configured)
- {
- int16_t recvByte = ReadByte();
-
- // Did we get a byte? If so, hand it off to the correct handler
- // function based on the current state
- if (recvByte >= 0)
- {
- switch (curCommandState)
- {
- case WaitingForCommand:
- USBSerial_HandleWaitingForCommandByte((uint8_t)recvByte);
- break;
- case ReadingChipsReadLength:
- USBSerial_HandleReadingChipsReadLengthByte((uint8_t)recvByte);
- break;
- case ReadingChips:
- USBSerial_HandleReadingChipsByte((uint8_t)recvByte);
- break;
- case WritingChips:
- USBSerial_HandleWritingChipsByte((uint8_t)recvByte);
- break;
- case ErasePortionReadingPosLength:
- USBSerial_HandleErasePortionReadPosLengthByte((uint8_t)recvByte);
- break;
- case ReadingChipsReadStartPos:
- USBSerial_HandleReadingChipsReadStartPosByte((uint8_t)recvByte);
- break;
- case WritingChipsReadingStartPos:
- USBSerial_HandleWritingChipsReadingStartPosByte((uint8_t)recvByte);
- break;
- case ReadingChipsMask:
- USBSerial_HandleReadingChipsMaskByte((uint8_t)recvByte);
- break;
- }
- }
- }
-
- // And do the periodic CDC and USB tasks...
- CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
- USB_USBTask();
-}
-
-// If we're in the "waiting for command" state, handle the command...
-void USBSerial_HandleWaitingForCommandByte(uint8_t byte)
-{
- switch (byte)
- {
- // Asked to enter waiting mode -- we're already there, so say OK.
- case EnterWaitingMode:
- SendByte(CommandReplyOK);
- curCommandState = WaitingForCommand;
- break;
- // Asked to do the electrical test. Reply OK, and then do the test,
- // sending whatever replies necessary
- case DoElectricalTest:
- SendByte(CommandReplyOK);
- // Force LUFA to send initial "OK" reply immediately in this case
- // so the caller gets immediate feedback that the test has started
- CDC_Device_Flush(&VirtualSerial_CDC_Interface);
- SIMMElectricalTest_Run(USBSerial_ElectricalTest_Fail_Handler);
- SendByte(ProgrammerElectricalTestDone);
- curCommandState = WaitingForCommand;
- break;
- // Asked to identify the chips in the SIMM. Identify them and send reply.
- case IdentifyChips:
- {
- struct ChipID chips[NUM_CHIPS];
- SendByte(CommandReplyOK);
- ExternalMem_IdentifyChips(chips);
- int x;
- for (x = 0; x < NUM_CHIPS; x++)
- {
- SendByte(chips[x].manufacturerID);
- SendByte(chips[x].deviceID);
- }
- SendByte(ProgrammerIdentifyDone);
- break;
- }
- // Asked to read a single byte from each SIMM. Change the state and reply.
- case ReadByte:
- /*curCommandState = ReadingByteWaitingForAddress;
- byteAddressReceiveCount = 0;
- SendByte(CommandReplyOK);*/
- SendByte(CommandReplyInvalid); // not implemented yet
- break;
- // Asked to read all four chips. Set the state, reply with the first chunk.
- // This will read from the BEGINNING of the SIMM every time. Use
- // ReadChipsAt to specify a start position
- case ReadChips:
- curCommandState = ReadingChipsReadLength;
- curReadIndex = 0;
- readLengthByteIndex = 0;
- readLength = 0;
- SendByte(CommandReplyOK);
- break;
- case ReadChipsAt:
- curCommandState = ReadingChipsReadStartPos;
- curReadIndex = 0;
- readLengthByteIndex = 0;
- readLength = 0;
- SendByte(CommandReplyOK);
- break;
- // Erase the chips and reply OK. (TODO: Sometimes erase might fail)
- case EraseChips:
- ExternalMem_EraseChips(chipsMask);
- SendByte(CommandReplyOK);
- break;
- // Begin writing the chips. Change the state, reply, wait for chunk of data
- case WriteChips:
- curCommandState = WritingChips;
- curWriteIndex = 0;
- writePosInChunk = -1;
- SendByte(CommandReplyOK);
- break;
- case WriteChipsAt:
- curCommandState = WritingChipsReadingStartPos;
- curWriteIndex = 0;
- readLengthByteIndex = 0;
- writePosInChunk = -1;
- SendByte(CommandReplyOK);
- break;
- // Asked for the current bootloader state. We are in the program right now,
- // so reply accordingly.
- case GetBootloaderState:
- SendByte(CommandReplyOK);
- SendByte(BootloaderStateInProgrammer);
- break;
- // Enter the bootloader. Wait a bit, then jump to the bootloader location.
- case EnterBootloader:
- SendByte(CommandReplyOK);
- CDC_Device_Flush(&VirtualSerial_CDC_Interface);
-
- // Insert a small delay to ensure that it arrives before rebooting.
- _delay_ms(1000);
-
- // Done with the USB interface -- the bootloader will re-initialize it.
- USB_Disable();
-
- // Disable interrupts so nothing weird happens...
- cli();
-
- // Wait a little bit to let everything settle and let the program
- // close the port after the USB disconnect
- _delay_ms(2000);
-
- // And, of course, go into the bootloader.
- __asm__ __volatile__ ( "jmp 0xE000" );
- break;
- // Enter the programmer. We're already there, so reply OK.
- case EnterProgrammer:
- // Already in the programmer
- SendByte(CommandReplyOK);
- break;
- // Set the SIMM type to the older, smaller chip size (2MB and below)
- case SetSIMMTypePLCC32_2MB:
- ExternalMem_SetChipType(ChipType8BitData_4MBitSize);
- SendByte(CommandReplyOK);
- break;
- case SetSIMMTypeLarger:
- ExternalMem_SetChipType(ChipType8Bit16BitData_16MBitSize);
- SendByte(CommandReplyOK);
- break;
- case SetVerifyWhileWriting:
- verifyDuringWrite = true;
- SendByte(CommandReplyOK);
- break;
- case SetNoVerifyWhileWriting:
- verifyDuringWrite = false;
- SendByte(CommandReplyOK);
- break;
- case ErasePortion:
- readLengthByteIndex = 0;
- eraseLength = 0;
- erasePosition = 0;
- curCommandState = ErasePortionReadingPosLength;
- SendByte(CommandReplyOK);
- break;
- case SetChipsMask:
- curCommandState = ReadingChipsMask;
- SendByte(CommandReplyOK);
- break;
- // We don't know what this command is, so reply that it was invalid.
- default:
- SendByte(CommandReplyInvalid);
- break;
- }
-}
-
-// If we're in the "reading chips" state, handle the incoming byte...
-void USBSerial_HandleReadingChipsByte(uint8_t byte)
-{
- // The byte should be a reply from the computer. It should be either:
- // 1) ComputerReadOK -- meaning it got the chunk we just sent
- // or
- // 2) ComputerReadCancel -- meaning the user canceled the read
- switch (byte)
- {
- case ComputerReadOK:
- // If they have confirmed the final data chunk, let them know
- // that they have finished, and enter command state.
- if (curReadIndex >= readLength)
- {
- LED_Off();
- SendByte(ProgrammerReadFinished);
- curCommandState = WaitingForCommand;
- }
- else // There's more data left to read, so read it and send it to them!
- {
- LED_Toggle();
- SendByte(ProgrammerReadMoreData);
- USBSerial_SendReadDataChunk();
- }
- break;
- case ComputerReadCancel:
- // If they've canceled, let them know we got their request and go back
- // to "waiting for command" state
- SendByte(ProgrammerReadConfirmCancel);
- curCommandState = WaitingForCommand;
- break;
- }
-}
-
-// If we're figuring out the length to read, grab it now...
-void USBSerial_HandleReadingChipsReadLengthByte(uint8_t byte)
-{
- // There will be four bytes, so count up until we know the length. If they
- // have sent all four bytes, send the first read chunk.
- readLength |= (((uint32_t)byte) << (8*readLengthByteIndex));
- if (++readLengthByteIndex >= 4)
- {
- // Ensure it's within limits and a multiple of 1024
- if ((curReadIndex + readLength > NUM_CHIPS * MAX_CHIP_SIZE) ||
- (readLength % READ_CHUNK_SIZE_BYTES) ||
- (curReadIndex % READ_CHUNK_SIZE_BYTES) ||
- (readLength == 0))// Ensure it's within limits and a multiple of 1024
- {
- SendByte(ProgrammerReadError);
- curCommandState = WaitingForCommand;
- }
- else
- {
- // Convert the length/pos into the number of chunks we need to send
- readLength /= READ_CHUNK_SIZE_BYTES;
- curReadIndex /= READ_CHUNK_SIZE_BYTES;
- curCommandState = ReadingChips;
- SendByte(ProgrammerReadOK);
- USBSerial_SendReadDataChunk();
- }
- }
-}
-
-// Read the next chunk of data from the SIMM and send it off over the serial.
-void USBSerial_SendReadDataChunk(void)
-{
- // Here's a buffer we will use to read the next chunk of data.
- // It's static because the stack is NOT big enough for it. If I start
- // running low on RAM, I could pull this out of the function and share it
- // with other functions, but I'm not bothering with that for now.
- static union
- {
- uint32_t readChunks[READ_CHUNK_SIZE_BYTES / NUM_CHIPS];
- uint8_t readChunkBytes[READ_CHUNK_SIZE_BYTES];
- } chunks;
-
- // Read the next chunk of data, send it over USB, and make sure
- // we sent it correctly.
- ExternalMem_Read(curReadIndex * (READ_CHUNK_SIZE_BYTES/NUM_CHIPS),
- chunks.readChunks, READ_CHUNK_SIZE_BYTES/NUM_CHIPS);
- uint8_t retVal = SendData((const char *)chunks.readChunkBytes,
- READ_CHUNK_SIZE_BYTES);
-
- // If for some reason there was an error, mark it as such. Otherwise,
- // increment our pointer so we know the next chunk of data to send.
- if (retVal != ENDPOINT_RWSTREAM_NoError)
- {
- //curCommandState = ReadingChipsUnableSendError; // TODO: not implemented
- curCommandState = WaitingForCommand;
- }
- else
- {
- curReadIndex++;
- }
-}
-
-// Handles a received byte from the computer while we're in the "writing chips"
-// mode.
-void USBSerial_HandleWritingChipsByte(uint8_t byte)
-{
- // A buffer we use to store the incoming data. This, too, could be shared
- // with other functions if I end up running out of RAM. Again, I'm not
- // bothering with that yet, but this could easily be shared with the
- // read function.
- static union
- {
- uint32_t writeChunks[WRITE_CHUNK_SIZE_BYTES / 4];
- uint8_t writeChunkBytes[WRITE_CHUNK_SIZE_BYTES];
- } chunks;
-
- // This means we have just started the entire process or just finished
- // a chunk, so see what the computer has decided for us to do.
- if (writePosInChunk == -1)
- {
- switch (byte)
- {
- // The computer asked to write more data to the SIMM.
- case ComputerWriteMore:
- writePosInChunk = 0;
- // Make sure we don't write past the capacity of the chips.
- if (curWriteIndex < MAX_CHIP_SIZE / (WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS))
- {
- SendByte(ProgrammerWriteOK);
- }
- else
- {
- LED_Off();
- SendByte(ProgrammerWriteError);
- curCommandState = WaitingForCommand;
- }
- break;
- // The computer said that it's done writing.
- case ComputerWriteFinish:
- LED_Off();
- SendByte(ProgrammerWriteOK);
- curCommandState = WaitingForCommand;
- break;
- // The computer asked to cancel.
- case ComputerWriteCancel:
- LED_Off();
- SendByte(ProgrammerWriteConfirmCancel);
- curCommandState = WaitingForCommand;
- break;
- }
- }
- else // Interpret the incoming byte as data to write to the SIMM.
- {
- // Save the byte, and check if we've filled up an entire chunk
- chunks.writeChunkBytes[writePosInChunk++] = byte;
- if (writePosInChunk >= WRITE_CHUNK_SIZE_BYTES)
- {
- // We filled up the chunk, write it out and confirm it, then wait
- // for the next command from the computer!
- uint8_t writeResult = ExternalMem_Write(curWriteIndex * (WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS),
- chunks.writeChunks, WRITE_CHUNK_SIZE_BYTES/NUM_CHIPS, chipsMask, verifyDuringWrite);
-
- // But if we asked to verify, make sure it came out OK.
- if (verifyDuringWrite && (writeResult != 0))
- {
- // Uh oh -- verification failure.
- LED_Off();
- // Send the fail bit along with a mask of failed chips.
- SendByte(ProgrammerWriteVerificationError | writeResult);
- curCommandState = WaitingForCommand;
- }
- else
- {
- SendByte(ProgrammerWriteOK);
- curWriteIndex++;
- writePosInChunk = -1;
- LED_Toggle();
- }
- }
- }
-}
-
-// Whenever an electrical test failure occurs, this handler will be called
-// by it. It sends out a failure notice followed by indexes of the two
-// shorted pins.
-void USBSerial_ElectricalTest_Fail_Handler(uint8_t index1, uint8_t index2)
-{
- SendByte(ProgrammerElectricalTestFail);
- SendByte(index1);
- 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, chipsMask))
- {
- 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, chipsMask))
- {
- 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;
- }
- }
-}
-
-void USBSerial_HandleReadingChipsReadStartPosByte(uint8_t byte)
-{
- // There will be four bytes, so count up until we know the position. If they
- // have sent all four bytes, then start reading the length
- curReadIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
- if (++readLengthByteIndex >= 4)
- {
- readLengthByteIndex = 0;
- curCommandState = ReadingChipsReadLength;
- }
-}
-
-void USBSerial_HandleWritingChipsReadingStartPosByte(uint8_t byte)
-{
- // There will be four bytes, so count up until we know the position. If they
- // have sent all four bytes, then confirm the write and begin
- curWriteIndex |= (((uint32_t)byte) << (8*readLengthByteIndex));
- if (++readLengthByteIndex >= 4)
- {
- // Got it...now, is it valid? If so, allow the write to begin
- if ((curWriteIndex % WRITE_CHUNK_SIZE_BYTES) ||
- (curWriteIndex >= NUM_CHIPS * MAX_CHIP_SIZE))
- {
- SendByte(ProgrammerWriteError);
- curCommandState = WaitingForCommand;
- }
- else
- {
- // Convert write size into an index appropriate for rest of code
- curWriteIndex /= WRITE_CHUNK_SIZE_BYTES;
- SendByte(ProgrammerWriteOK);
- curCommandState = WritingChips;
- }
- }
-}
-
-void USBSerial_HandleReadingChipsMaskByte(uint8_t byte)
-{
- // Single byte follows containing mask of chips we're programming
- if (byte <= 0x0F)
- {
- // Mask has to be less than or equal to 0x0F because there are only
- // four valid mask bits.
- chipsMask = byte;
- SendByte(CommandReplyOK);
- }
- else
- {
- SendByte(CommandReplyError);
- }
-
- // Done either way; now we're waiting for a command to arrive
- curCommandState = WaitingForCommand;
-}
-
-// LUFA event handler for when the USB configuration changes.
-void EVENT_USB_Device_ConfigurationChanged(void)
-{
- bool ConfigSuccess = true;
-
- ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
-}
-
-// LUFA event handler for when a USB control request is received
-void EVENT_USB_Device_ControlRequest(void)
-{
- CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
-}
diff --git a/delay.h b/util.h
similarity index 71%
rename from delay.h
rename to util.h
index eeb1215..4e646f6 100644
--- a/delay.h
+++ b/util.h
@@ -1,10 +1,10 @@
/*
- * delay.h
+ * util.h
*
- * Created on: Dec 4, 2011
+ * Created on: Nov 25, 2020
* Author: Doug
*
- * Copyright (C) 2011-2012 Doug Brown
+ * Copyright (C) 2011-2020 Doug Brown
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -22,11 +22,10 @@
*
*/
-#ifndef DELAY_H_
-#define DELAY_H_
+#ifndef UTIL_H_
+#define UTIL_H_
-#include
+/// Macro so we don't have to repeat this monstrosity multiple times
+#define ALWAYS_INLINE __attribute__ ((__always_inline__)) inline
-#define DelayMS(ms) _delay_ms(ms)
-
-#endif /* DELAY_H_ */
+#endif /* UTIL_H_ */