📄 boot_loader.s
字号:
# flash_copier.s
#
# TPA: NOTE: You should re-name this file to "flash_copier.S" with a
# CAPITAL ESS. This tells GCC to run the C pre-processor
# on it.
#
#
# (C) 2003 Altera Coroporation, all rights reserved.
#
# This program is a boot-loader, designed to start a Nios II system
# from flash. It copies a payload into RAM (presumably a program),
# then jumps-into the copied code.
#
# This is not a very complex job, but there is one requirement
# that makes this program tricky:
#
# (*) We want to make the exact-same binary code run on any system,
# at any (cache-line-aligned) address, and still work properly.
#
# Thus, this program must be position-independent, use no
# memory-variables, and derive all payload-specific data from the
# payload itself.
#
# This same program really has two lives:
# 1) A CFI flash-copier.
# 2) An EPCS flash-copier.
#
# A CFI flash-copier is pretty easy, because reading data from CFI
# flash is as simple as a "ldbuio" instruction. But reading data
# from EPCS flash is a big deal--because it's serial. We have to
# reproduce a subset of the EPCS access-routines right here in
# position-independent assembly-language, whilst using no memory
# variables. And, indeed, we have done just that.
#
# In either case, the payload is identical (the same ordered
# sequence of bytes), and is interpreted by the boot-copier in
# the same way. The only two differences between CFI and
# EPCS are:
#
# 1) The method used to READ bytes from flash.
# 2) The method used to FIND the first byte in the payload.
#
# You control which kind of boot-copier you get using the
# pre-processor symbol "EPCS".
#
# **** WHAT IT DOES
#
# EPCS or otherwise, this program executes a simple set of
# unconditional actions. It copies its "payload" from flash into
# RAM, then jumps to it. Presumably, the payload contains a program
# and its initialized data. The payload is created by a
# utility at software-compile time (elf2flash).
#
# The elf2flash utility and this program have to agree on the
# location and structure of the payload, or else this will be a short trip.
# The *payload* is a sequence of bytes stored in flash-memory:
#
# * For CFI flash, the first byte of the payload immediately
# follows the last instruction in this boot-copier. Thus,
# the first byte of the payload is at the label
# "end_of_boot_copier:"
#
# * For EPCS flash, the first byte of the payload follows the last
# byte of the device-configuration. This is obnoxious, because
# the device configuration is of indeterminate length. The only
# way to know where the device-configuration ends is to
# read the configuration-header and extract the length. This
# boot-copier does this to find the payload. The implicit
# assumption is that there is, in fact, Cyclone device
# configuration data stored in the EPCS flash, starting
# at offset zero.
# |
# | dvb2004: Each "program record" looks like so:
# | 4 bytes Length L
# | 4 bytes Address A
# |
# | Generally, the next L bytes are then shoveled out
# | of flash (cfi or epcs) and stashed at authentic
# | RAM address A.
# |
# | If L is zero, then A is an address to JUMP to for your
# | application to execute.
# |
# | If L is 0xffffFFFF, then we ignore A and halt. This lets
# | an erased flash memory behave safely, and allows an EPCS
# | to contain a sof file but no program (we'll write the four
# | bytes of F when we program the sof).
# |
#
#
#
################################
#
# Conditionally-define these two "funcion" names. One way, you'll
# get a simple CFI boot-loader. The other way, you'll get a complex
# EPCS boot-loader
#ifdef EPCS
#define FIND_PAYLOAD sub_find_payload_epcs
#define READ_BYTE sub_read_byte_from_flash_epcs
#else
#define FIND_PAYLOAD sub_find_payload_cfi
#define READ_BYTE sub_read_byte_from_flash_cfi
#endif
#include "boot_loader.h"
# |
# | all aliases of things the linker or the compiler
# | or Tim Allen might expect to find at offset zero
# | of some code. --dvb
# |
.global reset
.global _start
.global main
.global end_of_boot_copier
reset:
_start:
main:
# Clear the CPU's status-register, thereby disabling interrupts.
# This is redundant after a "real" hardware-reset operation, but
# people who deliberately jump-to-reset may derive some benefit from
# this. And, if not, at least it doesn't hurt anyone.
#
wrctl status, r_zero
########
# The first thing we want to do is flush the instruction cache.
#
# In Nios II version 1.0, the maximum allowed cache-size is 64KBytes.
#
# NOTE: If Nios II ever supports more than 64KByte caches,
# someone will need to change this code.
#
# TPA: "r_asm_tmp" is probably a bad choice.
movhi r_flush_counter,%hi(0x10000)
cache_loop:
initi r_flush_counter
# don't flush the data cache, the boot copier doesn't access data.
addi r_flush_counter, r_flush_counter,-32
bne r_flush_counter, r_zero, cache_loop
# then flush the pipeline
flushp
# r_flash_ptr = find_payload();
nextpc return_address_less_4
br FIND_PAYLOAD
########
# Copy-Job.
#
# At the start of the loop, r_flash_ptr contains the address of the next
# Program-Record to process.
#
# 1) Read the length-word (4-bytes) of the Program-Record (r_data_size)
# (if r_zero, exit loop).
#
# 2) Read the destination-address of this record (r_dest)
#
# 3) Inner-loop:
# Copy r_data_size bytes, one byte at a time: *r_data_size++ = *r_asm_tmp++
#
per_record_loop:
# r_data_size = read_int_from_flash(r_flash_ptr++)
nextpc return_address_less_4
br sub_read_int_from_flash
mov r_data_size, rf_int_return_value
# r_dest = read_int_from_flash (r_flash_ptr++)
nextpc return_address_less_4
br sub_read_int_from_flash
mov r_dest, rf_int_return_value
####
# Test to see if r_data_size (r_data_size) is r_zero.
# If so, we go run the program.
#
beq r_data_size, r_zero, last_program_record
# ------------------------------------------
# | A record length of 0xffffFFFF is
# | is a HALT record. Here, we skimp
# | and merely check for a negative
# | record length. Someday you'll need
# | to load a 2 gig or greater
# | record from flash to RAM, and
# | you may curse my initials: dvb, 2004
# |
halt_record_forever:
blt r_data_size,r_zero,halt_record_forever
# |
# ------------------------------------------
copy_loop:
# *r_dest++ = read_byte_from_flash ((char*)r_flash_ptr++)
#
nextpc return_address_less_4
br READ_BYTE
stbio rf_byte_return_value, 0(r_dest)
addi r_dest, r_dest, 1
addi r_data_size, r_data_size, -1 # (down-counter)
bne r_data_size, r_zero, copy_loop # Keep looping if counter is not zero.
# If you got to here, you're done with the current record.
# And, you know that it wasn't the last one (because it's
# length-field wasn't zero--we checked. So, that can only mean
# one thing. Time for the next record:
br per_record_loop
last_program_record:
# The last Program-Record is the jump-record. The
# r_dest is the entry-point of the
# program. This is easy as cheese.
#
# People seem to like to "return" from their main-program, and then
# they expet someting reasonable to happen. Weird.
callr r_dest
afterlife: # So...this is where programs go when they die.
br afterlife
########
# Read_Int_From_Flash
#
# Pseudo-subroutine which reads four bytes from flash and concatenates
# them into an integer. The four bytes start at a
# not-necessarily-aligned flash offset.
#
# Register usage:
# argument: r_flash_ptr (post-incremented by 4)
# return-vale: rf_int_return_value
#
# rfi_return_address -- temporary
#
sub_read_int_from_flash:
# Fix-up and stash return address
addi rfi_return_address, return_address_less_4, 4
# rfi_int_return_value = read_byte_from_flash ((char*)r_flash_ptr++)
#
nextpc return_address_less_4
br READ_BYTE
mov rf_int_return_value, rf_byte_return_value
# rfi_int_return_value |= (read_byte_from_flash ((char*)r_flash_ptr++)) << 8
#
nextpc return_address_less_4
br READ_BYTE
slli rf_byte_return_value, rf_byte_return_value, 8
or rf_int_return_value, rf_int_return_value, rf_byte_return_value
# rfi_int_return_value |= (read_byte_from_flash ((char*)r_flash_ptr++)) << 16
#
nextpc return_address_less_4
br READ_BYTE
slli rf_byte_return_value, rf_byte_return_value, 16
or rf_int_return_value, rf_int_return_value, rf_byte_return_value
# rfi_int_return_value |= (read_byte_from_flash ((char*)r_flash_ptr++)) << 24
#
nextpc return_address_less_4
br READ_BYTE
slli rf_byte_return_value, rf_byte_return_value, 24
or rf_int_return_value, rf_int_return_value, rf_byte_return_value
# Return.
jmp rfi_return_address
.end
# end of file
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -