Menu Close

RISC-V C语言编程1(1)FII-RISC-V CPU简介和C语言编译流程

1. FII-RISC-V CPU简介

 

相关参考文章:

RISC-V教学教案

 

首先,简单了解一下FII-RISC-V CPU。RISC-V是一种基于精简指令集计算机(RISC,reduced instruction set computer)原理的开源标准指令集体系结构(ISA,instruction set architecture) [1]。FII-RISC-V 是按照RISC-V标准进行研发的。以下是有关FII-RISC-V的一些基本特点:

  • 支持32个寄存器(RV32I,支持整型运算)
  • 支持乘除法汇编指令(有些CPU版本不支持)
  • 不支持原子指令
  • 支持压缩指令(有些CPU版本不支持)
  • 支持软件中断
  • 支持计时器中断
  • 支持UART输出(外设)
  • 支持GPIO 4组(每组32个)
  • 4K bytes DTCM
  • 16K bytes ITCM

图1截取了部分RISC-V地址分配(完整地址图点击这里),主要用于编写C语言工程时的地址赋值查询。

 

IMG_256

图1 地址分配

 

GPIO配置如下所示。 相关代码模块位于顶层fii_cpu_sys.v中。

  wire [ 31: 0 ] gpio_ia;
  wire [ 31: 0 ] gpio_oa;
  wire [ 31: 0 ] gpio_ta;
  wire [ 31: 0 ] gpio_a;

  //LED I/O buffer(缓冲器)
  fii_iobuf #( .IO_WIDTH( 32 ) )
  fii_iobuf_insta
  (
      .i_dio_t    ( gpio_ta ),
      .i_dio      ( gpio_oa ),
      .o_dio      ( gpio_ia ),
      .io_dio_p   ( gpio_a )
  );


  assign LED = gpio_a[ IO_WIDTHa - 1: 0 ];


  wire [ 31: 0 ] gpio_ib;
  wire [ 31: 0 ] gpio_ob;
  wire [ 31: 0 ] gpio_tb;
  wire [ 31: 0 ] gpio_b;
  //数码管位选I/O buffer(缓冲器)
  fii_iobuf #( .IO_WIDTH( 32 ) )
  fii_iobuf_instb
  (
      .i_dio_t    ( gpio_tb ),
      .i_dio      ( gpio_ob ),
      .o_dio      ( gpio_ib ),
      .io_dio_p   ( gpio_b )
  );

  assign SEAT = gpio_b[ IO_WIDTHb - 1: 0 ];

  wire [ 31: 0 ] gpio_ic;
  wire [ 31: 0 ] gpio_oc;
  wire [ 31: 0 ] gpio_tc;
  wire [ 31: 0 ] gpio_c;

  //数码管段选I/O buffer(缓冲器)
  fii_iobuf #( .IO_WIDTH( 32 ) )
  fii_iobuf_instc
  (
      .i_dio_t    ( gpio_tc ),
      .i_dio      ( gpio_oc ),
      .o_dio      ( gpio_ic ),
      .io_dio_p   ( gpio_c )
  );

  assign SEG = gpio_c[ IO_WIDTHc - 1: 0 ];

  wire [ 31: 0 ] gpio_id;
  wire [ 31: 0 ] gpio_od;
  wire [ 31: 0 ] gpio_td;
  wire [ 31: 0 ] gpio_d;


  //按键I/O buffer (缓冲器)
  fii_iobuf #( .IO_WIDTH( 32 ) )
  fii_iobuf_instd
  (
      .i_dio_t    ( gpio_td ),
      .i_dio      ( gpio_od ),
      .o_dio      ( gpio_id ),
      .io_dio_p   ( gpio_d )
  );


  assign PB  = gpio_d[2:0];


需要注意的是,只有将assign与I/O buffer(缓冲器)一起使用时,assign才能接inout类型,否则,assign只能连接输出。

 

2.C语言工程编译流程

如图2所示,C 语言的编译流程如下:

  • 编写C语言代码(e.g. Unix和MS-DOS中的foo.c)
  • Compiler(编译器)将C语言转成汇编语言(e.g. Unix中的foo.s,MS-DOS中的 foo.asm)
  • Assembler(汇编器)将汇编语言转换为机器码模块(e.g.Unix中的foo.o, MS-DOS中的foo.obj)
  • Linker(链接器)将汇编语言模块组装,生产最终输出的执行文件(e.g. Unix中的a.out, MS-DOS中的a.exe)
  • 运行时,loader把这个程序加载到内存中,并跳转到它开始的地址

IMG_257

图2 从C源代码翻译为可运行程序的步骤 [2]

 

下图3-5列出了不同的编译情形。可以看出,compiler和assembler将C源文件转换为目标文件(机器码模块),linker在转换多个文件时将每个目标文件链接在一起。如果仅给出汇编文件或目标文件,则可以略过compiler或assembler的翻译。

 

IMG_259

图3 单一文件编译

 

IMG_260

图4 两个文件同时编译

 

IMG_258

图5 多种文件(*.c, *.s, *.o) 同时编译

 

注意:现在广泛使用的工具链,比如GCC(GNC Compiler Collection)可能将编译器,汇编器,链接器,调试器(用于调试)等都打包到一起,以上所示的C语言编译流程只是从逻辑上的划分,实际运行时编译流程可能更加复杂。

 

3. C语言编译流程

  • C语言文件,如图6所示

%title插图%num

图6 C 语言文件 [3]

 

  • 编译器编译后得到的汇编文件,如图7所示

%title插图%num

图7 汇编文件 [3]

 

  • 汇编器汇编之后的机器语言模块,如图8所示

%title插图%num

图8 机器语言模块 [3]

 

  • 链接器链接后的可执行文件,如图9所示

%title插图%num

图9 可执行文件 [3]

 

补充说明:链接器松弛(linker relaxation)

  • JAL有20位的相对地址,一条指令已经足够调到很远的位置。尽管编译器准备了两次跳转,一条就够了。为了优化,链接器会多次扫描替换,尽可能把两条指令替换成一条,每次替换后,函数都会和调用它的位置之间的距离缩短。

 

4.文章参考

[1] F. Based, “C Programming (1) on RISCV FII-PRX100 (ARTIX-7, XC7A100T) XILINX FPGA Board with our FII-Risc-V CPU (RV32G2.0)”, Fraser Innovation Inc | We focus on FPGA Development Board, 2021. [Online]. Available: https://fraserinnovations.com/fpga-board-based/risc-v-cpu-risc-v-c-programming/. [Accessed: 10- Mar- 2021].

[2] Riscv.org, 2021. [Online]. Available: https://riscv.org/wp-content/uploads/2019/12/riscv-spec-20191213.pdf. [Accessed: 22- Feb- 2021].

[3] D. Patterson and A. Waterman, The RISC-V reader. Berkeley: Strawberry Canyon LLC, 2018.

Posted in C语言, RISC-V, RISC-V 教案, 应用开发, 教材与教案, 文章
0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论

相关链接