Nios II的Boot过程分析
2012-12-10
标签:

5.2 boot_loader_epcs_bits.s解读

// 从EPCS串行flash设备读取字节的子过程

// 通过寄存器和EPCS打交道获取字节数

#include "boot_loader.h"

.global sub_find_payload_epcs

.global sub_read_int_from_flash_epcs

.global sub_streaming_copy_epcs

.global sub_epcs_close

// EPCS控制和状态寄存器的偏移量

#define EPCS_RXDATA_OFFSET0x00

#define EPCS_TXDATA_OFFSET0x04

#define EPCS_STATUS_OFFSET0x08

#define EPCS_CONTROL_OFFSET 0x0C

// EPCS的位掩码

#define EPCS_STATUS_TMT_MASK0x20

#define EPCS_STATUS_TRDY_MASK 0x40

#define EPCS_STATUS_RRDY_MASK 0x80

#define EPCS_CONTROL_SSO_MASK 0x400

// EPCS命令

#define EPCS_COMMAND_READ 0x03

.text

//

// 查找EPCS的数据负荷

//

// 过程:

//- 在偏移量为0的地方打开EPCS器件(FPGA配置数据在这里)

//- 分析配置数据获取数据负荷开始的地址

//- 关闭EPCS

//- 在数据负荷的开始的地址再次打开EPCS

//

sub_find_payload_epcs:

// 修正并存贮返回地址

addir_findp_return_address, return_address_less_4, 4

//

// 计算EPCS控制/状态寄存器块的地址

// 它在离本段代码的开头偏移量为512个字节的地方

// 因为这段代码必须在512字节边界处,

// 我们简单地把当前地址园整到下一个512个地址的边界。

//

// |

// | 为了调试,你可以定义EPCS_REGS_BASE

// | 作为EPCS寄存器基地址。否则就假设下一个512字节边界。

// |

nextpcr_findp_temp

#ifdef EPCS_REGS_BASE

movhir_epcs_base_address, %hi(EPCS_REGS_BASE)

addir_epcs_base_address, r_epcs_base_address, %lo(EPCS_REGS_BASE)

#else

orir_epcs_base_address, r_findp_temp, 511

addir_epcs_base_address, r_epcs_base_address, 1

#endif

//

// 在偏移量为0的地方打开EPCS器件

//

movir_flash_ptr, 0

nextpcreturn_address_less_4

brsub_epcs_open_address

//

// 分析器件配置数据顺序读出字节直到下面任一个条件满足

//1) 我们找到0xA6 (其实应该是0x56,因为我们没有把位序颠倒过来)

//当我们找到它时表示我们找到配置数据,可以接着算出它的长度。

//2) 我们找到不是xFF字节,在这种情况我们根本没有在配置数据里查找

//我们假定我一定是在一个boot loader记录。跳过整个配置数据长度的计算

//开始装载。

//3) 我们在任意长的时间内找到的都是0xFF。我们猜测flash是空的没有其它可利用资源

//

// 搜索随意的一大块字节

movir_findp_count, 0x400

// 我们要找的模板是0x56

movir_findp_pattern, 0x56

// 在我们找到0x56之前唯一可以接受的字节是0xFF

movir_findp_temp, 0xFF

fp_look_for_56_loop:

nextpcreturn_address_less_4

brsub_read_byte_from_flash_epcs

// 我们发现模板了吗?

beqr_read_byte_return_value, r_findp_pattern, fp_found_sync

// 我们发现非0xFF的字节了吗?

bner_read_byte_return_value, r_findp_temp, fp_short_circuit

// 更新循环计数器开始循环

subir_findp_count, r_findp_count, 1

bner_findp_count, r_zero, fp_look_for_56_loop

// 我们没有找到模板或其它匹配的字节,挂起。

// 先关闭EPCS器件

nextpcreturn_address_less_4

brsub_epcs_close

fp_hang:

brfp_hang

fp_found_sync:

// 同步模板后面紧跟着的4个字节是我们感兴趣

nextpcreturn_address_less_4

brsub_read_int_from_flash_epcs

// 这4个字节是配置的长度,它们的字节顺序是little-endian,但位序是反的。

nextpcreturn_address_less_4

brsub_read_int_from_flash_epcs

// 把长度放到r_flash_ptr 中

movr_flash_ptr, r_read_int_return_value

// 此时我们获得了长度但是在EPCS器件中Quarts

// 以相反的位序存贮字节

//

//我们先把4位组反过来,再把2位组反过来,然后再把所有的位反过来。

//就象这样:

//

//76543210 – 4位组反序--> 32107654 – 两位组反序 --> 10325476 – 位反序 --> 01234567

//

//下面是整个循环的进行机制

//你会注意到这个反序过程只展示了一次

//不用担心,所有的字节都会被反序

//

//("x" == unknown, "." == zero)

//

//bytetempmaskcount

//-----------------------------

//初始态76543210xxxxxxxx000011114

//

// 1 temp = byte & mask76543210....3210000011114

// 2 temp <<= count765432103210....000011114

// 3 byte >>= countxxxx76543210....000011114

// 4 byte &= mask....76543210....000011114

// 5 byte |= temp321076543210....000011114

// 6 count >>= 1321076543210....000011112

// 7 temp = mask << count3210765400111100000011112

// 8 mask ^= temp3210765400111100001100112

//

//loop on (count != 0)

//

//temp = byte & mask32107654..10..54001100112

//temp <<= count3210765410..54..001100112

//byte >>= countxx32107610..54..001100112

//byte &= mask..32..7610..54..001100112

//byte |= temp1032547610..54..001100112

//count >>= 11032547610..54..001100111

//temp = mask << count1032547601100110001100111

//mask ^= temp1032547601100110010101011

//

//loop on (count != 0)

//

//temp = byte & mask10325476.0.2.4.6010101011

//temp <<= count103254760.2.4.6.010101011

//byte >>= countx10325470.2.4.6.010101011

//byte &= mask.1.3.5.70.2.4.6.010101011

//byte |= temp012345670.2.4.6.010101011

//count >>= 1012345670.2.4.6.010101010

//temp = mask << count0123456701010101010101010

//mask ^= temp0123456701010101000000000

//

// 初始化mask

movhir_revbyte_mask, 0x0F0F

addir_revbyte_mask, r_revbyte_mask, 0x0F0F

// 装入count

movir_findp_count, 4

fp_reverse_loop:

// 屏蔽高一半的位把结果装入TEMP寄存器

andr_findp_temp, r_flash_ptr, r_revbyte_mask// 1

// 把TEMP中的位左移4位

sllr_findp_temp, r_findp_temp, r_findp_count// 2

// 把PTR中字节右移4位

srlr_flash_ptr, r_flash_ptr, r_findp_count// 3

// 屏蔽掉高4位

andr_flash_ptr, r_flash_ptr, r_revbyte_mask// 4

// 把PTR和TEMP中的位组合起来

orr_flash_ptr, r_flash_ptr, r_findp_temp// 5

// 更新移位计数器

srlir_findp_count, r_findp_count, 1// 6

// 左移MASK 2位

sllr_findp_temp, r_revbyte_mask, r_findp_count// 7

// 更新MASK

xorr_revbyte_mask, r_revbyte_mask, r_findp_temp// 8

// 循环直到移位计数器为0

bner_findp_count, r_zero, fp_reverse_loop

//

// 这个长度是以位为单位的长度,把它圆整到以字节为单位的长度。

//

addir_flash_ptr, r_flash_ptr, 7// r_flash_ptr += 7

srlir_flash_ptr, r_flash_ptr, 3// r_flash_ptr /= 8;

fp_short_circuit:

// 关闭EPCS器件

nextpcreturn_address_less_4

brsub_epcs_close

// 重新打开EPCS器件(at r_flash_ptr)

nextpcreturn_address_less_4

brsub_epcs_open_address

jmpr_findp_return_address

////////

// EPCS_Open_Address

//

// 打开EPCS器件以便于我们读取给定地址开始的字节流

// 地址在r_flash_ptr给出

//

// 这只是一个sub_tx_rx_int_epcs 子过程的头部

// 没有必要修正返回地址,相反它直接跳转到sub_tx_rx_int_epcs

// 然后让子过程返回到原来的调用者那里。

//

//寄存器用法:

//参数:r_flash_ptr

//临时寄存器: r_eopen_eclose_tmp

//返回值:--none--

//

sub_epcs_open_address:

// 不需要修正返回地址,这只是一个子过程的头部

// 通过控制寄存器使能EPCS器件的片选

movir_eopen_eclose_tmp, EPCS_CONTROL_SSO_MASK

stwior_eopen_eclose_tmp, EPCS_CONTROL_OFFSET (r_epcs_base_address)

// 把读命令送入既定的寄存器中

movhir_epcs_tx_value, (EPCS_COMMAND_READ << 8)

// 把flash指针送入低24位中

orr_epcs_tx_value, r_epcs_tx_value, r_flash_ptr

// 跳转到sub_tx_rx_int 子过程

brsub_tx_rx_int_epcs

// 现在EPCS器件已经在r_flash_ptr处打开

////////

// 关闭EPCS

//

// 终止当前的EPCS事务

//

sub_epcs_close:

// 修正返回地址

addireturn_address_less_4, return_address_less_4, 4

// 等待控制器说发送器空

close_ready_loop:

ldwior_eopen_eclose_tmp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andir_eopen_eclose_tmp, r_eopen_eclose_tmp, EPCS_STATUS_TMT_MASK

beqr_eopen_eclose_tmp, r_zero, close_ready_loop

// 清除SSO位释放CS

stwior_zero, EPCS_CONTROL_OFFSET (r_epcs_base_address)

// 返回

jmpreturn_address_less_4// 我们已经修复了返回地址

////////

// sub_read_int_from_flash_epcs

//

// epcs_rx_tx的另外一个入口

//

// 在进入sub_tx_rx_int_epcs先把epcs_tx_value清0

//

sub_read_int_from_flash_epcs:

// 这个子过程读取EPCS器件的下一个32位word,

// 假设一个有效的读命令和地址已经发出去,片选也是使能的

// 给发送的内容清0。

//

movr_epcs_tx_value, r_zero

//

// 进入sub_tx_rx_int_epcs子过程

//

////////

// sub_tx_rx_int_epcs

//

//这个子过程往flash写4个字节同时也读回4个字节

//这4个字节没有什么地址对齐的限制

//这个子过程写的时候是高位在先,读的时候是低位在先

//因为EPCS处理命令的时候是高位在先,但是SOF文件的

//编码却是低位在先

//

//这个子过程和tx_rx_byte共享输入参数

//只要tx_rx_byte 不破坏它的输入参数,

//那这么做就是安全的。

//

//寄存器用法:

//入口参数:r_epcs_tx_value

//局部变量:r_trie_count

//局部返回指针:r_riff_return_address

//返回的值:r_read_int_return_value

//

sub_tx_rx_int_epcs:

// 修正返回地址

addir_riff_return_address, return_address_less_4, 4

//

// 写(高位在先)然后读(低位在先)

//

// 清楚返回的值

movr_read_int_return_value, r_zero

// 发送/接收的字节数

movir_trie_count, 4

trie_loop:

// 定位发送字节,使符合参数格式要求

rolir_epcs_tx_value, r_epcs_tx_value, 8

// 发送/接收一个字节

nextpcreturn_address_less_4

brsub_tx_rx_byte_epcs

// 把它反在结果寄存器的低位字节

orr_read_int_return_value, r_read_int_return_value, r_read_byte_return_value

// 循环移位结果寄存器以便于最后一个字节在高位字节

//把其它字节移到低位字节

rolir_read_int_return_value, r_read_int_return_value, 24

// 计数器减1,继续循环。

subir_trie_count, r_trie_count, 1

bner_trie_count, r_zero, trie_loop

// 返回

jmpr_riff_return_address

////////

// sub_read_byte_from_flash_epcs

//

// epcs_rx_tx.的另一个入口

//

//在进入epcs_tx_rx 之前把epcs_tx_value清0

//

sub_read_byte_from_flash_epcs:

// 该过程读取EPCS器件的下一个字节,

// 假设一个读命令和地址已经发送,片选也已经使能。

//

// 只要发送0给器件,我们就能收到下一个字节。

//

movr_epcs_tx_value, r_zero

//

// 进入sub_tx_rx_byte_epcs子过程

//

////////

// sub_tx_rx_byte_epcs

//

// EPCS器件很有趣,每次你发送一些东西,同时也会收到东西。

// 每次你想收到东西,你就必须发送一些东西。

// 这个子过程把它的入口参数内容发送给EPCS, and returns whatever was

// 然后返回它从EPCS获取的值。

//

// 寄存器用法:

//输入参数:r_epcs_tx_value

//临时寄存器:rf_temp

//返回值:r_read_byte_return_value

//

sub_tx_rx_byte_epcs:

// 修正返回地址Fix-up return-address(NOTE: LEAF)

addireturn_address_less_4, return_address_less_4, 4

// 等待控制器准备好接收TX字节,然后发送它。

tx_ready_loop:

ldwiorf_temp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andirf_temp, rf_temp, EPCS_STATUS_TRDY_MASK

beqrf_temp, r_zero, tx_ready_loop

stwior_epcs_tx_value, EPCS_TXDATA_OFFSET (r_epcs_base_address)

// 等待从EPCS接收的字节有效,然后获取它。

rx_ready_loop:

ldwiorf_temp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andirf_temp, rf_temp, EPCS_STATUS_RRDY_MASK

beqrf_temp, r_zero, rx_ready_loop

ldbuior_read_byte_return_value, EPCS_RXDATA_OFFSET (r_epcs_base_address)

// 返回

jmpreturn_address_less_4// 返回地址已被修正

////////

// 流拷贝

//

//拷贝r_data_size字节,从r_flash_ptr到r_dest。

//

//寄存器用法:

//参数:r_data_size – 要拷贝的字节数

//参数:r_dest- 拷贝的目的地址

//隐含条件:r_flash_ptr – 拷贝的源地址

//临时寄存器: rf_temp

//返回值:无

//

//所有参数在子过程中都会被破坏

//

//Note: we don't keep the flash ptr up to date.Instead

//we just keep streaming from the EPCS device

//

sub_streaming_copy_epcs:

// 修正返回地址(NOTE: LEAF)

addireturn_address_less_4, return_address_less_4, 4

// 为了更好的可读性,给r_data_size再定义一个别名

#define r_dest_end r_data_size

// 通过长度计算结束地址

addr_dest_end, r_data_size, r_dest

subir_dest_end, r_dest_end, 1

// 等待EPCS控制器准备好接收TX字节

epcs_copy_initial_wait:

ldwiorf_temp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andirf_temp, rf_temp, EPCS_STATUS_TRDY_MASK

beqrf_temp, r_zero, epcs_copy_initial_wait

// 给EPCS送0

stwior_zero, EPCS_TXDATA_OFFSET (r_epcs_base_address)

//

// do {

//*r_dest++ = (char*)r_flash_ptr++)

// while (r_dest <= r_dest_end);

//

epcs_copy_loop:

// 等待读取的字节有效

ldwiorf_temp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andirf_temp, rf_temp, EPCS_STATUS_RRDY_MASK

beqrf_temp, r_zero, epcs_copy_loop

// 读取EPCS的一个字节,并立即要求下一个字节

// 不必等待TX准备好,如果RX准备好了TX也一样。

ldwiorf_temp, EPCS_RXDATA_OFFSET (r_epcs_base_address)

stwior_zero, EPCS_TXDATA_OFFSET (r_epcs_base_address)

// 存贮读到的字节,并更新目的地址指针

stbiorf_temp, 0(r_dest)

addir_dest, r_dest, 1

// 循环直到目的地址指针指向结束地址

bner_dest, r_dest_end, epcs_copy_loop

epcs_copy_last_wait:

// 等待最后读取的字节有效

ldwiorf_temp, EPCS_STATUS_OFFSET (r_epcs_base_address)

andirf_temp, rf_temp, EPCS_STATUS_RRDY_MASK

beqrf_temp, r_zero, epcs_copy_last_wait

// 读取最后一个字节

ldwiorf_temp, EPCS_RXDATA_OFFSET (r_epcs_base_address)

// 存贮最后一个字节

stbiorf_temp, 0(r_dest)

// 返回

jmpreturn_address_less_4// Don't worry--we fixed it.

// 文件结束

5.3 boot_loader_cfi_bits.s解读

#include "boot_loader.h"

.global sub_find_payload_cfi// 查找数据负荷的子程序

.global sub_read_int_from_flash_cfi// 从CFI并行flash中读取32位word的子程序

.global sub_streaming_copy_cfi// 从CFI并行flash中拷贝流的子程序

////////

// Read_Int_From_Flash_CFI

//

//伪子程序,它从flash中读取4个字节并把它们拼起来形成一个整数

//这4个字节没有地址对齐的要求

//寄存器用法:

//内部变量:r_riff_count

//内部指针:r_riff_return_address

//返回值:r_read_int_return_value

//

sub_read_int_from_flash_cfi:

// 修正中断返回地址,即在返回地址寄存器上加4

addir_riff_return_address, return_address_less_4, 4

//

// 读取字节然后把它们移进返回寄存器中

//

// 先对返回寄存器清0

movr_read_int_return_value, r_zero

// 返回的字节数

movir_riff_count, 4

riffc_loop:

// 返回一个字节并泵进一下r_flash_ptr

ldbuior_read_byte_return_value, 0(r_flash_ptr)

addir_flash_ptr, r_flash_ptr, 1

// 把它以逻辑或运算的方式送入结果寄存器的低位字节

orr_read_int_return_value, r_read_int_return_value, r_read_byte_return_value

// 循环左移结果寄存器使最后一个字节在高位字节,

// 把其它字节移到低位字节

rolir_read_int_return_value, r_read_int_return_value, 24

// 计数器减1并循环

subir_riff_count, r_riff_count, 1

bner_riff_count, r_zero, riffc_loop

// 返回.

jmpr_riff_return_address

////////

// 流拷贝

//

//拷贝 r_data_size 字节从r_flash_ptr 到 r_dest

//

//寄存器用法:

//参数:r_data_size 要拷贝的字节数

//参数:r_dest拷贝的目的地址

//隐含的寄存器参数:r_flash_ptr拷贝的源地址

//临时寄存器:rf_temp

//返回值: 无

//

//所有的参数寄存器都会在这个子过程中被破坏

//

sub_streaming_copy_cfi:

// 修正返回地址(NOTE: LEAF)

addireturn_address_less_4, return_address_less_4, 4

// 为更好的可读性,给同一个寄存器定义了两个别名。

#define r_dest_end_plus_one r_data_size

// 把长度转化成结束地址加1

addr_dest_end_plus_one, r_data_size, r_dest

//

// do {

//*r_dest++ = (char*)r_flash_ptr++)

// while (r_dest != r_dest_end_plus_one);

//

cfi_copy_loop:

ldbuiorf_temp, 0(r_flash_ptr)

addir_flash_ptr, r_flash_ptr, 1

stbiorf_temp, 0(r_dest)

addir_dest, r_dest, 1

// 循环直到目的地址destination == 1 + 结束地址

bner_dest, r_dest_end_plus_one, cfi_copy_loop

// Return

jmpreturn_address_less_4// 不用担心,我们已经修正了它的值。.

////////

// 查找数据负荷

//把数据负荷的第一个字节的偏移量送到r_flash_ptr返回。

// CFI:

//数据负荷紧挨着boot-copier的后面存放,使用一些nextpc 这些位置无关

//的指令来查找。

sub_find_payload_cfi:

// 修正并存贮返回地址

addir_findp_return_address, return_address_less_4, 4

nextpcr_flash_ptr

payload_offset_base:

addir_flash_ptr, r_flash_ptr, (end_of_boot_copier - payload_offset_base)

// 找到数据负荷r_flash_ptr现在包含有数据负荷的地址。

jmpr_findp_return_address

//

// 对于一个基于flash的启动代码,我们把它放在

// |reset地址,然后把数据紧挨着它存放,end_of_boot_copier

// 就是数据负荷的地址。

end_of_boot_copier:

// 数据在这里。

.end

共 5 页   上一页12345下一页
可能会用到的工具/仪表
相关文章
推荐文章
热门文章
章节目录
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号