1.riscv基本的寄存器列表
这里只关注32个通用寄存器x0-x31
2.引导程序代码
# 1 "iriscvboot.casm"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "iriscvboot.casm"
###define 0x52080000 0
# 29 "iriscvboot.casm"
.section .text
.org 0x0
_th0_reset:
l.j _th0_main
l.nop
_LOOP:
l.j _LOOP
## === version info start at 0x80 ==
.org 0x80
.global _pack_version
.global _build_date
.global _api_version
_pack_version:
.word 0x0171
_svn_version:
.word 0x0367ad3
_build_date:
.word 0x07e60701
_api_version:
.word 0x0180
_silicon_version:
.word 1000
## === version info end ===
###################################################################
_th0_main:
l.addi x2,x0,0x0
l.addi x3,x0,0x0
l.addi x4,x0,0x0
l.addi x5,x0,0x0
l.addi x6,x0,0x0
l.addi x7,x0,0x0
l.addi x8,x0,0x0
l.addi x9,x0,0x0
l.addi x10,x0,0x0
l.addi x11,x0,0x0
l.addi x12,x0,0x0
l.addi x13,x0,0x0
l.addi x14,x0,0x0
l.addi x15,x0,0x0
l.addi x16,x0,0x0
l.addi x17,x0,0x0
l.addi x18,x0,0x0
l.addi x19,x0,0x0
l.addi x20,x0,0x0
l.addi x21,x0,0x0
l.addi x22,x0,0x0
l.addi x23,x0,0x0
l.addi x24,x0,0x0
l.addi x25,x0,0x0
l.addi x26,x0,0x0
l.addi x27,x0,0x0
l.addi x28,x0,0x0
l.addi x29,x0,0x0
l.addi x30,x0,0x0
l.addi x31,x0,0x0
l.lui x2,0x52086
_init_cmem_done_th0:
l.jal _mymain
l.j _LOOP
3.引导代码含义,及相关指令。
.section .text表示下面的代码链接时放在.text段,.org设置当前代码的地址偏移(相对于链接时指定定.text段的起始地址)。
接下来定义了标签_th0_reset,riscv执行时从这儿开始执行指令,l.j _th0_main表示跳转到
_th0_main标签处,
_th0_reset:
l.j _th0_main
l.nop
_th0_main标签处,连续的l.addi 指令可以理解为对32个通用寄存器的清操作。
但是注意 l.lui x2,0x52086指令指定了sp寄存器(栈指针)的,开始地址。(lui 指令将 20 位立即数的值左移 12 位(低 12 位补 0)成为一个 32 位数,将此数写 回寄存器 x2 中。)
接着执行_init_cmem_done_th0标签处。此处就是我们定义的c程序的入口处。l.jal _mymain表示跳转到_mymain处,可以看下面c代码的汇编,其中会根据mymain函数创建_mymain标签。(jal 指令完成跳转并且将其下一条指令的 PC(即当前指令 PC+4)的值写入其结果寄存器 ra 中。注意:在实际的汇编程序编写中,跳转的目标往往使用汇编程序中的 label,汇编器 会自动根据 label 所在的地址计算出相对的偏移量赋予指令编码。)
到_mymain中后就会执行c代码中内容了。 执行完c代码后,会根据ra寄存器的值返回来执行下一句,l.j _LOOP。这里是为了让riscv不乱跑,设置这个循环。
4.c代码
C代码内容很简单,分别对0x1000这个地址写入次,第一次值100,第二次值0x202
void write32(int addr,int value)
{
int *addr_value;
addr_value = (int*)addr;
*addr_value = value;
}
void mymain()
{
volatile int age=100;
write32(0x1000,age);
write32(0x1000,0x202);
}
C代码的汇编结果gcc -S:
.file "firmware.c"
.option nopic
.text
.align 2
.globl _write32
.type _write32, @function
_write32:
l.sw a1,0(a0)
l.ret
.size _write32, .-_write32
.align 2
.globl _mymain
.type _mymain, @function
_mymain:
l.add sp,sp,-32
l.li a5,100
l.sw a5,12(sp)
l.lw a1,12(sp)
l.li a0,4096
l.sw ra,28(sp)
call _write32
l.li a1,514
l.li a0,4096
call _write32
l.lw ra,28(sp)
l.add sp,sp,32
l.jr ra
.size _mymain, .-_mymain
.ident "GCC: (GNU) 6.1.0"
5.链接
链接脚本
MEMORY
{
flash : ORIGIN = 0x52040000, LENGTH = 0x00040000
ram : ORIGIN = 0x5b000000, LENGTH = 0x00006000
glb : ORIGIN = 0x52086000, LENGTH = 0x00032000
rodata : ORIGIN = 0xa0000000, LENGTH = 0x00040000
}
SECTIONS
{
.text :
{
*(.text)
} > flash
.data :
{
*(.data)
} > ram
.bss :
{
*(.bss)
} > ram
.debug :
{
*(.debug_frame .zdebug_frame)
*(.debug_abbrev)
*(.debug_line)
*(.debug_str)
*(.debug_loc)
*(.debug_macinfo)
*(.debug_info)
*(.debug_aranges)
*(.debug_ranges)
*(.comment)
} > ram
.rodata :
{
*(.rodata)
} > rodata
}
链接结果,通过objdump工具进行反汇编注意使用-D选项,对所有段进行反汇编:
isp_firmware: file format elf32-littleriscv
Disassembly of section .text:
52040000 <_th0_reset>:
52040000: 0940006f l.jal zero,52040094 <_th0_main>
52040004: 00000013 l.addi zero,zero,0
52040008 <_LOOP>:
52040008: 0000006f l.jal zero,52040008 <_LOOP>
...
52040080 <_pack_version>:
52040080: 00000171 0x171
52040084 <_svn_version>:
52040084: 00367ad3 l.fadd.s fs5,fa2,ft3
52040088 <_build_date>:
52040088: 07e60701 0x7e60701
5204008c <_api_version>:
5204008c: 00000180 0x180
52040090 <_silicon_version>:
52040090: 000003e8 0x3e8
52040094 <_th0_main>:
52040094: 00000113 l.addi sp,zero,0
52040098: 00000193 l.addi gp,zero,0
5204009c: 00000213 l.addi tp,zero,0
520400a0: 00000293 l.addi t0,zero,0
520400a4: 00000313 l.addi t1,zero,0
520400a8: 00000393 l.addi t2,zero,0
520400ac: 00000413 l.addi s0,zero,0
520400b0: 00000493 l.addi s1,zero,0
520400b4: 00000513 l.addi a0,zero,0
520400b8: 00000593 l.addi a1,zero,0
520400bc: 00000613 l.addi a2,zero,0
520400c0: 00000693 l.addi a3,zero,0
520400c4: 00000713 l.addi a4,zero,0
520400c8: 00000793 l.addi a5,zero,0
520400cc: 00000813 l.addi a6,zero,0
520400d0: 00000893 l.addi a7,zero,0
520400d4: 00000913 l.addi s2,zero,0
520400d8: 00000993 l.addi s3,zero,0
520400dc: 00000a13 l.addi s4,zero,0
520400e0: 00000a93 l.addi s5,zero,0
520400e4: 00000b13 l.addi s6,zero,0
520400e8: 00000b93 l.addi s7,zero,0
520400ec: 00000c13 l.addi s8,zero,0
520400f0: 00000c93 l.addi s9,zero,0
520400f4: 00000d13 l.addi s10,zero,0
520400f8: 00000d93 l.addi s11,zero,0
520400fc: 00000e13 l.addi t3,zero,0
52040100: 00000e93 l.addi t4,zero,0
52040104: 00000f13 l.addi t5,zero,0
52040108: 00000f93 l.addi t6,zero,0
5204010c: 52086137 l.lui sp,0x52086
52040110 <_init_cmem_done_th0>:
52040110: 010000ef l.jal ra,52040120 <_mymain>
52040114: ef5ff06f l.jal zero,52040008 <_LOOP>
52040118 <_write32>:
52040118: 00b52023 l.sw a1,0(a0)
5204011c: 00008067 l.jalr zero,0(ra)
52040120 <_mymain>:
52040120: fe010113 l.addi sp,sp,-32 # 52085fe0 <_mymain+0x45ec0>
52040124: 06400793 l.addi a5,zero,100
52040128: 00f12623 l.sw a5,12(sp)
5204012c: 00c12583 l.lw a1,12(sp)
52040130: 00001537 l.lui a0,0x1
52040134: 00112e23 l.sw ra,28(sp)
52040138: fe1ff0ef l.jal ra,52040118 <_write32>
5204013c: 20200593 l.addi a1,zero,514
52040140: 00001537 l.lui a0,0x1
52040144: fd5ff0ef l.jal ra,52040118 <_write32>
52040148: 01c12083 l.lw ra,28(sp)
5204014c: 02010113 l.addi sp,sp,32
52040150: 00008067 l.jalr zero,0(ra)
Disassembly of section .debug:
5b000000 <.debug>:
5b000000: 3a434347 l.fmsub.d ft6,ft6,ft4,ft7,rmm
5b000004: 4e472820 0x4e472820
5b000008: 36202955 l.c.setireg m18,m2,0x1b10
5b00000c: 302e312e 0x302e312e
...
6.生成firmware
然后使用python脚本将,对应需要的段的地址和指令编码dump出来。使用的python脚本如下,输入文件是上面的反汇编结果:
#!/usr/bin/python
import sys
import re
def replace_line(input_file, output_file):
input_f = open(input_file, 'r')
output_f = open(output_file, 'w')
text=0;
rodata=0;
sdata=0;
for line in input_f:
line = line.rstrip() # removing right space
#print(line)
if("text" in line):
text=1
sdata=0
rodata=0
if("sdata" in line):
sdata=1
text=0
rodata=0
if("rodata" in line):
rodata=1
text=0
sdata=0
if(("debug" in line)):
sdata=0;
rodata=0;
text=0;
if (line.find(' l.') != -1)&(text==1) :
line = line.replace (":", ",")
words = line.split()
output_f.write(words[0]+words[1]+'\n')
if (re.findall(r'\d+:\s+\w+',line) != [])&(sdata==1) :
line = line.replace (":", ",")
words = line.split()
output_f.write(words[0]+words[1]+'\n')
if (re.findall(r'\d+:\s+\w+',line) != [])&(rodata==1) :
line = line.replace (":", ",")
words = line.split()
output_f.write(words[0]+words[1]+'\n')
arg_list = sys.argv
print(arg_list[1])
print(arg_list[2])
if len(arg_list) != 3:
print("need input file list")
sys.exit()
replace_line(arg_list[1], arg_list[2])
dump出的firmware的文件如下,其中包含指令编码以及所放置的mem中的地址:
52040000,0940006f
52040004,00000013
52040008,0000006f
52040084,00367ad3
52040094,00000113
52040098,00000193
5204009c,00000213
520400a0,00000293
520400a4,00000313
520400a8,00000393
520400ac,00000413
520400b0,00000493
520400b4,00000513
520400b8,00000593
520400bc,00000613
520400c0,00000693
520400c4,00000713
520400c8,00000793
520400cc,00000813
520400d0,00000893
520400d4,00000913
520400d8,00000993
520400dc,00000a13
520400e0,00000a93
520400e4,00000b13
520400e8,00000b93
520400ec,00000c13
520400f0,00000c93
520400f4,00000d13
520400f8,00000d93
520400fc,00000e13
52040100,00000e93
52040104,00000f13
52040108,00000f93
5204010c,52086137
52040110,010000ef
52040114,ef5ff06f
52040118,00b52023
5204011c,00008067
52040120,fe010113
52040124,06400793
52040128,00f12623
5204012c,00c12583
52040130,00001537
52040134,00112e23
52040138,fe1ff0ef
5204013c,20200593
52040140,00001537
52040144,fd5ff0ef
52040148,01c12083
5204014c,02010113
52040150,00008067
7.仿真结果
将上面dump出来的结果,进行仿真。结果如下所示。可以看见和C代码内容一样分别对0x1000这个地址写入次,第一次值100(0x64),第二次值0x202
pc指针的初始值值设置的是52040000,也就是.text的初始地址。
参考:
1.RISC-V架构与嵌入式开发快速入门