471 lines
16 KiB
C
471 lines
16 KiB
C
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: iocp.c
|
|
// Created: January 2019
|
|
// Author(s): Philip Smart
|
|
// Description: ZPU bootloader, termed IOCP (IO Control Program).
|
|
// This program initialises the ZPU, allows basic interaction such as program
|
|
// upload and transfers control to a main application if available.
|
|
// On startup, if no intput (via the serial console) has been detected within 1 second
|
|
// and an application is stored in BRAM or available via SD card (which it loads),
|
|
// control is transferred to this program.
|
|
//
|
|
// Credits:
|
|
// Copyright: (c) 2019 Philip Smart <philip.smart@net2net.org>
|
|
//
|
|
// History: January 2019 - Initial script written.
|
|
// July 2019 - Stripped down to the bare minimum, all other functionality moved
|
|
// into the testapp.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This source file 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 source file 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 <http://www.gnu.org/licenses/>.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#include <zstdio.h>
|
|
#include <stdlib.h>
|
|
#include <zpu-types.h>
|
|
#include "zpu_soc.h"
|
|
#include "uart.h"
|
|
#include "interrupts.h"
|
|
#include "pff.h" /* Declarations of FatFs API */
|
|
#include "diskio.h"
|
|
#include <string.h>
|
|
#include "simple_utils.h"
|
|
#include "iocp.h"
|
|
|
|
// Version info.
|
|
#define VERSION "v1.5"
|
|
#define VERSION_DATE "29/08/2019"
|
|
|
|
// Method to process interrupts. This involves reading the interrupt status register and then calling
|
|
// the handlers for each triggered interrupt. A read of the interrupt controller clears the interrupt pending
|
|
// register so new interrupts will be processed after this method exits.
|
|
//
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 1
|
|
static volatile unsigned int autobootTimer = 0;
|
|
void interrupt_handler()
|
|
{
|
|
// Read the interrupt controller to find which devices caused an interrupt.
|
|
//
|
|
uint32_t intr = INTERRUPT_STATUS(INTR0);
|
|
|
|
// Prevent additional interrupts.
|
|
DisableInterrupts();
|
|
|
|
if(INTR_IS_TIMER(intr))
|
|
{
|
|
autobootTimer = autobootTimer + 1;
|
|
}
|
|
|
|
// Enable new interrupts.
|
|
EnableInterrupts();
|
|
}
|
|
|
|
// Method to enable the timer.
|
|
//
|
|
void enableTimer()
|
|
{
|
|
OPTIONAL(puts("Enabling timer...\n"));
|
|
TIMER_ENABLE(TIMER1) = 1; // Enable timer 1
|
|
}
|
|
#endif
|
|
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY == 0
|
|
// Function to upload an application into memory from the serial channel in binary.
|
|
//
|
|
int uploadToMemory(uint32_t memAddr, uint32_t memSize)
|
|
{
|
|
// Locals.
|
|
uint8_t resultCode = 1;
|
|
|
|
// Indicate mode selected and instructions to start upload.
|
|
//
|
|
OPTIONAL(puts("Binary upload, waiting...\n"));
|
|
|
|
// Initialise CRC.
|
|
//
|
|
uint32_t crcDst = crc32_init();
|
|
|
|
// Wait for start sequence.
|
|
//
|
|
int start_seq = 0;
|
|
while(start_seq == 0)
|
|
{
|
|
if(getserial() == 'I')
|
|
if(getserial() == 'O')
|
|
if(getserial() == 'C')
|
|
if(getserial() == 'P')
|
|
{
|
|
start_seq = 1;
|
|
}
|
|
}
|
|
|
|
// Get size of image and validate.
|
|
//
|
|
uint32_t image_size = get_dword();
|
|
if (image_size > memSize-8)
|
|
{
|
|
puts(" ERROR! Upload too big!\n\n");
|
|
} else
|
|
{
|
|
// Get CRC of image.
|
|
//
|
|
uint32_t crcSrc = get_dword();
|
|
|
|
// Read in image_size words and store.
|
|
//
|
|
uint32_t data_pointer = memAddr;
|
|
uint32_t count = image_size;
|
|
uint32_t word;
|
|
while(count > 0)
|
|
{
|
|
word = get_dword();
|
|
*(uint32_t *)data_pointer = word;
|
|
crcDst = crc32_addword(crcDst, word);
|
|
data_pointer = data_pointer + 1;
|
|
count-=4;
|
|
}
|
|
crcDst = ~crcDst;
|
|
|
|
// Short delay to allow uploader to terminate.
|
|
//
|
|
//delay(1000);
|
|
|
|
// Debug output to verify data.
|
|
OPTIONAL(dbg_puts("Image_Size="));
|
|
OPTIONAL(dbg_printdhex(image_size));
|
|
OPTIONAL(dbg_puts(", "));
|
|
OPTIONAL(dbg_puts("Source CRC="));
|
|
OPTIONAL(dbg_printdhex(crcSrc));
|
|
OPTIONAL(dbg_puts(", Destination CRC="));
|
|
OPTIONAL(dbg_printdhex(crcDst));
|
|
OPTIONAL(dbg_puts("\n"));
|
|
|
|
// If CRCs dont match then indicate failure.
|
|
if(crcSrc != crcDst)
|
|
{
|
|
puts("CRC mismatch.\r\n");
|
|
} else
|
|
resultCode = 0;
|
|
}
|
|
|
|
// Return success (0) or fail (1).
|
|
return(resultCode);
|
|
}
|
|
#endif
|
|
|
|
// Function to output the IOCP version information and optionally the
|
|
// configured hardware details.
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 2
|
|
void printVersion(uint8_t showConfig)
|
|
{
|
|
#if FUNCTIONALITY == 0
|
|
// Basic title showing name and Cpu Id.
|
|
puts("\n** IOCP BIOS (");
|
|
printZPUId(cfgSoC.zpuId);
|
|
puts(" ZPU, rev");
|
|
printhexbyte((uint8_t)cfgSoC.zpuId);
|
|
puts(") " VERSION " " VERSION_DATE " **\n");
|
|
|
|
// Show configuration if requested.
|
|
if(showConfig)
|
|
{
|
|
showSoCConfig();
|
|
}
|
|
#else
|
|
puts("IOCP " VERSION " " VERSION_DATE "\n");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// Interactive command processor. Allow user to input a command and execute accordingly.
|
|
//
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 1
|
|
int cmdProcessor(void)
|
|
{
|
|
int8_t funcSelect;
|
|
int8_t cfgAvail = 0;
|
|
uint32_t startAppAddr = 0;
|
|
uint32_t memAddr;
|
|
FILINFO fno;
|
|
FRESULT rc;
|
|
DIR dir;
|
|
|
|
// Initial prompt.
|
|
puts("* ");
|
|
while(1)
|
|
{
|
|
// console input
|
|
funcSelect = getserial_nonblocking();
|
|
if(funcSelect != -1)
|
|
{
|
|
putchar((char)funcSelect);
|
|
puts("\n");
|
|
}
|
|
|
|
// Execute according to key selection.
|
|
switch((char)funcSelect)
|
|
{
|
|
// Boot from Boot BRAM (start application)
|
|
case '0':
|
|
startAppAddr = BRAM_APP_START_ADDR;
|
|
break;
|
|
|
|
// Boot from RAM (start application)
|
|
case '1':
|
|
startAppAddr = cfgSoC.addrRAM;
|
|
break;
|
|
|
|
#if FUNCTIONALITY == 0
|
|
// load bram via UART0
|
|
case '2':
|
|
// If BRAM is implemented then upload to the application area with limit 512 bytes below stack frame.
|
|
if(cfgSoC.implBRAM)
|
|
uploadToMemory(cfgSoC.addrBRAM + BRAM_APP_START_ADDR, cfgSoC.sizeBRAM - BRAM_APP_START_ADDR - 504);
|
|
break;
|
|
|
|
case '3':
|
|
// If RAM or SDRAM is implemented then upload with size limit 512 bytes below stack frame.
|
|
if(cfgSoC.implRAM || cfgSoC.implSDRAM)
|
|
uploadToMemory(cfgSoC.addrRAM, cfgSoC.sizeRAM - 504);
|
|
break;
|
|
#endif
|
|
|
|
// BRAM Memory dump
|
|
case '4':
|
|
if(cfgSoC.implInsnBRAM || cfgSoC.implBRAM)
|
|
{
|
|
puts("Dump BRAM Memory\n");
|
|
memoryDump(cfgSoC.addrBRAM, cfgSoC.sizeBRAM);
|
|
puts("\n\nDumping completed.\n\n");
|
|
} else
|
|
{
|
|
OPTIONAL(puts("BRAM memory not implemented.\n"));
|
|
}
|
|
break;
|
|
|
|
// Stack Memory dump - whichever memory is being used, dump out the stack.
|
|
case '5':
|
|
puts("Dump Stack Memory\n");
|
|
memoryDump(cfgSoC.stackStartAddr-504, cfgSoC.stackStartAddr+8);
|
|
puts("\n\nDumping completed.\n\n");
|
|
break;
|
|
|
|
// RAM Memory dump
|
|
case '6':
|
|
if(cfgSoC.implRAM)
|
|
{
|
|
puts("Dump RAM\n");
|
|
memoryDump(cfgSoC.addrRAM, cfgSoC.sizeRAM);
|
|
puts("\n\nDumping completed.\n\n");
|
|
} else
|
|
{
|
|
OPTIONAL(puts("RAM memory not implemented.\n"));
|
|
}
|
|
break;
|
|
|
|
// Clear BRAM.
|
|
case 'C':
|
|
if(cfgSoC.implBRAM && cfgSoC.implInsnBRAM)
|
|
{
|
|
puts("Clearing BRAM Memory\n");
|
|
for(memAddr=cfgSoC.addrBRAM; memAddr < (cfgSoC.addrBRAM+cfgSoC.sizeBRAM); memAddr+=4)
|
|
{
|
|
*(uint32_t *)(memAddr) = 0x00000000;
|
|
}
|
|
} else
|
|
{
|
|
puts("BRAM memory not implemented.\n");
|
|
}
|
|
break;
|
|
|
|
// Clear RAM.
|
|
case 'c':
|
|
if(cfgSoC.implRAM)
|
|
{
|
|
puts("Clearing RAM\n");
|
|
for(memAddr=cfgSoC.addrRAM; memAddr < (cfgSoC.addrRAM+cfgSoC.sizeRAM); memAddr+=4)
|
|
{
|
|
*(uint32_t *)(memAddr) = 0xaa55ff00;
|
|
}
|
|
} else
|
|
{
|
|
puts("RAM memory not implemented.\n");
|
|
}
|
|
break;
|
|
|
|
// List the SD directory contents.
|
|
case 'd':
|
|
rc = pf_opendir(&dir, "");
|
|
if(!rc)
|
|
{
|
|
for (;;) {
|
|
rc = pf_readdir(&dir, &fno); /* Read a directory item */
|
|
if (rc || !fno.fname[0]) break; /* Error or end of dir */
|
|
if (fno.fattrib & AM_DIR)
|
|
{
|
|
puts(" <dir> "); puts(fno.fname); puts("\n");
|
|
} else
|
|
{
|
|
printdhex(fno.fsize); puts(" "); puts(fno.fname); puts("\n");
|
|
}
|
|
}
|
|
}
|
|
if(rc) { puts("Error: "); printhex(rc); puts("\n"); }
|
|
break;
|
|
|
|
// Reset the system.
|
|
case 'R':
|
|
puts("Restarting...\n");
|
|
void *strtptr = (void *)0x00000;
|
|
goto *strtptr;
|
|
break;
|
|
|
|
// Help screen
|
|
case 'h':
|
|
printVersion(false);
|
|
puts("0: Execute App in Boot BRAM. 1: Execute App in RAM\n"
|
|
"2: Upload App to BRAM. 3: Upload App to RAM.\n"
|
|
"4: Dump BRAM Memory. 5: Dump Stack Memory.\n"
|
|
"6: Dump RAM Memory. d: List SD directory.\n"
|
|
"c: Clear RAM. C: Clear BRAM App Memory.\n"
|
|
"h: Show this screen. i: Configuration information.\n"
|
|
"R: Reset system.\n");
|
|
break;
|
|
|
|
// Configuration information
|
|
case 'i':
|
|
printVersion(true);
|
|
break;
|
|
|
|
// No input
|
|
case -1:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// If a key was pressed then re-output prompt for next key.
|
|
//
|
|
if(funcSelect != -1)
|
|
{
|
|
puts("* ");
|
|
}
|
|
|
|
// If the autoboot timer has expired then attempt to autoboot preloaded application.
|
|
//
|
|
if(autobootTimer > 5)
|
|
{
|
|
// If BRAM is implemented and the application program memory portion is not empty, execute it.
|
|
if(cfgSoC.implBRAM && *(uint32_t *)(cfgSoC.addrBRAM + BRAM_APP_START_ADDR) != 0x00000000)
|
|
{
|
|
startAppAddr = cfgSoC.addrBRAM + BRAM_APP_START_ADDR;
|
|
} else
|
|
// Else if RAM is implemented (either as static/BRAM or SDRAM) and the program memory is not empty, execute it.
|
|
if((cfgSoC.implRAM || cfgSoC.implSDRAM) && *(uint32_t *)(cfgSoC.addrRAM) != 0x00000000)
|
|
{
|
|
startAppAddr = cfgSoC.addrRAM;
|
|
}
|
|
OPTIONAL(if(startAppAddr != 0) { puts("..autobooting.\n"); });
|
|
}
|
|
|
|
// Start application request
|
|
if(startAppAddr != 0)
|
|
{
|
|
// Disable interrupts before starting application, application can reenable if needed.
|
|
DisableInterrupt(INTR_TIMER);
|
|
|
|
// Start application
|
|
OPTIONAL(puts("\nStart App @ 0x"); printdhex(startAppAddr); putchar((int)'\n'));
|
|
void *jmpptr = (void *)startAppAddr;
|
|
goto *jmpptr;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Main program entry point.
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Setup the required baud rate for the UART. Once the divider is loaded, a reset takes place within the UART.
|
|
UART_BRGEN(UART0) = BAUDRATEGEN(UART0, 115200, 115200);
|
|
UART_BRGEN(UART1) = BAUDRATEGEN(UART1, 115200, 115200);
|
|
|
|
// Enable the RX/TX units and enable FIFO mode.
|
|
UART_CTRL(UART0) = UART_TX_FIFO_ENABLE | UART_TX_ENABLE | UART_RX_FIFO_ENABLE | UART_RX_ENABLE;
|
|
UART_CTRL(UART1) = UART_TX_FIFO_ENABLE | UART_TX_ENABLE | UART_RX_FIFO_ENABLE | UART_RX_ENABLE;
|
|
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 2
|
|
// Ensure interrupts are disabled before configuring the SoC.
|
|
DisableInterrupts();
|
|
|
|
// Setup the configuration using the SoC configuration register if implemented otherwise the compiled internals.
|
|
setupSoCConfig();
|
|
#endif
|
|
|
|
// Start the timer running and enable interrupts. The time count is used to decide if we autoboot.
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 1
|
|
SetIntHandler(interrupt_handler);
|
|
EnableInterrupt(0);
|
|
|
|
// Intro screen, show title and configuration.
|
|
printVersion(true);
|
|
#endif
|
|
|
|
// Try and mount the disk.
|
|
if(pf_mount(&FatFs))
|
|
{
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 2
|
|
puts("Failed to mount disk.\n");
|
|
#endif
|
|
} else
|
|
{
|
|
// Try and open the source file. If no errors in opening the file, proceed with reading and loading into memory.
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY == 3
|
|
if(!pf_open(BOOT_TINY_FILE_NAME))
|
|
#else
|
|
if(!pf_open(BOOT_FILE_NAME))
|
|
#endif
|
|
{
|
|
char *memPtr = (char *)BOOT_LOAD_ADDR;
|
|
void *gotoptr = (void *)BOOT_EXEC_ADDR;
|
|
uint32_t readSize;
|
|
|
|
// Indicate booting...
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 2
|
|
puts("Boot SD\n");
|
|
#endif
|
|
|
|
// Load the application into memory and execute.
|
|
//
|
|
TIMER_MILLISECONDS_UP = 0;
|
|
for (;;) {
|
|
if(pf_read(memPtr, 512, &readSize)) break; /* error */
|
|
if(readSize == 0) break; /* eof */
|
|
memPtr += readSize;
|
|
}
|
|
goto *gotoptr;
|
|
} else
|
|
{
|
|
#if !defined(FUNCTIONALITY) || FUNCTIONALITY <= 1
|
|
// Command processor. If it exits, then reset the CPU.
|
|
cmdProcessor();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Reboot as it is not normal if auto boot fails or the command processor terminates.
|
|
void *rbtptr = (void *)0x00000000;
|
|
goto *rbtptr;
|
|
}
|
|
// Should never get here...
|