RISC-V MCU中文社区

【分享】 蜂鸟例程的编译与程序的加载

发表于 开源蜂鸟E203 2023-05-18 20:29:43
0
906
1

蜂鸟例程的编译与程序的加载

队伍编号:CICC2136
队伍名称:芯如止水

1 例程的编译

1.1 汇编语言程序原理
对于汇编文件(.S),以rv32ui-p-add.S文件为例,编译与执行过程如下:

① 使用命令make dump,make dump命在定义文件夹下的Makefile中,依次执行了clean、elf、dump三个命令。编译过程使用了riscv的toolchain:

  • clean: 清除原有编译/执行文件等;
  • elf: clean,再将(.S)汇编文件编译成(.o)目标文件,目标文件包含着机器代码以及代码在运行时使用的数据;
  • dump: 先elf,再通过(.o)目标文件生成(.dump)反汇编文件,通过(.o)目标文件生成(.verilog)文件,修改(.verilog)内容,(.verilog)文件会在仿真时直接读取进ITCM来执行程序。

② 在vsim文件夹下依次使用命令make clean、make install、make run_test,所有命令都定义在vsim文件夹下的Makefile中:

  • clean: 删除了vsim目录下的run和install文件夹;
  • install: 创建目录vsim/install/tb,将tb_top.v和整个rtl文件夹复制过来,将tb_top.v 中的e200修改为e203;
  • run_test: 先创建目录vsim/run,创建软链接到vsim//bin/run.makefile;再make compile,编译run下的文件,编译过程同样使用了riscv的toolchain;最后make run,使用VCS平台执行TESTCASE对应的仿真程序。

1.2 高级语言程序
对于C语言文件(.c),以自定义的baremetal/demo_i2c文件夹下的demo_i2c.c文件为例,编译与执行过程如下:

① 使用命令make dasm PROGRAM=demo_i2c,将make dsam命令的定义在hbird_hdk/Build文件夹下的Makefile.rules中,实际上需要依次执行clean、elf、dasm三个命令;而在demo_i2c文件夹下的Makefile,定义了具体路径。编译过程同样使用了riscv的toolchain:

  • clean: 清除路径下原有编译/执行文件等;
  • elf: 将相应路径下的相应文件(.c)编译成(.o)目标执行文件;
  • dasm: 先elf,再通过(.o)目标文件生成(.dump)反汇编文件,通过(.o)目标文件生成 (.verilog)文件,修改(.verilog)内容。

② 在vsim文件夹下依次使用命令make clean、make install、make run_test,所有命令都定义在vsim文件夹下的Makefile中。这一过程与1.1节所述内容一致,只是需要修改TESTCASE为待执行程序对应路径。

2 程序的加载

在仿真的过程中,会首先将前文编译生成的(.verilog)文件通过tb_top.v文件里initial过程块中的readmemh函数读进来,存放在ITCM中:

     initial begin
       $readmemh({testcase, ".verilog"}, itcm_mem);

       for (i=0;i<(`E203_ITCM_RAM_DP);i=i+1) begin
           `ITCM.mem_r[i][00+7:00] = itcm_mem[i*8+0];
           `ITCM.mem_r[i][08+7:08] = itcm_mem[i*8+1];
           `ITCM.mem_r[i][16+7:16] = itcm_mem[i*8+2];
           `ITCM.mem_r[i][24+7:24] = itcm_mem[i*8+3];
           `ITCM.mem_r[i][32+7:32] = itcm_mem[i*8+4];
           `ITCM.mem_r[i][40+7:40] = itcm_mem[i*8+5];
           `ITCM.mem_r[i][48+7:48] = itcm_mem[i*8+6];
           `ITCM.mem_r[i][56+7:56] = itcm_mem[i*8+7];
       end

初始化后,ROM里的上电程序执行完成后会跳转到ITCM的地址(8000_0000),再开始执行ITCM里面的指令,即预先编写的软件代码。在这一过程中,PC值经历了0000_1000到0000_1004到8000_0000的变化过程。

以下是每一步的详细过程:
① PC initial:0000_1000
在e203_cpu_top模块中,定义了PC的初始化信号:

    // This signal can be used to indicate the PC value for the core after reset

  input  [`E203_PC_SIZE-1:0] pc_rtvec,

对信号进行trace-driver,可见在e203_subsys_top即整个系统的互联模块中定义了PC的初始值,也就是ROM的起始地址值:

      .pc_rtvec        (32'h0000_1000),

② PC:0000_1000 → 0000_1004
在e203_ifu_ifetch模块中进行PC生成,在顺序取指而非流水线冲刷或分支指令的情况下,对32位指令,下一条指令的PC值为当前PC值加4。

③ PC:0000_1004 → 8000_0000
在sirv_mrom模块中,定义了jump_to_ram_gen模块。其含义是PC进入ROM,首先执行auipc t0, 0x7fff指令,该指令将立即数左移12位后,将得到的数字与PC值相加,最后存到t0寄存器中。此时PC=0x0000_1000,与0x7fff_f000相加即得到t0=0x8000_0000。
与此同时,PC自增4,随后在jump_to_ram_gen模块中,第二条指令jr t0被执行,该指令跳转到t0所存地址,即ITCM起始地址0x8000_0000,随后即可按照软件所编写的指令执行。

  generate 
   if(1) begin: jump_to_ram_gen
       // Just jump to the ITCM base address 
      for (i=0;i<1024;i=i+1) begin: rom_gen
          if(i==0) begin: rom0_gen
              assign mask_rom[i] = 32'h7ffff297; //auipc   t0, 0x7ffff
          end
          else if(i==1) begin: rom1_gen
              assign mask_rom[i] = 32'h00028067; //jr   t0
          end
          else begin: rom_non01_gen
              assign mask_rom[i] = 32'h00000000; 
          end
      end
   end

④ PC:8000_0000 → 8000_0004 → ……
随后可见PC值不断自增。

查看自定义的demo_i2c.c编译得出的(.dump)反汇编文件,可见仿真中初始化后PC的地址变化情况与反汇编程序中PC的变化情况相同,则可说明处理器已经正确地按照预定程序执行:

80000000 <_start>:
80000000:   30047073            csrci   mstatus,8
80000004:   10001197            auipc   gp,0x10001
80000008:   f0418193            addi    gp,gp,-252 # 90000f08 <__global_pointer$>
8000000c:   10010117            auipc   sp,0x10010
80000010:   ff410113            addi    sp,sp,-12 # 90010000 <_sp>
80000014:   00000517            auipc   a0,0x0
80000018:   08450513            addi    a0,a0,132 # 80000098 <_itcm>
......
喜欢1
用户评论
zzzjy

zzzjy 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板