mirror of
https://github.com/dougg3/mac-rom-simm-programmer.git
synced 2024-12-01 19:50:18 +00:00
Add implementation of ParallelBus for M258KE
This commit is contained in:
parent
ce5e2e97a3
commit
4bda68346f
638
hal/m258ke/parallel_bus.c
Normal file
638
hal/m258ke/parallel_bus.c
Normal file
@ -0,0 +1,638 @@
|
||||
/*
|
||||
* parallel_bus.c
|
||||
*
|
||||
* Created on: Jun 19, 2023
|
||||
* Author: Doug
|
||||
*
|
||||
* Copyright (C) 2011-2023 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../parallel_bus.h"
|
||||
#include "../../util.h"
|
||||
#include "../gpio.h"
|
||||
#include "gpio_hw.h"
|
||||
#include "nuvoton/NuMicro.h"
|
||||
|
||||
// Borrowed from Nuvoton's sample code
|
||||
#define GPIO_PIN_DATA(port, pin) (*((volatile uint32_t *)((GPIO_PIN_DATA_BASE+(0x40*(port))) + ((pin)<<2))))
|
||||
#define PB15 GPIO_PIN_DATA(1, 15)
|
||||
#define PC14 GPIO_PIN_DATA(2, 14)
|
||||
#define PD15 GPIO_PIN_DATA(3, 15)
|
||||
|
||||
/// Defines for speedier toggle of these pins
|
||||
#define FLASH_WE_PIN PD15
|
||||
#define FLASH_OE_PIN PC14
|
||||
#define FLASH_CS_PIN PB15
|
||||
|
||||
/// The index of the highest address line on the parallel bus
|
||||
#define PARALLEL_BUS_HIGHEST_ADDRESS_LINE 20
|
||||
|
||||
// Data pins:
|
||||
// PA0-7: D24-D31
|
||||
// PA8-11: D16-D19
|
||||
// PC9-12: D20-D23
|
||||
// PE0-7: D8-D15
|
||||
// PE8-15: D0-D7
|
||||
|
||||
// Address pins:
|
||||
// PB0-11: A0-A11
|
||||
// PC0-8: A12-A20
|
||||
|
||||
static inline void ParallelBus_SetDataAllInput(void);
|
||||
static inline void ParallelBus_SetDataAllOutput(void);
|
||||
|
||||
/// The /WE pin for the parallel bus
|
||||
static const GPIOPin flashWEPin = {GPIOD, 15};
|
||||
/// The /OE pin for the parallel bus
|
||||
static const GPIOPin flashOEPin = {GPIOC, 14};
|
||||
/// The /CS pin for the flash chip
|
||||
static const GPIOPin flashCSPin = {GPIOB, 15};
|
||||
|
||||
// Macros for asserting and deasserting pins
|
||||
#define AssertControl(p) p = 0
|
||||
#define DeassertControl(p) p = 1
|
||||
|
||||
/** Initializes the 32-bit data/21-bit address parallel bus.
|
||||
*
|
||||
*/
|
||||
void ParallelBus_Init(void)
|
||||
{
|
||||
// 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_SetDataAllInput();
|
||||
ParallelBus_SetDataPullups(0xFFFFFFFFUL);
|
||||
|
||||
// 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)
|
||||
{
|
||||
const uint32_t addrMaskB = 0xFFFUL;
|
||||
const uint32_t addrMaskC = 0x1FFUL;
|
||||
|
||||
const uint32_t addrB = address & addrMaskB;
|
||||
const uint32_t addrC = (address >> 12) & addrMaskC;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
uint32_t tmpB = PB->DOUT;
|
||||
uint32_t tmpC = PC->DOUT;
|
||||
tmpB &= ~addrMaskB;
|
||||
tmpB |= addrB;
|
||||
tmpC &= ~addrMaskC;
|
||||
tmpC |= addrC;
|
||||
PB->DOUT = tmpB;
|
||||
PC->DOUT = tmpC;
|
||||
}
|
||||
|
||||
/** Sets the output data on the 32-bit data bus
|
||||
*
|
||||
* @param data The data
|
||||
*/
|
||||
void ParallelBus_SetData(uint32_t data)
|
||||
{
|
||||
const uint32_t dataMaskA = 0xFFFUL;
|
||||
const uint32_t dataMaskC = 0x1E00UL;
|
||||
const uint32_t dataMaskE = 0xFFFFUL;
|
||||
|
||||
const uint32_t dataA = data & dataMaskA;
|
||||
const uint32_t dataC = (data >> 3) & dataMaskC; // bits 0-11 are in dataA, but dataMaskC starts at bit 9, so shift by 3
|
||||
const uint32_t dataE = (data >> 16) & dataMaskE;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
uint32_t tmpA = PA->DOUT;
|
||||
uint32_t tmpC = PC->DOUT;
|
||||
uint32_t tmpE = PE->DOUT;
|
||||
tmpA &= ~dataMaskA;
|
||||
tmpA |= dataA;
|
||||
tmpC &= ~dataMaskC;
|
||||
tmpC |= dataC;
|
||||
tmpE &= ~dataMaskE;
|
||||
tmpE |= dataE;
|
||||
PA->DOUT = tmpA;
|
||||
PC->DOUT = tmpC;
|
||||
PE->DOUT = tmpE;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Interleaves zeros between each bit in a 16-bit value
|
||||
* @param value The original value to interleave, 16 bits
|
||||
* @return Interleaved version of value, 32 bits
|
||||
*
|
||||
* For example, if value = 0b0000111100110101, then it will return:
|
||||
* 0b00000000010101010000010100010001
|
||||
*/
|
||||
static uint32_t InterleaveZeros(uint16_t value)
|
||||
{
|
||||
uint32_t x = value;
|
||||
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
static const uint32_t B[] = {0x55555555UL, 0x33333333UL, 0x0F0F0F0FUL, 0x00FF00FFUL};
|
||||
static const uint32_t S[] = {1UL, 2UL, 4UL, 8UL};
|
||||
|
||||
x = (x | (x << S[3])) & B[3];
|
||||
x = (x | (x << S[2])) & B[2];
|
||||
x = (x | (x << S[1])) & B[1];
|
||||
x = (x | (x << S[0])) & B[0];
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
const uint32_t addrMaskB = 0xFFFUL;
|
||||
const uint32_t addrMaskC = 0x1FFUL;
|
||||
|
||||
const uint32_t addrB = outputs & addrMaskB;
|
||||
const uint32_t addrC = (outputs >> 12) & addrMaskC;
|
||||
|
||||
// For efficiency, we talk directly to the PORT registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
// There are actually 2 bits per pin. We want to write 00 if input,
|
||||
// 01 if output. So we need to interleave a zero in between each bit
|
||||
// to match the register layout.
|
||||
|
||||
const uint32_t regMaskB = 0xFFFFFFUL;
|
||||
const uint32_t regMaskC = 0x3FFFFUL;
|
||||
|
||||
const uint32_t regB = InterleaveZeros(addrB);
|
||||
const uint32_t regC = InterleaveZeros(addrC);
|
||||
|
||||
uint32_t tmpB = PB->MODE;
|
||||
uint32_t tmpC = PC->MODE;
|
||||
tmpB &= ~regMaskB;
|
||||
tmpB |= regB;
|
||||
tmpC &= ~regMaskC;
|
||||
tmpC |= regC;
|
||||
PB->MODE = tmpB;
|
||||
PC->MODE = tmpC;
|
||||
}
|
||||
|
||||
/** 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
|
||||
*
|
||||
* Typically all pins would be set as inputs or outputs, and it's automatically
|
||||
* handled by the read/write cycle functions. This function exists mainly for
|
||||
* test purposes.
|
||||
*/
|
||||
void ParallelBus_SetDataDir(uint32_t outputs)
|
||||
{
|
||||
const uint32_t dataMaskA = 0xFFFUL;
|
||||
const uint32_t dataMaskC = 0x1E00UL;
|
||||
const uint32_t dataMaskE = 0xFFFFUL;
|
||||
|
||||
const uint32_t dataA = outputs & dataMaskA;
|
||||
const uint32_t dataC = (outputs >> 3) & dataMaskC; // bits 0-11 are in dataA, but dataMaskC starts at bit 9, so shift by 3
|
||||
const uint32_t dataE = (outputs >> 16) & dataMaskE;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
// There are actually 2 bits per pin. We want to write 00 if input,
|
||||
// 01 if output. So we need to interleave a zero in between each bit
|
||||
// to match the register layout.
|
||||
|
||||
const uint32_t regMaskA = 0xFFFFFFUL;
|
||||
const uint32_t regMaskC = 0x3FC0000UL;
|
||||
const uint32_t regMaskE = 0xFFFFFFFFUL;
|
||||
|
||||
const uint32_t regA = InterleaveZeros(dataA);
|
||||
const uint32_t regC = InterleaveZeros(dataC);
|
||||
const uint32_t regE = InterleaveZeros(dataE);
|
||||
|
||||
uint32_t tmpA = PA->MODE;
|
||||
uint32_t tmpC = PC->MODE;
|
||||
uint32_t tmpE = PE->MODE;
|
||||
tmpA &= ~regMaskA;
|
||||
tmpA |= regA;
|
||||
tmpC &= ~regMaskC;
|
||||
tmpC |= regC;
|
||||
tmpE &= ~regMaskE;
|
||||
tmpE |= regE;
|
||||
PA->MODE = tmpA;
|
||||
PC->MODE = tmpC;
|
||||
PE->MODE = tmpE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets all data pins as inputs
|
||||
*
|
||||
* This is an optimized version for the read cycle functions
|
||||
*/
|
||||
static inline void ParallelBus_SetDataAllInput(void)
|
||||
{
|
||||
PA->MODE &= ~0xFFFFFFUL;
|
||||
PC->MODE &= ~0x3FC0000UL;
|
||||
PE->MODE = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets all data pins as outputs
|
||||
*
|
||||
* This is an optimized version for the write cycle functions
|
||||
*/
|
||||
static inline void ParallelBus_SetDataAllOutput(void)
|
||||
{
|
||||
PA->MODE = (PA->MODE & ~0xFFFFFFUL) | 0x555555UL;
|
||||
PC->MODE = (PC->MODE & ~0x3FC0000UL) | 0x1540000UL;
|
||||
PE->MODE = 0x55555555UL;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
const uint32_t addrMaskB = 0xFFFUL;
|
||||
const uint32_t addrMaskC = 0x1FFUL;
|
||||
|
||||
const uint32_t addrB = pullups & addrMaskB;
|
||||
const uint32_t addrC = (pullups >> 12) & addrMaskC;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
// There are actually 2 bits per pin. We want to write 00 if disabled,
|
||||
// 01 if enabled. So we need to interleave a zero in between each bit
|
||||
// to match the register layout.
|
||||
|
||||
const uint32_t regMaskB = 0xFFFFFFUL;
|
||||
const uint32_t regMaskC = 0x3FFFFUL;
|
||||
|
||||
const uint32_t regB = InterleaveZeros(addrB);
|
||||
const uint32_t regC = InterleaveZeros(addrC);
|
||||
|
||||
uint32_t tmpB = PB->PUSEL;
|
||||
uint32_t tmpC = PC->PUSEL;
|
||||
tmpB &= ~regMaskB;
|
||||
tmpB |= regB;
|
||||
tmpC &= ~regMaskC;
|
||||
tmpC |= regC;
|
||||
PB->PUSEL = tmpB;
|
||||
PC->PUSEL = tmpC;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
const uint32_t dataMaskA = 0xFFFUL;
|
||||
const uint32_t dataMaskC = 0x1E00UL;
|
||||
const uint32_t dataMaskE = 0xFFFFUL;
|
||||
|
||||
const uint32_t dataA = pullups & dataMaskA;
|
||||
const uint32_t dataC = (pullups >> 3) & dataMaskC; // bits 0-11 are in dataA, but dataMaskC starts at bit 9, so shift by 3
|
||||
const uint32_t dataE = (pullups >> 16) & dataMaskE;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
// There are actually 2 bits per pin. We want to write 00 if input,
|
||||
// 01 if output. So we need to interleave a zero in between each bit
|
||||
// to match the register layout.
|
||||
|
||||
const uint32_t regMaskA = 0xFFFFFFUL;
|
||||
const uint32_t regMaskC = 0x3FC0000UL;
|
||||
const uint32_t regMaskE = 0xFFFFFFFFUL;
|
||||
|
||||
const uint32_t regA = InterleaveZeros(dataA);
|
||||
const uint32_t regC = InterleaveZeros(dataC);
|
||||
const uint32_t regE = InterleaveZeros(dataE);
|
||||
|
||||
uint32_t tmpA = PA->PUSEL;
|
||||
uint32_t tmpC = PC->PUSEL;
|
||||
uint32_t tmpE = PE->PUSEL;
|
||||
tmpA &= ~regMaskA;
|
||||
tmpA |= regA;
|
||||
tmpC &= ~regMaskC;
|
||||
tmpC |= regC;
|
||||
tmpE &= ~regMaskE;
|
||||
tmpE |= regE;
|
||||
PA->PUSEL = tmpA;
|
||||
PC->PUSEL = tmpC;
|
||||
PE->PUSEL = tmpE;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
const uint32_t addrMaskB = 0xFFFUL;
|
||||
const uint32_t addrMaskC = 0x1FFUL;
|
||||
|
||||
// For efficiency, we talk directly to the registers in this function,
|
||||
// rather than going through the GPIO class.
|
||||
|
||||
const uint32_t readB = PB->PIN;
|
||||
const uint32_t readC = PC->PIN;
|
||||
|
||||
uint32_t result = readB & addrMaskB;
|
||||
result |= ((readC & addrMaskC) << 12);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Reads the current data on the 32-bit data bus.
|
||||
*
|
||||
* @return The 32-bit data readback
|
||||
*/
|
||||
uint32_t ParallelBus_ReadData(void)
|
||||
{
|
||||
const uint32_t dataMaskA = 0xFFFUL;
|
||||
const uint32_t dataMaskC = 0x1E00UL;
|
||||
const uint32_t dataMaskE = 0xFFFFUL;
|
||||
|
||||
const uint32_t readA = PA->PIN;
|
||||
const uint32_t readC = PC->PIN;
|
||||
const uint32_t readE = PE->PIN;
|
||||
|
||||
uint32_t result = readA & dataMaskA;
|
||||
result |= ((readC & dataMaskC) << 3); // Bits 9-12 of port C are bits 12 to 15 of data bus
|
||||
result |= ((readE & dataMaskE) << 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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 write to
|
||||
* @param data The 32-bit data to write to the bus
|
||||
*/
|
||||
void ParallelBus_WriteCycle(uint32_t address, uint32_t data)
|
||||
{
|
||||
// We should currently be in a state of "CS is asserted, OE/WE not asserted".
|
||||
// As an optimization, operate under that assumption.
|
||||
|
||||
// Set address
|
||||
ParallelBus_SetAddress(address);
|
||||
|
||||
// Ensure the data pins are all outputs
|
||||
ParallelBus_SetDataAllOutput();
|
||||
|
||||
// Set data
|
||||
ParallelBus_SetData(data);
|
||||
|
||||
// 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
|
||||
*/
|
||||
uint32_t ParallelBus_ReadCycle(uint32_t address)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
// We should currently be in a state of "CS is asserted, OE/WE not asserted".
|
||||
// As an optimization, operate under that assumption.
|
||||
|
||||
// Ensure the data pins are all inputs
|
||||
ParallelBus_SetDataAllInput();
|
||||
|
||||
// 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
|
||||
ParallelBus_SetAddress(address);
|
||||
|
||||
// Read data
|
||||
ret = ParallelBus_ReadData();
|
||||
|
||||
// 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 ret;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
|
||||
// Ensure the data pins are all inputs
|
||||
ParallelBus_SetDataAllInput();
|
||||
|
||||
// Assert OE, now the chip will start spitting out data.
|
||||
AssertControl(FLASH_OE_PIN);
|
||||
|
||||
while (len--)
|
||||
{
|
||||
// Set address
|
||||
ParallelBus_SetAddress(startAddress++);
|
||||
|
||||
// Read data
|
||||
*buf++ = ParallelBus_ReadData();
|
||||
}
|
||||
|
||||
// Deassert OE once we are done
|
||||
DeassertControl(FLASH_OE_PIN);
|
||||
|
||||
// Control lines are left as "CS asserted, OE/WE not asserted" here.
|
||||
}
|
Loading…
Reference in New Issue
Block a user