From 7425af761a62e4b21e15bfa04e208aeb74b4d494 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Tue, 17 Nov 2020 21:03:32 -0800 Subject: [PATCH] Break out code into a HAL, optimize flash operations This makes the code pretty easily portable to other architectures if someone wants to make a more modern SIMM programmer. I also was pretty careful to split responsibilities of the different components and give the existing components better names. I'm pretty happy with the organization of the code now. As part of this change I have also heavily optimized the code. In particular, the read and write cycle routines are very important to the overall performance of the programmer. In these routines I had to make some tradeoffs of code performance versus prettiness, but the overall result is much faster programming. Some of these performance changes are the result of what I discovered when I upgraded my AVR compiler. I discovered that it is smarter at looking at 32-bit variables when I use a union instead of bitwise operations. I also shaved off more CPU cycles by carefully making a few small tweaks. I added a bypass for the "program only some chips" mask, because it was adding unnecessary CPU cycles for a feature that is rarely used. I removed the verification feature from the write routine, because we can always verify the data after the write chunk is complete, which is more efficient. I also added assumptions about the initial/final state of the CS/OE/WE pins, which allowed me to remove more valuable CPU cycles from the read/write cycle routines. There are also a few enormous performance optimizations I should have done a long time ago: 1) The code was only handling one received byte per main loop iteration. Reading every byte available cut nearly a minute off of the 8 MB programming time. 2) The code wasn't taking advantage of the faster programming command available in the chips used on the 8 MB SIMM. The end result of all of these optimizations is I have programming time of the 8 MB SIMM down to 3:31 (it used to be 8:43). Another minor issue I fixed: the Micron SIMM chip identification wasn't working properly. It was outputting the manufacturer ID again instead of the device ID. --- .cproject | 193 +---- delay.c | 25 - drivers/mcp23s17.c | 195 +++++ drivers/mcp23s17.h | 59 ++ drivers/parallel_flash.c | 412 +++++++++++ drivers/parallel_flash.h | 66 ++ external_mem.c | 502 ------------- external_mem.h | 113 --- Descriptors.c => hal/at90usb646/Descriptors.c | 4 +- Descriptors.h => hal/at90usb646/Descriptors.h | 0 .../LUFA}/CodeTemplates/DriverStubs/Buttons.h | 0 .../CodeTemplates/DriverStubs/Dataflash.h | 0 .../CodeTemplates/DriverStubs/Joystick.h | 0 .../LUFA}/CodeTemplates/DriverStubs/LEDs.h | 0 .../LUFA}/CodeTemplates/LUFAConfig.h | 0 .../CodeTemplates/makefile_template.avr8 | 0 .../LUFA}/CodeTemplates/makefile_template.uc3 | 0 .../CodeTemplates/makefile_template.xmega | 0 .../LUFA}/Common/ArchitectureSpecific.h | 0 .../at90usb646/LUFA}/Common/Architectures.h | 0 .../at90usb646/LUFA}/Common/Attributes.h | 0 .../at90usb646/LUFA}/Common/BoardTypes.h | 0 {LUFA => hal/at90usb646/LUFA}/Common/Common.h | 0 .../LUFA}/Common/CompilerSpecific.h | 0 .../at90usb646/LUFA}/Common/Endianness.h | 0 {LUFA => hal/at90usb646/LUFA}/Doxygen.conf | 0 .../LUFA}/DoxygenPages/AboutLUFA.txt | 0 .../LUFA}/DoxygenPages/AlternativeStacks.txt | 0 .../at90usb646/LUFA}/DoxygenPages/Author.jpg | Bin .../BuildingLinkableLibraries.txt | 0 .../LUFA}/DoxygenPages/ChangeLog.txt | 0 .../LUFA}/DoxygenPages/CompileTimeTokens.txt | 0 .../LUFA}/DoxygenPages/CompilingApps.txt | 0 .../LUFA}/DoxygenPages/ConfiguringApps.txt | 0 .../LUFA}/DoxygenPages/DevelopingWithLUFA.txt | 0 .../LUFA}/DoxygenPages/DeviceSupport.txt | 0 .../LUFA}/DoxygenPages/DirectorySummaries.txt | 0 .../LUFA}/DoxygenPages/Donating.txt | 0 .../LUFA}/DoxygenPages/FutureChanges.txt | 0 .../LUFA}/DoxygenPages/GettingStarted.txt | 0 .../at90usb646/LUFA}/DoxygenPages/Groups.txt | 0 .../at90usb646/LUFA}/DoxygenPages/LUFA.png | Bin .../DoxygenPages/LUFAPoweredProjects.txt | 0 .../LUFA}/DoxygenPages/LUFA_thumb.png | Bin .../LUFA}/DoxygenPages/LUFAvsAtmelStack.txt | 0 .../LUFA}/DoxygenPages/LibraryApps.txt | 0 .../LUFA}/DoxygenPages/LibraryResources.txt | 0 .../LUFA}/DoxygenPages/LicenseInfo.txt | 0 .../LUFA}/DoxygenPages/MainPage.txt | 0 .../DoxygenPages/MigrationInformation.txt | 0 .../LUFA}/DoxygenPages/ProgrammingApps.txt | 0 .../DoxygenPages/SoftwareBootloaderJump.txt | 0 .../LUFA}/DoxygenPages/VIDAndPIDValues.txt | 0 .../LUFA}/DoxygenPages/WhyUseLUFA.txt | 0 .../DoxygenPages/WritingBoardDrivers.txt | 0 .../at90usb646/LUFA}/DoxygenPages/footer.htm | 0 .../LUFA}/Drivers/USB/Class/CDCClass.h | 0 .../Drivers/USB/Class/Common/CDCClassCommon.h | 0 .../Drivers/USB/Class/Device/CDCClassDevice.c | 0 .../Drivers/USB/Class/Device/CDCClassDevice.h | 0 .../LUFA}/Drivers/USB/Core/AVR8/Device_AVR8.c | 0 .../LUFA}/Drivers/USB/Core/AVR8/Device_AVR8.h | 0 .../USB/Core/AVR8/EndpointStream_AVR8.c | 0 .../USB/Core/AVR8/EndpointStream_AVR8.h | 0 .../Drivers/USB/Core/AVR8/Endpoint_AVR8.c | 0 .../Drivers/USB/Core/AVR8/Endpoint_AVR8.h | 0 .../LUFA}/Drivers/USB/Core/AVR8/Host_AVR8.c | 0 .../LUFA}/Drivers/USB/Core/AVR8/Host_AVR8.h | 0 .../LUFA}/Drivers/USB/Core/AVR8/OTG_AVR8.h | 0 .../Drivers/USB/Core/AVR8/PipeStream_AVR8.c | 0 .../Drivers/USB/Core/AVR8/PipeStream_AVR8.h | 0 .../LUFA}/Drivers/USB/Core/AVR8/Pipe_AVR8.c | 0 .../LUFA}/Drivers/USB/Core/AVR8/Pipe_AVR8.h | 0 .../Template/Template_Endpoint_Control_R.c | 0 .../Template/Template_Endpoint_Control_W.c | 0 .../Core/AVR8/Template/Template_Endpoint_RW.c | 0 .../USB/Core/AVR8/Template/Template_Pipe_RW.c | 0 .../USB/Core/AVR8/USBController_AVR8.c | 0 .../USB/Core/AVR8/USBController_AVR8.h | 0 .../Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c | 0 .../Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h | 0 .../LUFA}/Drivers/USB/Core/ConfigDescriptor.c | 0 .../LUFA}/Drivers/USB/Core/ConfigDescriptor.h | 0 .../LUFA}/Drivers/USB/Core/Device.h | 0 .../Drivers/USB/Core/DeviceStandardReq.c | 0 .../Drivers/USB/Core/DeviceStandardReq.h | 0 .../LUFA}/Drivers/USB/Core/Endpoint.h | 0 .../LUFA}/Drivers/USB/Core/EndpointStream.h | 0 .../LUFA}/Drivers/USB/Core/Events.c | 0 .../LUFA}/Drivers/USB/Core/Events.h | 0 .../at90usb646/LUFA}/Drivers/USB/Core/Host.h | 0 .../LUFA}/Drivers/USB/Core/HostStandardReq.c | 0 .../LUFA}/Drivers/USB/Core/HostStandardReq.h | 0 .../at90usb646/LUFA}/Drivers/USB/Core/OTG.h | 0 .../at90usb646/LUFA}/Drivers/USB/Core/Pipe.h | 0 .../LUFA}/Drivers/USB/Core/PipeStream.h | 0 .../LUFA}/Drivers/USB/Core/StdDescriptors.h | 0 .../LUFA}/Drivers/USB/Core/StdRequestType.h | 0 .../LUFA}/Drivers/USB/Core/USBController.h | 0 .../LUFA}/Drivers/USB/Core/USBInterrupt.h | 0 .../LUFA}/Drivers/USB/Core/USBMode.h | 0 .../LUFA}/Drivers/USB/Core/USBTask.c | 0 .../LUFA}/Drivers/USB/Core/USBTask.h | 0 .../at90usb646/LUFA}/Drivers/USB/USB.h | 0 {LUFA => hal/at90usb646/LUFA}/License.txt | 0 {LUFA => hal/at90usb646/LUFA}/Version.h | 0 {LUFA => hal/at90usb646/LUFA}/makefile | 0 hal/at90usb646/board.c | 34 + hal/at90usb646/board_hw.h | 23 + .../at90usb646/cdc_device_definition.c | 26 +- .../at90usb646/cdc_device_definition.h | 0 hal/at90usb646/gpio.c | 99 +++ hal/at90usb646/gpio_hw.h | 22 + hal/at90usb646/hardware.h | 49 ++ hal/at90usb646/parallel_bus.c | 666 ++++++++++++++++++ hal/at90usb646/spi.c | 170 +++++ hal/at90usb646/spi_private.h | 20 + hal/at90usb646/usbcdc.c | 56 ++ hal/at90usb646/usbcdc_hw.h | 51 ++ hal/board.h | 21 + hal/gpio.h | 55 ++ hal/parallel_bus.h | 44 ++ hal/spi.h | 58 ++ hal/usbcdc.h | 24 + led.h | 41 +- main.c | 44 +- mcp23s17.c | 213 ------ mcp23s17.h | 39 - ports.c | 338 --------- ports.h | 90 --- programmer_protocol.h | 6 +- simm_programmer.c | 661 +++++++++++++++++ usb_serial/usb_serial.h => simm_programmer.h | 12 +- tests/simm_electrical_test.c | 348 +++++---- tests/simm_electrical_test.h | 10 - usb_serial/usb_serial.c | 612 ---------------- delay.h => util.h | 17 +- 137 files changed, 3085 insertions(+), 2333 deletions(-) delete mode 100644 delay.c create mode 100644 drivers/mcp23s17.c create mode 100644 drivers/mcp23s17.h create mode 100644 drivers/parallel_flash.c create mode 100644 drivers/parallel_flash.h delete mode 100644 external_mem.c delete mode 100644 external_mem.h rename Descriptors.c => hal/at90usb646/Descriptors.c (96%) rename Descriptors.h => hal/at90usb646/Descriptors.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/DriverStubs/Buttons.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/DriverStubs/Dataflash.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/DriverStubs/Joystick.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/DriverStubs/LEDs.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/LUFAConfig.h (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/makefile_template.avr8 (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/makefile_template.uc3 (100%) rename {LUFA => hal/at90usb646/LUFA}/CodeTemplates/makefile_template.xmega (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/ArchitectureSpecific.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/Architectures.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/Attributes.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/BoardTypes.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/Common.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/CompilerSpecific.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Common/Endianness.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Doxygen.conf (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/AboutLUFA.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/AlternativeStacks.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/Author.jpg (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/BuildingLinkableLibraries.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/ChangeLog.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/CompileTimeTokens.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/CompilingApps.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/ConfiguringApps.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/DevelopingWithLUFA.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/DeviceSupport.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/DirectorySummaries.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/Donating.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/FutureChanges.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/GettingStarted.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/Groups.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LUFA.png (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LUFAPoweredProjects.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LUFA_thumb.png (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LUFAvsAtmelStack.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LibraryApps.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LibraryResources.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/LicenseInfo.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/MainPage.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/MigrationInformation.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/ProgrammingApps.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/SoftwareBootloaderJump.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/VIDAndPIDValues.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/WhyUseLUFA.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/WritingBoardDrivers.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/DoxygenPages/footer.htm (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Class/CDCClass.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Class/Common/CDCClassCommon.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Class/Device/CDCClassDevice.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Class/Device/CDCClassDevice.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Device_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Device_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/EndpointStream_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Endpoint_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Endpoint_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Host_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Host_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/OTG_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/PipeStream_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/PipeStream_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Pipe_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Pipe_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_R.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Template/Template_Endpoint_Control_W.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Template/Template_Endpoint_RW.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/Template/Template_Pipe_RW.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/USBController_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/USBController_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/ConfigDescriptor.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/ConfigDescriptor.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Device.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/DeviceStandardReq.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/DeviceStandardReq.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Endpoint.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/EndpointStream.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Events.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Events.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Host.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/HostStandardReq.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/HostStandardReq.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/OTG.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/Pipe.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/PipeStream.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/StdDescriptors.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/StdRequestType.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/USBController.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/USBInterrupt.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/USBMode.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/USBTask.c (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/Core/USBTask.h (100%) rename {LUFA => hal/at90usb646/LUFA}/Drivers/USB/USB.h (100%) rename {LUFA => hal/at90usb646/LUFA}/License.txt (100%) rename {LUFA => hal/at90usb646/LUFA}/Version.h (100%) rename {LUFA => hal/at90usb646/LUFA}/makefile (100%) create mode 100644 hal/at90usb646/board.c create mode 100644 hal/at90usb646/board_hw.h rename cdc_device_definition.c => hal/at90usb646/cdc_device_definition.c (61%) rename cdc_device_definition.h => hal/at90usb646/cdc_device_definition.h (100%) create mode 100644 hal/at90usb646/gpio.c create mode 100644 hal/at90usb646/gpio_hw.h create mode 100644 hal/at90usb646/hardware.h create mode 100644 hal/at90usb646/parallel_bus.c create mode 100644 hal/at90usb646/spi.c create mode 100644 hal/at90usb646/spi_private.h create mode 100644 hal/at90usb646/usbcdc.c create mode 100644 hal/at90usb646/usbcdc_hw.h create mode 100644 hal/board.h create mode 100644 hal/gpio.h create mode 100644 hal/parallel_bus.h create mode 100644 hal/spi.h create mode 100644 hal/usbcdc.h delete mode 100644 mcp23s17.c delete mode 100644 mcp23s17.h delete mode 100644 ports.c delete mode 100644 ports.h create mode 100644 simm_programmer.c rename usb_serial/usb_serial.h => simm_programmer.h (80%) delete mode 100644 usb_serial/usb_serial.c rename delay.h => util.h (71%) 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_ */