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