mac-rom-simm-programmer/hal/at90usb646/gpio.c

117 lines
3.1 KiB
C
Raw Normal View History

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.
2020-11-18 05:03:32 +00:00
/*
* gpio.c
*
* Created on: Nov 14, 2020
* Author: Doug
*
* 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
* 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.
*
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.
2020-11-18 05:03:32 +00:00
*/
#include "../gpio.h"
#include <avr/io.h>
/// 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);
}