/*****************************************************************************
* File: custom_boot_copier.c
*
* This is an example of a custom Nios II boot copier implemented in C.
* The boot copier can be built and run from Nios II IDE. For instructions
* on how to build and run this boot copier, refer to the application note
* that accompianies it.
*
* Feel free to customize this boot copier, but do so at your own risk. This
* boot copier can only be supported by Altera in its current, unmodified form
*
*****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "advanced_boot_copier.h"
#include "system.h"
#include "alt_types.h"
#include "sys/alt_alarm.h"
#include "sys/alt_cache.h"
#include "sys/alt_dev.h"
#include "sys/alt_irq.h"
#include "sys/alt_sys_init.h"
#include "priv/alt_file.h"
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_jtag_uart_regs.h"
#include "altera_avalon_jtag_uart.h"
/*
* Edit this define to turn on or turn off the JTAG UART during boot
*/
#define USING_JTAG_UART 0
/*
* This example prints a lot of information, this macro is intended to make the
* code a bit easier to read. If you do not wish to print anything during boot,
* change the define at the top of this file to "#define USING_JTAG_UART 0"
*/
#if USING_JTAG_UART
# define JTAGPRINT(name) MyJtagWrite(name, sizeof(name)-1)
# define JTAG_UART_TIMEOUT ( alt_ticks_per_second() * 2 ) // 2 seconds
# define JTAG_WAITING_BIT (1 << 1)
# define JTAG_ABANDONED_BIT (1 << 2)
// Stores the transmit state of the JTAG_UART
volatile alt_u32 jtag_uart_state;
// Prototypes for our JTAG UART printing functions
int MyJtagWrite(const char *buf, int len);
int MyJtagWrite8(const char *buf, int len);
#endif //USING_JTAG_UART
/*
* The following statement defines "main()" so that when the Nios II SBT4E
* debugger is set to break at "main()", it will break at the appropriate
* place in this program, which does not contain a function called "main()".
*/
int main (void) __attribute__ ((weak, alias ("alt_main")));
/*****************************************************************************
* Function: alt_main
*
* Purpose: This is our boot copier's entry point. We are implementing
* this as an alt_main() instead of a main(), so that we can better control
* the drivers that load and the system resources that are enabled. Since
* code size may be a consideration, this method allows us to keep the
* memory requirements small.
*
*****************************************************************************/
int alt_main(void)
{
/*
* Define the local variables used by this function.
*/
alt_u32 entry_point;
/*
* Define the strings used in this function for printing out STDOUT. The
* purpose of this is to avoid using the potentially large printf C library.
*/
# if USING_JTAG_UART
char stars_str[] = "****************************************\n";
char hello_str0[] = " Example Custom Boot Copier Starting\n Booting EPCS from On-Chip RAM \n\n";
char hello_str1[] = "\nThis copier finds application images\n after .pof in EPCS memory.\n";
char boot_proceed_str[] = "\nNow attempting to boot.\n";
char reset_str[] = "\nNow jumping back to reset vector.\n";
char no_image_str[] = "\nNo valid application image found in\nflash memory.\n";
char jumping_str[] = "\nNow attempting to load and jump to the\napplication.\n\n";
// Initialize the STDIO defaults as selected in the Nios II IDE.
alt_io_redirect (ALT_STDOUT, ALT_STDIN, ALT_STDERR);
jtag_uart_state = 0x1;
# endif // USING_JTAG_UART
/*
* Perform system initialization. Since our entry point is alt_main instead
* of simply main, it's our responsibility to initialize any drivers we
* wish to use. An example of how to do this can be found in the
* hello_world freestanding demo.
*/
/*
* In order to allow interrupts to occur while the boot copier executes we
* initialize the main irq handler.
*/
alt_irq_init (ALT_IRQ_BASE);
/*
* Now we initialize the drivers that we require.
*/
alt_sys_init();
/*
* If you intend to print anything during boot, you'll need to initialize
* the STDIO defaults as selected in the Nios II IDE. If you dont need
* to print anything during boot, you can comment out this line.
*/
# if USING_JTAG_UART
alt_io_redirect (ALT_STDOUT, ALT_STDIN, ALT_STDERR);
jtag_uart_state = 0x1;
# endif // USING_JTAG_UART
/*
* Now the system is initialized and ready to use.
*
* At this point you could start doing whatever you feel necessary. You could
* perform diagnostics, validate the hardware environment, communicate with
* other parts of the system to obtain instructions how to proceed, etc., etc
*
* In this example, we'll just turn on an LED, print some basic information
* to the JTAG UART, and then boot an application from flash.
*/
// Turn on an LED to indicate we are alive
IOWR_ALTERA_AVALON_PIO_DATA(IOLED_BASE, 0x1);
/*
* Print a startup message to let the user see we're alive.
*/
# if USING_JTAG_UART
JTAGPRINT(stars_str);
JTAGPRINT(stars_str);
JTAGPRINT(hello_str0);
JTAGPRINT(stars_str);
JTAGPRINT(hello_str1);
JTAGPRINT(stars_str);
JTAGPRINT(stars_str);
# endif //USING_JTAG_UART
/*
* Now we begin the boot process.
*/
// Print a message to the JTAG UART that we're beginning to boot
#if USING_JTAG_UART
JTAGPRINT(boot_proceed_str);
#endif //USING_JTAG_UART
/*
* Pick a flash image to load. The criteria for picking an image are
* discussed the text of the application note, and also in the code comments
* preceeding the function "PickFlashImage()" found in this file.
*/
#if USING_JTAG_UART
JTAGPRINT(jumping_str);
#endif //USING_JTAG_UART
alt_irq_disable_all ();
entry_point = findImage();
if( entry_point >= 0 ) // load the image
{
// Turn on an LED to indicate we are jumping to a valid image.
IOWR_ALTERA_AVALON_PIO_DATA(IOLED_BASE, 0x2);
// Jump to the entry point of the application
JumpFromBootCopier((void(*)(void))(entry_point));
}
else
{
#if USING_JTAG_UART
// Print which image was picked.
JTAGPRINT(no_image_str);
#endif //USING_JTAG_UART
// Turn on an LED to indicate we are resetting
IOWR_ALTERA_AVALON_PIO_DATA(IOLED_BASE, 0x4);
usleep(5000000);
#if USING_JTAG_UART
// Print which image was picked.
JTAGPRINT(reset_str);
#endif //USING_JTAG_UART
// If the entry point is not found, then we should jump back to the
// reset vector.
JumpFromBootCopier((void(*)(void))(NIOS2_RESET_ADDR));
}
// We should never get here
exit(0);
}
/*****************************************************************************
* Function: JumpFromBootCopier
*
* Purpose: This routine shuts down the boot copier and jumps somewhere else.
* The place to jump is passed in as a function pointer named "target".
*
*****************************************************************************/
void JumpFromBootCopier(void target(void))
{
/*
* If you have any outstanding I/O or system resources that needed to be
* cleanly disabled before leaving the boot copier program, then this is
* the place to do that.
*
* In this example we only need to ensure the state of the Nios II cpu is
* equivalent to reset. If we disable interrupts, and flush the caches,
* then the program we jump to should receive the cpu just as it would
* coming out of a hardware reset.
*/
alt_irq_disable_all ();
alt_dcache_flush_all ();
alt_icache_flush_all ();
/*
* The cpu state is as close to reset as we can get it, so we jump to the new
* application.
*/
target();
/*
* In the odd event that the program we jump to decides to return, we should
* probably just jump back to the reset vector. We pass in the reset address
* as a function pointer.
*/
// Turn on an LED to indicate we are resetting
IOWR_ALTERA_AVALON_PIO_DATA(IOLED_BASE, 0x4);
// Wait 5 seconds
usleep(5000000);
// Jump back to the reset address
JumpFromBootCopier((void(*)(void))(NIOS2_RESET_ADDR));
}
/*****************************************************************************
* Function: CopyFromFlash
*
* Purpose: This subroutine copies data from a flash memory to a buffer
* The function uses the appropriate copy routine for the flash that is
* defined by FLASH_TYPE. EPCS devices cant simply be read from using
* memcpy().
*
*****************************************************************************/
void* CopyFromFlash( void * dest, const void * src, size_t num )
{
// If we're dealing with EPCS, "src" has already been defined for us as
// an offset into the EPCS, not an absolute address.
epcs_read_buffer( EPCS_FLASH_CONTROLLER_BASE + EPCS_FLASH_CONTROLLER_REGISTER_OFFSET,
(int)src,
(alt_u8*)dest,
(int)num );
return (dest);
}
#if USING_JTAG_UART
/*****************************************************************************
* Function: MyJtagWrite
*
* Purpose: This subroutine is provided so that we can write characters out
* the JTAG UART without getting into a situation where we are permanently
* blocked, because there is no host reading from the JTAG UART. So this
* routine takes the message that we want to write, and breaks it up into
* 8 byte or less chunks to send to MyJtagWrite8(). MyJtagWrite8()
* makes sure that we can write to the jtag uart before attempting the write.
*
*****************************************************************************/
int MyJtagWrite(const char *buf, int len)
{
int ret_val;
int orig_len = len;
if(len > 8)
{
// Our write message is greater than 8 bytes long, so break it up into
// 8-byte or less chunks.
do
{
ret_val = MyJtagWrite8( buf, (len > 8)?(8):(len));
if(ret_val < 0)
{
// If an error returns then we're done
break;
}
else
{
// No error, decrement our length, and increment the buffer
len -= ret_val;
buf += ret_val;
}
} while(len > 0);
// Return the whole length of the buffer transmitted
ret_val = orig_len;
}
else
{
// If the write message is less than 8 bytes, just write it.
ret_val = MyJtagWrite8( buf, len);
}
return ret_val;
}
/*****************************************************************************
* Function: MyJtagWrite8
*
* Purpose: This subroutine is called by MyJtagWrite, which feeds it 8-byte
* or less chunks of data to write out the JTAG UART. Basically, this routine
* queries the write fifo in the jtag uart to make sure there is room for our
* data first. If there's not room, then it waits JTAG_UART_TIMEOUT seconds
* to see if a host will clear some of the pending data to allow us to write
* new data to the fifo. If no host connects within JTAG_UART_TIMEOUT, then
* the jtag uart is marked abandoned, and the write data is discarded. Once
* abandoned, every time a new write call is made to this routine, it checks
* to see if a host may have emptied the write fifo, if that ever happens,
* then the jtag uart is reclaimed and the original algorithm prevails.
*
*****************************************************************************/
int MyJtagWrite8(const char *buf, int len)
{
alt_fd *the_fd;
altera_avalon_jtag_uart_dev *the_dev;
altera_avalon_jtag_uart_state *the_state;
unsigned int the_base;
alt_u32 control;
alt_u32 wr_fifo_space;
int ret_val;
alt_alarm my_jtag_uart_alarm;
volatile alt_u32 my_jtag_uart_context;
/*
* Look thru the device table to find our device block and extract the base
* address to this STDOUT peripheral.
*
* A big assumption here is that the STDOUT peripheral is a JTAG UART.
*/
the_fd = &alt_fd_list[STDOUT_FILENO];
the_dev = (altera_avalon_jtag_uart_dev *)the_fd->dev;
the_state = (altera_avalon_jtag_uart_state *)&(the_dev->state);
the_base = the_state->base;
// Read the jtag uart control register and grab the write fifo space
control = IORD_ALTERA_AVALON_JTAG_UART_CONTROL(the_base);
wr_fifo_space = (control & ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_MSK)
>> ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_OFST;
if(wr_fifo_space >= len)
{
// The write fifo has room for our write, so clear the abandoned flag
// and write the message.
ret_val = write(STDOUT_FILENO, buf, len);
}
else
{
// The write fifo does not have room for us. have we previously abandoned
// the jtag uart?
if( jtag_uart_state & JTAG_ABANDONED_BIT )
{
// We have previously abandoned the jtag uart
if(control & ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK)
{
// There has been activity from a host so let's clear the abandoned
// flag, and see if the host will clear the fifo for us.
jtag_uart_state &= ~JTAG_ABANDONED_BIT;
}
}
// At this point we check to see if the jtag uart is abandoned
if( jtag_uart_state & JTAG_ABANDONED_BIT )
{
// The jtag uart has been abandoned, so just dump the data and return -1
ret_val = -1;
}
else
{
// The jtag uart has not been abandoned
ret_val = -1;
// Clear the activity bit in the jtag uart control register
control |= ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK;
IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(the_base, control);
// Set a timeout alarm
my_jtag_uart_context = 0;
alt_alarm_start (
&my_jtag_uart_alarm, // alt_alarm* alarm,
JTAG_UART_TIMEOUT, // alt_u32 nticks,
GenericTimeoutCallback, // alt_u32 (*callback) (void* context),
(void *)&my_jtag_uart_context // void* context
);
// Now wait until the timeout occurs and abandon the uart, or the host
// clears the fifo for us.
while( my_jtag_uart_context == 0 )
{
// Get the current control register value
control = IORD_ALTERA_AVALON_JTAG_UART_CONTROL(the_base);
if( control & ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK )
{
// We see activity, so stop the timeour alarm
alt_alarm_stop ( &my_jtag_uart_alarm );
my_jtag_uart_context = 0;
// Extract the write fifo space
wr_fifo_space = (control & ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_MSK)
>> ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_OFST;
if(wr_fifo_space >= len)
{
// We now have room to perform our write, so do it and get outa here
ret_val = write(STDOUT_FILENO, buf, len);
break;
}
// There's still not enough room so clear the activity bit
control |= ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK;
IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(the_base, control);
// And set the timeout alarm again
alt_alarm_start (
&my_jtag_uart_alarm, // alt_alarm* alarm,
JTAG_UART_TIMEOUT, // alt_u32 nticks,
GenericTimeoutCallback, // alt_u32 (*callback) (void* context),
(void *)&my_jtag_uart_context // void* context
);
}
}
// If we get here, then we're done waiting, so clear the waiting flag
jtag_uart_state &= ~JTAG_WAITING_BIT;
if( my_jtag_uart_context == 1 )
{
// We got here because the timeout alarm fired, so set the abandoned flag
jtag_uart_state |= JTAG_ABANDONED_BIT;
}
}
}
return ret_val;
}
/*****************************************************************************
* Function: GenericTimeoutCallback
*
* Purpose: This subroutine is a generic timeout callback routine for
* timeout alarms that get set. This routine simply increments the alt_u32
* pointed to by the context pointer and returns 0, which requests no
* additional alarm time.
*
*****************************************************************************/
alt_u32 GenericTimeoutCallback (void* context)
{
*((volatile alt_u32 *)(context)) += 0x1;
return(0);
}
#endif //USING_JTAG_UART
long findImage(void)
{
alt_u32 r_findp_temp = 0;
alt_u8 i;
alt_u8 array[100];
alt_u32 r_read_byte_return_value;
CopyFromFlash( (void*)(array), 0, (size_t)(100) );
for(i=0; i<25; i++)
{
r_read_byte_return_value = array[i+48] & 0x20;
r_read_byte_return_value <<= 26;
r_findp_temp >>= 1;
r_findp_temp |= r_read_byte_return_value;
}
for(i=0; i<7; i++)
{
r_read_byte_return_value = array[i+33] & 0x10;
r_read_byte_return_value <<= 27;
r_findp_temp >>= 1;
r_findp_temp |= r_read_byte_return_value;
}
//
// Finally, it turns out the length was given in BITS. Round-up
// to the next byte, and convert to bytes
//
r_findp_temp += 7;
r_findp_temp /= 8;
//Now we have begin address program in EPCS
alt_u32 length;
alt_u32 address;
CopyFromFlash( (void*)(&length), (void*)r_findp_temp, (size_t)(4) );
r_findp_temp += 4;
// Now loop until we get jump record, or a halt recotd
while( (length != 0) && (length != 0xffffffff) )
{
// Get the next 4 bytes of the boot record, which should be an address
// record
CopyFromFlash( (void*)(&address), (void*)r_findp_temp, (size_t)(4) );
r_findp_temp += 4;
// Copy the next "length" bytes to "address"
//if(address != NIOS2_RESET_ADDR)
CopyFromFlash( (void*)(address), (void*)r_findp_temp, (size_t)(length) );
r_findp_temp += length;
// Get the next 4 bytes of the boot record, which now should be another
// length record
CopyFromFlash( (void*)(&length), (void*)r_findp_temp, (size_t)(4) );
r_findp_temp += 4;
}
// "length" was read as either 0x0 or 0xffffffff, which means we are done
// copying.
if( length == 0xffffffff )
{
// We read a HALT record, so return a -1
return -1;
}
else // length == 0x0
{
// We got a jump record, so read the next 4 bytes for the entry address
CopyFromFlash( (void*)(&address), (void*)r_findp_temp, (size_t)(4) );
r_findp_temp += 4;
// Return the entry point address
return address;
}
}
//return 1 if test ram is Ok
int testRAM(void)
{
//code of test
return 0;
}