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.
This commit is contained in:
Doug Brown 2020-11-17 21:03:32 -08:00 committed by Doug Brown
parent 927c178671
commit 7425af761a
137 changed files with 3085 additions and 2333 deletions

193
.cproject
View File

@ -41,6 +41,9 @@
<listOptionValue builtIn="false" value="USE_STATIC_OPTIONS=&quot;(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)&quot;"/>
</option>
<option id="de.innot.avreclipse.compiler.option.otherflags.1513967861" name="Other flags" superClass="de.innot.avreclipse.compiler.option.otherflags" value="-ffunction-sections -fdata-sections" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="de.innot.avreclipse.compiler.option.incpath.173462079" superClass="de.innot.avreclipse.compiler.option.incpath" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/SIMMProgrammer/hal/at90usb646}&quot;"/>
</option>
<inputType id="de.innot.avreclipse.compiler.winavr.input.1367172122" name="C Source Files" superClass="de.innot.avreclipse.compiler.winavr.input"/>
</tool>
<tool id="de.innot.avreclipse.tool.cppcompiler.app.debug.24092953" name="AVR C++ Compiler" superClass="de.innot.avreclipse.tool.cppcompiler.app.debug">
@ -85,7 +88,7 @@
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildArtefactType="de.innot.avreclipse.buildArtefactType.app" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=de.innot.avreclipse.buildArtefactType.app,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" description="" id="de.innot.avreclipse.configuration.app.release.1163010375" name="Release" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=" parent="de.innot.avreclipse.configuration.app.release" postannouncebuildStep="Generate BIN file for firmware flashing" postbuildStep="avr-objcopy -R .eeprom -O binary ${BuildArtifactFileName} ${BuildArtifactFileBaseName}.bin">
<configuration artifactName="${ProjName}" buildArtefactType="de.innot.avreclipse.buildArtefactType.app" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=de.innot.avreclipse.buildArtefactType.app,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" description="" id="de.innot.avreclipse.configuration.app.release.1163010375" name="Release" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=" parent="de.innot.avreclipse.configuration.app.release" postannouncebuildStep="Generate BIN file for firmware flashing" postbuildStep="avr-objcopy -R .eeprom -O binary ${BuildArtifactFileName} ${BuildArtifactFileBaseName}.bin">
<folderInfo id="de.innot.avreclipse.configuration.app.release.1163010375." name="/" resourcePath="">
<toolChain id="de.innot.avreclipse.toolchain.winavr.app.release.405615296" name="AVR-GCC Toolchain" superClass="de.innot.avreclipse.toolchain.winavr.app.release">
<option id="de.innot.avreclipse.toolchain.options.toolchain.objcopy.flash.app.release.1550189649" name="Generate HEX file for Flash memory" superClass="de.innot.avreclipse.toolchain.options.toolchain.objcopy.flash.app.release"/>
@ -113,6 +116,9 @@
<listOptionValue builtIn="false" value="USE_STATIC_OPTIONS=&quot;(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)&quot;"/>
</option>
<option id="de.innot.avreclipse.compiler.option.otherflags.1566165436" name="Other flags" superClass="de.innot.avreclipse.compiler.option.otherflags" value="-ffunction-sections -fdata-sections" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="de.innot.avreclipse.compiler.option.incpath.2119807530" superClass="de.innot.avreclipse.compiler.option.incpath" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/SIMMProgrammer/hal/at90usb646}&quot;"/>
</option>
<inputType id="de.innot.avreclipse.compiler.winavr.input.2084198580" name="C Source Files" superClass="de.innot.avreclipse.compiler.winavr.input"/>
</tool>
<tool id="de.innot.avreclipse.tool.cppcompiler.app.release.912002244" name="AVR C++ Compiler" superClass="de.innot.avreclipse.tool.cppcompiler.app.release">
@ -151,6 +157,7 @@
</storageModule>
<storageModule moduleId="refreshScope"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
@ -163,16 +170,6 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
@ -203,56 +200,6 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="avr-gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="avr-g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.release.1163010375;de.innot.avreclipse.configuration.app.release.1163010375.;de.innot.avreclipse.tool.compiler.winavr.app.release.883804772;de.innot.avreclipse.compiler.winavr.input.2084198580">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
@ -265,16 +212,6 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
@ -305,58 +242,8 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="avr-gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="avr-g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.release.1163010375;de.innot.avreclipse.configuration.app.release.1163010375.">
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.debug.1674332705;de.innot.avreclipse.configuration.app.debug.1674332705.;de.innot.avreclipse.tool.compiler.winavr.app.debug.1123922496;de.innot.avreclipse.compiler.winavr.input.1367172122">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
@ -368,16 +255,6 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="makefileGenerator">
<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
@ -408,57 +285,7 @@
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="avr-gcc" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
<profile id="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileCPP">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="true"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="avr-g++" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cproject>

25
delay.c
View File

@ -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"

195
drivers/mcp23s17.c Normal file
View File

@ -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;
}

59
drivers/mcp23s17.h Normal file
View File

@ -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_ */

412
drivers/parallel_flash.c Normal file
View File

@ -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;
}
}

66
drivers/parallel_flash.h Normal file
View File

@ -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_ */

View File

@ -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 <avr/io.h>
#include <util/delay.h>
#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;
}

View File

@ -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 <stdint.h>
#include <stdbool.h>
#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_ */

View File

@ -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)

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Some files were not shown because too many files have changed in this diff Show More