Altera提供了两个bootloader程序,一个用于从EPCS器件中boot,另一个用于从flash器件中boot。它们的汇编源码和makefile都在C:\altera\kits\nios2_51\components\altera_nios2\sdk\src\boot_loader_sources目录中。其中boot_loader.s是公共部分,而boot_loader_epcs_bits.s则用于从EPCS器件中Boot,boot_loader_cfi_bits.s用于从flash中Boot。
5.1 boot_loader.s解读
#ifdef EPCS
#define FIND_PAYLOADsub_find_payload_epcs// 查找EPCS中数据负荷子程序
#define READ_INTsub_read_int_from_flash_epcs// 从EPCS中读取一个32位word
#define STREAMING_COPY sub_streaming_copy_epcs// 从EPCS中拷贝流的子程序
#define CLOSE_DEVICEsub_epcs_close// 关闭EPCS器件的子程序
#else
#define FIND_PAYLOADsub_find_payload_cfi// 查找CFI并行flash中数据负荷的子程序
#define READ_INTsub_read_int_from_flash_cfi// 从CFI并行flash中读取一个32位的word
#define STREAMING_COPY sub_streaming_copy_cfi// 从CFI并行flash中拷贝流的子程序
#endif
#include "boot_loader.h"
.global reset
.global _start
.global main
.global end_of_boot_copier
reset:
_start:
main:
// 清除CPU的状态寄存器禁止中断,这个动作在硬件复位的时候其实已经自动完成。.
wrctlstatus, r_zero
// 冲刷指令cache.
// Nios II 最多支持64Kbytes的指令cache,所以只初始化了64Kbytes的指令cache
movhir_flush_counter,%hi(0x10000)
cache_loop:
initir_flush_counter
// 没有必要初始化数据cache, bootloader不存取存贮器数据
addir_flush_counter, r_flush_counter,-32
bner_flush_counter, r_zero, cache_loop
// 冲刷流水线
flushp
// r_flash_ptr = find_payload();
// 调用查找数据负荷子程序寻找数据负荷
nextpcreturn_address_less_4
brFIND_PAYLOAD
// 拷贝.
//
// 在循环的开始,寄存器r_flash_ptr 包含“程序记录”的地址。
//
// 1) 读取“程序记录”的长度域(4-bytes)(r_data_size)
// 2) 读取“程序记录”的目的地址域(4-bytes)(r_dest)
// 3) 循环:
//拷贝 r_data_size 个字节, 一次一个字节: *r_dest++ = *r_flash_ptr++
// 把0xFFFFFFFF装入r_halt_record,用于测试是否要停机。
subir_halt_record, r_zero, 1
per_record_loop:
//读取“程序记录”的长度域,r_data_size = READ_INT(r_flash_ptr++)。
nextpcreturn_address_less_4
brREAD_INT
movr_data_size, r_read_int_return_value
// 读取“程序记录”的目的地址域,r_dest = READ_INT(r_flash_ptr++)。
nextpcreturn_address_less_4
brREAD_INT
movr_dest, r_read_int_return_value
// 测试长度域是否为0
// 如果是就直接运行程序
beqr_data_size, r_zero, last_program_record
// 如果长度域为-1(0xFFFFFFFF),就停机。
halt_record_forever:
beqr_data_size, r_halt_record, halt_record_forever
// 使用拷贝流子程序搬移数据
nextpcreturn_address_less_4
brSTREAMING_COPY
// 程序运行到这里,表明已经处理了当前的“程序记录”了,
// 而且知道这不是最后一个“程序记录”因为它的长度域不为0,
// 这就意味着要处理下一个“程序记录”。
brper_record_loop
last_program_record:
// 处理完最后一个程序记录后就要把控制权转给实际的运行程序.
// r_dest是实际程序的入口地址
// 在中止boot-loader之前要关闭EPCS器件,如果不做这一步,
// 会导致HAL的open()调用要花好几秒钟才能打开EPCS器件
#ifdef EPCS
nextpcreturn_address_less_4
brCLOSE_DEVICE
#endif
// 跳转到目的地址运行程序
callrr_dest
afterlife:// 程序跑到这里表明有问题。
brafterlife
.end