Compare commits

..

35 Commits

Author SHA1 Message Date
Doug Brown
77aa45ff47 Don't assert OE and CS simultaneously during electrical test
If you do, and a SIMM is installed in the socket, you will get an
erroneous short detected for any data pins that are currently outputting
a 0. Avoid this by testing OE and CS for 5V shorts separately.
2023-08-06 20:35:30 -07:00
Doug Brown
ddf08fd5cd Mark the M258KE board as having pulldown support 2023-08-06 20:35:30 -07:00
Doug Brown
f28d8fbb41 Add pulldown (5V short detection) to SIMM electrical test 2023-08-06 20:35:30 -07:00
Doug Brown
f2a2d042d2 Hook up pulldowns in M258KE ParallelBus implementation 2023-08-06 20:35:30 -07:00
Doug Brown
0f69ccb8c1 Add dummy AVR pulldown functions for ParallelBus 2023-08-06 20:35:30 -07:00
Doug Brown
439c9629ba Add prototypes for pulldown control in ParallelBus 2023-08-06 20:35:30 -07:00
Doug Brown
af39922c48 Add support for GPIO pulldown control
Note that it does nothing on the AVR, which doesn't support it.
2023-08-06 20:35:29 -07:00
Doug Brown
a98947574d Exclude M258KE code from Eclipse AVR builds 2023-08-06 20:35:29 -07:00
Doug Brown
03e0c9785e Hook up the M258KE port
Note that ASM has to be enabled as a project language because the M258KE
startup code is an assembly file.
2023-08-06 20:35:29 -07:00
Doug Brown
8785611795 Add cmake source/options includes for M258KE port 2023-08-06 20:35:29 -07:00
Doug Brown
82c9c86b03 Disable watchdog timer when the firmware boots
If it gets this far, the firmware is at least sort of running, so the
watchdog has served its purpose of resetting to the bootloader if
there's invalid firmware flashed to the device.
2023-08-06 20:35:29 -07:00
Doug Brown
19caeca088 Enable timer, use for delay functions 2023-08-06 20:35:29 -07:00
Doug Brown
e79b367cde Implement M258KE board functions 2023-08-06 20:35:29 -07:00
Doug Brown
5dd60a87fe Add initial hardware.h for M258KE
Delay functions aren't implemented yet; they will be coming later.
2023-08-06 20:35:29 -07:00
Doug Brown
8212e054cb Add implementation of ParallelBus for M258KE 2023-08-06 20:35:29 -07:00
Doug Brown
23b7a32381 Add ability for status LED to be inverted 2023-08-06 20:35:28 -07:00
Doug Brown
c980b05831 Add GPIO driver
Provide the basic functionality for setting direction, turning on and
off, toggling, reading inputs, and enabling/disabling pullups.

This chip also provides pulldowns, so in the future I will also
implement pulldown control so we can detect shorts to 5V.
2023-08-06 20:35:28 -07:00
Doug Brown
a4a6219478 Implement stubbed-out SPI driver
SPI isn't needed on this platform because we don't need an I/O expander.
So this can be a bunch of stub functions that do nothing. They will be
optimized out during the linking process anyway.
2023-08-06 20:35:28 -07:00
Doug Brown
d07dbea24d Add cmake toolchain file for M258KE build 2023-08-06 20:35:28 -07:00
Doug Brown
47565c4b77 Add USB CDC serial port code
This implements a USB CDC serial port using the Nuvoton USBD driver. The
USB handling is based on Nuvoton's BSP sample code, especially the IRQ
handlers and descriptor buffer configuration. The descriptors have been
adapted to be similar to the AVR version, and RX/TX functions have been
written to implement an API closer to LUFA, which is what the SIMM
programmer common code needs.
2023-08-06 20:35:28 -07:00
Doug Brown
b042ce58b0 Include usbd.h
Since I stripped the peripheral header includes from M251.h, I have to
include it manually instead.
2023-08-06 20:35:28 -07:00
Doug Brown
eec3fee387 Fix const correctness of S_USBD_INFO_T struct 2023-08-06 20:35:28 -07:00
Doug Brown
15030a0b3b Disable unnecessary interrupt
I don't really need to bother with VBUS or "no-event-wake-up"
interrupts. This allows me to strip out more code in the IRQ handler.
2023-08-06 20:35:28 -07:00
Doug Brown
3eca572f12 Change USBD_MemCopy to not be static inline
I'm sure it's slightly more efficient as a static inline function, but
it results in less flash usage as a separate function. This is important
for the bootloader where every byte matters.
2023-08-06 20:35:28 -07:00
Doug Brown
436895ccb5 Strip out unnecessary callbacks and code in USBD driver
This is important for the bootloader. There's a bunch of stuff here we
don't need that unnecessarily bloats the code.
2023-08-06 20:35:27 -07:00
Doug Brown
ee0b6e5ccc Bypass GCC built-in startup code
Nuvoton's sample startup_M251.S file handles enough initialization for
my purposes, so I can completely bypass _start and jump directly to
main. Note that I also had to add a define to enable clearing of BSS.
2023-08-06 20:35:27 -07:00
Doug Brown
cc225883f9 Strip out unnecessary clock and UART code
The default values for SystemCoreClock, CyclesPerUs, and PllClock work
fine for my purposes of running from the 48 MHz HIRC. Remove unnecessary
initialization code. This is especially useful for the bootloader where
flash space is at a premium.

Also strip out unneeded UART setup code.
2023-08-06 20:35:27 -07:00
Doug Brown
696bc0447c Don't include peripheral header files in M251.h
I don't want to include all of Nuvoton's peripheral drivers, but I do
want to use this header file. Remove the unnecessary peripheral
includes.
2023-08-06 20:35:27 -07:00
Doug Brown
895d96c65d Reserve 4 bytes at end of RAM for magic number
This will be used during firmware updates so that the main firmware can
communicate to the bootloader that it should stay in the bootloader for
a firmware update rather than run the main firmware again.
2023-08-06 20:35:27 -07:00
Doug Brown
b61d56ed7b Fix issue with linker script allowing data section to overflow flash 2023-08-06 20:35:27 -07:00
Doug Brown
135ce94395 Update linker scripts with correct RAM/flash sizes
Change code style so that it's easy to see the number of kilobytes too.
2023-08-06 20:35:27 -07:00
Doug Brown
a7bafb5408 Add README explaining the Nuvoton directory 2023-08-06 20:35:27 -07:00
Doug Brown
d602c772f3 Initial commit of Nuvoton USBD driver 2023-08-06 20:35:26 -07:00
Doug Brown
59a29363c4 Initial commit of register defines, CMSIS code from Nuvoton BSP 2023-08-06 20:35:26 -07:00
Doug Brown
048772c694 Change license to GPLv3
I can't use GPLv2 as soon as I need to start using the Nuvoton sample
code which is licensed with an Apache 2.0 license.
2023-08-06 20:35:26 -07:00
4 changed files with 68 additions and 236 deletions

View File

@ -140,112 +140,32 @@ void ParallelFlash_EraseChips(uint8_t chipsMask)
* @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
* @param numEraseSectorGroups The number of erase sector groups we know about
* @param eraseSectorGroups The erase sector groups
* @return True on success, false on failure
*/
bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask, uint8_t numEraseSectorGroups, ParallelFlashEraseSectorGroup const *eraseSectorGroups)
bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chipsMask)
{
// Choose a default sector group if we don't have the info
static const ParallelFlashEraseSectorGroup defaultSST39SF040Sectors[] = {
{0xFFFFFFFFUL, SECTOR_SIZE_SST39SF040}
};
static const ParallelFlashEraseSectorGroup defaultM29F160FBSectors[] = {
{1, 0x4000},
{2, 0x2000},
{1, 0x8000},
{0xFFFFFFFFUL, SECTOR_SIZE_M29F160FB5AN6E2_8}
};
// If we don't know the sector info (older programmer or unknown chips)
// then fall back to the previous hardcoded sector maps.
// Note that "chip type" isn't really accurate anymore; this is more about
// whether or not it has shifted unlock addresses. But these are the hardcoded
// defaults that seemed to work okay for people previously.
if (numEraseSectorGroups == 0)
{
switch (curChipType)
{
case ParallelFlash_SST39SF040_x4:
default:
eraseSectorGroups = defaultSST39SF040Sectors;
numEraseSectorGroups = sizeof(defaultSST39SF040Sectors)/sizeof(defaultSST39SF040Sectors[0]);
break;
case ParallelFlash_M29F160FB5AN6E2_x4:
eraseSectorGroups = defaultM29F160FBSectors;
numEraseSectorGroups = sizeof(defaultM29F160FBSectors)/sizeof(defaultM29F160FBSectors[0]);
break;
}
}
bool result = false;
// The first sector group and index in that group to erase
uint32_t firstSectorGroup = 0;
uint32_t firstSectorInGroup = 0;
// Temporary counters for matching up sector locations
uint32_t curSectorGroup = 0;
uint32_t curSectorInGroup = 0;
// Find the first sector we need to erase. Keep searching until we've
// 1) found it or gone past it, or
// 2) exhausted our list of erase sector groups
uint32_t curAddress = 0;
while (curAddress < address &&
curSectorGroup < numEraseSectorGroups)
// Figure out our sector size
uint32_t sectorSize;
switch (curChipType)
{
curAddress += eraseSectorGroups[curSectorGroup].size;
curSectorInGroup++;
if (curSectorInGroup >= eraseSectorGroups[curSectorGroup].count)
{
curSectorGroup++;
curSectorInGroup = 0;
}
case ParallelFlash_SST39SF040_x4:
default:
sectorSize = SECTOR_SIZE_SST39SF040;
break;
case ParallelFlash_M29F160FB5AN6E2_x4:
sectorSize = SECTOR_SIZE_M29F160FB5AN6E2_8;
break;
}
// If the start address wasn't on a sector boundary, bail
if (curAddress != address)
// Make sure the area requested to be erased is on good boundaries
if ((address % sectorSize) ||
(length % sectorSize))
{
return false;
}
// OK, we've found our first sector to erase.
firstSectorGroup = curSectorGroup;
firstSectorInGroup = curSectorInGroup;
// Now, locate our last sector to erase.
uint32_t curLength = 0;
while (curLength < length &&
curSectorGroup < numEraseSectorGroups)
{
curLength += eraseSectorGroups[curSectorGroup].size;
// If we still haven't handled the entire requested space,
// go to the next sector
if (curLength < length)
{
curSectorInGroup++;
if (curSectorInGroup >= eraseSectorGroups[curSectorGroup].count)
{
curSectorGroup++;
curSectorInGroup = 0;
}
}
}
// If the length wasn't on a sector boundary, bail
if (curLength != length)
{
return false;
}
// We've now verified that everything is on a sector boundary, so we can
// go ahead with the erase operation!
curSectorGroup = firstSectorGroup;
curSectorInGroup = firstSectorInGroup;
// We're good to go. Let's do it. The process varies based on the chip type
if (curChipType == ParallelFlash_SST39SF040_x4)
{
@ -263,15 +183,8 @@ bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chips
// unlock sequence has to be done again after this sector is done.
ParallelBus_WriteCycle(address, 0x30303030UL);
// Move our counters in preparation for the next sector
address += eraseSectorGroups[curSectorGroup].size;
length -= eraseSectorGroups[curSectorGroup].size;
curSectorInGroup++;
if (curSectorInGroup >= eraseSectorGroups[curSectorGroup].count)
{
curSectorGroup++;
curSectorInGroup = 0;
}
address += sectorSize;
length -= sectorSize;
// Wait for completion of this individual erase operation before
// we can start a new erase operation.
@ -289,19 +202,25 @@ bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chips
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);
// Move our counters in preparation for the next sector
address += eraseSectorGroups[curSectorGroup].size;
length -= eraseSectorGroups[curSectorGroup].size;
curSectorInGroup++;
if (curSectorInGroup >= eraseSectorGroups[curSectorGroup].count)
{
curSectorGroup++;
curSectorInGroup = 0;
}
address += sectorSize;
length -= sectorSize;
}
// Wait for completion of the entire erase operation
@ -311,6 +230,7 @@ bool ParallelFlash_EraseSectors(uint32_t address, uint32_t length, uint8_t chips
}
return result;
}
/** Writes a buffer of data to all 4 chips simultaneously

View File

@ -54,13 +54,6 @@ typedef enum ParallelFlashChipType
ParallelFlash_M29F160FB5AN6E2_x4,
} ParallelFlashChipType;
/// Struct representing a group of identical erase sectors
typedef struct ParallelFlashEraseSectorGroup
{
uint32_t count;
uint32_t size;
} ParallelFlashEraseSectorGroup;
// Tells which type of flash chip we are communicating with
void ParallelFlash_SetChipType(ParallelFlashChipType type);
ParallelFlashChipType ParallelFlash_ChipType(void);
@ -76,7 +69,7 @@ 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, uint8_t numEraseSectorGroups, ParallelFlashEraseSectorGroup const *eraseSectorGroups);
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.

View File

@ -46,9 +46,7 @@ typedef enum ProgrammerCommand
ErasePortion,
WriteChipsAt,
ReadChipsAt,
SetChipsMask,
SetSectorLayout,
GetFirmwareVersion
SetChipsMask
} ProgrammerCommand;
// After a command is sent, the programmer will always respond with
@ -186,15 +184,4 @@ typedef enum ProgrammerErasePortionOfChipReply
ProgrammerErasePortionFinished
} ProgrammerErasePortionOfChipReply;
// ------------------------- GET FIRMWARE VERSION PROTOCOL -------------------------
// If the command is GetFirmwareVersion, the programmer will reply CommandReplyOK.
// Next, it will return 4 bytes: major version, minor version, revision, and a final
// byte where 0 means it's a normal version and 1 means it's a prerelease version.
// Other values are reserved.
// Finally, it will finish the response with ProgrammerGetFWVersionDone.
typedef enum ProgrammerGetFWVersionReply
{
ProgrammerGetFWVersionDone
} ProgrammerGetFWVersionReply;
#endif /* PROGRAMMER_PROTOCOL_H_ */

View File

@ -29,7 +29,6 @@
#include "led.h"
#include "hardware.h"
#include <stdbool.h>
#include <string.h>
/// Maximum size of an individual chip on a SIMM we read
#define MAX_CHIP_SIZE (2UL * 1024UL * 1024UL)
@ -39,20 +38,8 @@
#if ((READ_WRITE_CHUNK_SIZE_BYTES % 4) != 0)
#error Read/write chunk size should be a multiple of 4 bytes
#endif
/// The maximum number of erase groups we deal with
#define MAX_ERASE_SECTOR_GROUPS 10
/// Version info to respond with
#define VERSION_MAJOR 1
#define VERSION_MINOR 5
#define VERSION_REVISION 0
/// The number of erase sector groups we know about currently.
/// If it's zero, we don't know, so fall back to defaults.
static uint8_t numEraseSectorGroups = 0;
/// The erase sector groups that we will pass to the programmer
static ParallelFlashEraseSectorGroup eraseSectorGroups[MAX_ERASE_SECTOR_GROUPS];
/// 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
@ -65,7 +52,6 @@ typedef enum ProgrammerCommandState
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
ReadingSectorLayout, //!< Reading the erase sector layout
} ProgrammerCommandState;
static ProgrammerCommandState curCommandState = WaitingForCommand;
@ -98,7 +84,6 @@ 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);
static void SIMMProgrammer_HandleReadingSectorLayoutByte(uint8_t byte);
/** Initializes the SIMM programmer and prepares it for USB communication.
*
@ -147,9 +132,6 @@ void SIMMProgrammer_Check(void)
case ReadingChipsMask:
SIMMProgrammer_HandleReadingChipsMaskByte(recvByte);
break;
case ReadingSectorLayout:
SIMMProgrammer_HandleReadingSectorLayoutByte(recvByte);
break;
}
}
@ -282,18 +264,6 @@ static void SIMMProgrammer_HandleWaitingForCommandByte(uint8_t byte)
curCommandState = ReadingChipsMask;
USBCDC_SendByte(CommandReplyOK);
break;
case SetSectorLayout:
curCommandState = ReadingSectorLayout;
USBCDC_SendByte(CommandReplyOK);
break;
case GetFirmwareVersion:
USBCDC_SendByte(CommandReplyOK);
USBCDC_SendByte(VERSION_MAJOR);
USBCDC_SendByte(VERSION_MINOR);
USBCDC_SendByte(VERSION_REVISION);
USBCDC_SendByte(0);
USBCDC_SendByte(ProgrammerGetFWVersionDone);
break;
// We don't know what this command is, so reply that it was invalid.
default:
USBCDC_SendByte(CommandReplyInvalid);
@ -553,28 +523,44 @@ static void SIMMProgrammer_HandleErasePortionReadPosLengthByte(uint8_t byte)
if (++readLengthByteIndex >= 8)
{
ParallelFlashChipType chipType = ParallelFlash_ChipType();
bool eraseSuccess = false;
// Ensure the position and length are a multiple of 4 so that the division by 4
// won't confuse anything.
if (((erasePosition % 4) == 0) &&
((eraseLength % 4) == 0))
// 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 our addressable length too.
// We can't address more than 8 MB of data at a time.
if (boundary <= (8 * 1024UL * 1024UL))
// Ensure they are within the limits of the chip size too
if (chipType == ParallelFlash_SST39SF040_x4)
{
// OK! We're erasing certain sectors of a 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,
numEraseSectorGroups, eraseSectorGroups))
if (boundary <= (8 * 1024UL * 1024UL))
{
eraseSuccess = true;
// OK! We're erasing certain sectors of a 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 a 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;
}
}
}
}
@ -660,57 +646,3 @@ static void SIMMProgrammer_HandleReadingChipsMaskByte(uint8_t byte)
// Done either way; now we're waiting for a command to arrive
curCommandState = WaitingForCommand;
}
/** Handles a received byte when we are reading in the sector layout
*
* @param byte The received byte, which is the first sector layout byte
*/
static void SIMMProgrammer_HandleReadingSectorLayoutByte(uint8_t byte)
{
numEraseSectorGroups = 0;
uint32_t sectorCount = byte;
uint32_t sectorSize = 0;
int byteIndex = 1;
while (1)
{
// Read in the sector size
for (int i = byteIndex; i < 4; i++)
{
uint32_t nextByte = (uint32_t)USBCDC_ReadByteBlocking();
sectorCount |= nextByte << (i * 8);
}
// From now on, we loop over 4 bytes, not 3
byteIndex = 0;
// If we read in a count of 0, we're done!
if (sectorCount == 0)
{
break;
}
// We have a nonzero count, so read in the size now
for (int i = 0; i < 4; i++)
{
uint32_t nextByte = (uint32_t)USBCDC_ReadByteBlocking();
sectorSize |= nextByte << (i * 8);
}
// If we have room to store it in the array, do it
if (numEraseSectorGroups < MAX_ERASE_SECTOR_GROUPS)
{
eraseSectorGroups[numEraseSectorGroups].count = sectorCount;
eraseSectorGroups[numEraseSectorGroups].size = sectorSize;
numEraseSectorGroups++;
}
// Now read in the next chunk of data
sectorCount = 0;
sectorSize = 0;
}
// We got the list. Done!
USBCDC_SendByte(CommandReplyOK);
curCommandState = WaitingForCommand;
}