diff --git a/.cproject b/.cproject index 7532b36..48e4975 100644 --- a/.cproject +++ b/.cproject @@ -41,6 +41,9 @@ @@ -85,7 +88,7 @@ - + @@ -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_ */