长沙门户网站开发,网站规划与设计就业,深圳建设局网站打不开,投票链接制作哪家服务好目录 一、前言
二、从 C 程序到机器指令
三、实验
3.1 实验环境
3.11 Windows 平台下环境搭建
3.12 Ubuntu 平台下环境搭建
3.13 实验涉及到的代码或目录
3.2 各文件作用介绍
3.2.1 link.lds
3.2.2 start.S
3.2.3 lib 和 include 目录
3.2.4 common.mk
3.2.5 demo …目录 一、前言
二、从 C 程序到机器指令
三、实验
3.1 实验环境
3.11 Windows 平台下环境搭建
3.12 Ubuntu 平台下环境搭建
3.13 实验涉及到的代码或目录
3.2 各文件作用介绍
3.2.1 link.lds
3.2.2 start.S
3.2.3 lib 和 include 目录
3.2.4 common.mk
3.2.5 demo 目录
3.3 上板测试
第一种直接作为 FPGA 比特流的一部分下载到板子上。
第二种使用串口烧录程序。
四、写在最后 文章目录
RISC-V处理器的设计与实现一—— 基本指令集_Patarw_Li的博客-CSDN博客
RISC-V处理器的设计与实现二—— CPU框架设计_Patarw_Li的博客-CSDN博客
RISC-V处理器的设计与实现三—— 上板验证_Patarw_Li的博客-CSDN博客
RISC-V处理器设计四—— Verilog 代码设计-CSDN博客
RISC-V处理器设计五—— 在 RISC-V 处理器上运行 C 程序-CSDN博客
一、前言
前面我们使用 verilog 完成了一个 risc-v cpu 的设计但 cpu 最终也是为了程序服务的不能执行程序的 cpu 没有任何意义。所以这一节我们要研究如何在自己设计的 cpu 上运行 C 程序。项目仓库如下
risc-v-cpu: 一个基于 RISC-V 指令集的 CPU 实现成功移植到野火征途 PRO 开发板以及从零开始写一个基于 RISC-V 的 RT-Thread~ - Gitee.com
本节涉及到的代码都在仓库的 rt-thread 目录下。
二、从 C 程序到机器指令
当然cpu 肯定不能直接执行 C 程序cpu 只能识别机器语言机器语言就是由 0 和 1 组成的一条二进制序列比如 ADD 指令有如下格式 在确定 rs1、rs2、rd 三个寄存器后就能确定 ADD 指令的二进制序列cpu 能够识别并执行这个二进制序列。 risc-v 指令集相关内容可以看我的这篇文章RISC-V处理器的设计与实现一—— 基本指令集_risc_v-CSDN博客
那么我们如何从 C 程序得到我们想要的机器指令呢答案就是使用编译器编译分为如下四个阶段程序从编译到运行-CSDN博客 即预处理阶段、生成汇编代码阶段、汇编阶段、链接阶段你可能会问为什么图里面的编译器只参与了一个阶段的工作这是因为现在的编译工具功能非常强大比如 linux 下的 gcc我们只需要一行指令就能帮我们完成四个阶段的操作
gcc hello.c
最终生成的 a.out 即为操作系统如 Linux、Ubuntu上的可执行程序此时的 a.out 虽然能在 像 Linux 这种操作系统上运行但是 cpu 并不能直接识别并运行编译生成的 a.out 大多是 ELF 格式。
ELF 格式的可执行文件包含了很多 cpu 不能识别的信息但像 Linux 这样的操作系统可以识别这些信息比如判断该可执行文件可以在哪种架构上运行该可执行文件的各个段的位置在哪等等。关于 ELF 文件的解析可以看如下博客
ELF文件详解—初步认识-CSDN博客
为了得到 cpu 能直接识别的内容我们可以通过 objcopy 指令将生成的 a.out 转变为去掉了这些无用信息的 hello.bin 文件具体怎么操作可以看我的这篇博客
开发一个RISC-V上的操作系统一—— 环境搭建_riscv操作系统开发_Patarw_Li的博客-CSDN博客
同时如果要编译出我们 risc-v 架构的 cpu 能识别的机器指令我们还需要选择对应的交叉编译器如 Ubuntu 20.04 下可以使用官方提供的 riscv64-unknown-elf-gcc。如果你使用的是 x86 下的gcc那么编译出来的机器指令则只能由 x86 架构的 cpu 识别。
三、实验
实验的目录在本仓库的 rt-thread 目录下该目录下新增了一个 demo 目录可供用户自己设计 C 程序到本 cpu 上运行。其他的 experiment 目录都是和 rt-thread 移植相关的后续也会更新相应的文章。
3.1 实验环境
下面是两种平台下的编译环境搭建大家可以根据自己的情况自行选择。
3.11 Windows 平台下环境搭建
GNU 工具链链接https://pan.baidu.com/s/1Bdmn-FH0T7ekm2kMxkzJTw?pwdqn69 提取码qn69百度云下载解压后将 bin 目录添加到环境变量里即可。make 工具链接https://pan.baidu.com/s/1X-F1BVPMa3-B-V1EHB4tEQ?pwd418d 提取码418d百度云下载解压后将 bin 目录添加到环境变量里即可。Python 3.7
3.12 Ubuntu 平台下环境搭建
Ubuntu 版本
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal$ uname -r
5.15.0-76-generic
安装Ubuntu 20.04官方提供的 GNU工具链
sudo apt update
sudo apt install build-essential gcc make perl dkms git gcc-riscv64-unknown-elf
并且要将 Makefile 里面的 include ../common.mk 修改为 include ../common_ubuntu.mk。
3.13 实验涉及到的代码或目录
include公共头文件目录lib公共函数目录start.S启动文件link.lds链接脚本common.mkMakefile 的公共部分Windows 平台下common_ubuntu.mkMakefile 的公共部分Ubuntu 平台下demo用户可以在本目录下编写能在本 CPU 上运行的 C 程序
3.2 各文件作用介绍
3.2.1 link.lds
link.lds 被称为链接脚本是编译器链接步骤的重要部分。 官方文档
链接操作都是由链接脚本Linker Script所控制的按照官方的话来说链接脚本用来描述 input file比如 hello.c 和 printf.c编译器会将他们分别编译成 hello.o 和 printf.o这两个文件就是链接操作的输入中的每个 section 应该如何被映射到 output file最终生成的可执行文件中并且控制 output file 中的内存布局。
我们可以自己编写链接脚本也可以使用默认的链接脚本如果要使用自己编写的链接脚本则需要在编译时使用 -T 参数来指定。
关于链接脚本的语法大家可以自行查阅资料这里我只介绍对于本次实验比较重要的部分
1. ENTRY 来确定程序的入口为 _start该符号在 start.S 中声明
ENTRY(_start)
2. MEMORY 部分这部分是根据 soc 上的 flash 和 ram 在总线上的起始地址以及所支持的空间大小来配置的。比如本 cpu 的 rom 起始地址为 0x00000000大小为 16KB
MEMORY
{flash (rxai!w) : ORIGIN 0x00000000, LENGTH 16Kram (wxa!ri) : ORIGIN 0x10000000, LENGTH 8K
}
3. 定义程序栈大小如果其他地方没有 __stack_size 的定义就把栈大小定义为 2KB __stack_size DEFINED(__stack_size) ? __stack_size : 2K;
4. 有些段是可读可写的比如 .data 段这些段是不能放到 flash 上的flash 只读所以我们在链接脚本中会把 .data 段指定到 ram 上存储使用 ram .lalign :{. ALIGN(4);PROVIDE( _data_lma . );} flash ATflash .dalign :{. ALIGN(4);PROVIDE( _data . );} ram ATflash .data :{*(.rdata)*(.gnu.linkonce.r.*)*(.data .data.*)*(.gnu.linkonce.d.*). ALIGN(8);PROVIDE( __global_pointer$ . 0x800);*(.sdata .sdata.*)*(.gnu.linkonce.s.*). ALIGN(8);*(.srodata.cst16)*(.srodata.cst8)*(.srodata.cst4)*(.srodata.cst2)*(.srodata .srodata.*)} ram ATflash . ALIGN(4);PROVIDE( _edata . );PROVIDE( edata . );
但这样会导致生成的二进制文件中间产生很大的空洞因为 flash 和 ram 的地址一般是不同的所以我们需要使用 AT关于 AT 的作用可以看这篇博客https://www.cnblogs.com/LogicBai/p/16982841.html这样可以把 .data 段先放到 flash 中然后在启动文件 start.S 中通过 _data_lma、_data、_edata分别对应 .data 段在 flash 中的实际地址、在 ram 上的逻辑起始地址、在 ram 上的逻辑末尾地址这三个地址来把 .data 段从 flash 上搬运到 ram 上这样就可以将数据存储的位置和运行的位置区分开来。
5. 因为 .bss 段的数据都为 0所以无需占用存储空间只需要保存 .bss 段的运行时的逻辑起始地址和末地址__bss_start 和 _end然后在启动时使用 start.S 将起始地址到末地址中间的内容初始化为 0 即可 PROVIDE( __bss_start . );.bss :{*(.sbss*)*(.gnu.linkonce.sb.*)*(.bss .bss.*)*(.gnu.linkonce.b.*)*(COMMON). ALIGN(4);} ram ATram . ALIGN(8);PROVIDE( _end . );
3.2.2 start.S
启动文件 start.S 作为 cpu 上电复位后第一个执行的程序主要完成以下工作
初始化 gp (global pointer) 全局指针寄存器、sp (stack pointer) 栈指针寄存器将 .data 段的数据从 flash 中加载至 RAM 中清空 bss 段数据进入 main 函数运行
start.S 的代码如下
.section .init;
.globl _start;
.type _start, function_start:
.option push
.option norelaxla gp, __global_pointer$
.option popla sp, _sp/* 把 data section 从 flash 搬运到 ram 中 */la a0, _data_lmala a1, _datala a2, _edatabgeu a1, a2, 2f
1:lw t0, (a0) /* 从 flash 中取出一个 word 的数据 */sw t0, (a1) /* 将取出的数据存入 ram 中对应位置 */addi a0, a0, 4addi a1, a1, 4bltu a1, a2, 1b
2:/* 将 bss section 初始化为 0 */la a0, __bss_startla a1, _endbgeu a0, a1, 2f
1:sw zero, (a0)addi a0, a0, 4bltu a0, a1, 1b
2:/* 调用初始化函数 */call _init/* 跳转到 main */call main/* never came here */
loop:j loop
第 8 行 加载全局指针寄存器 gp。
第 10 行加载栈指针寄存器 sp。
第 13 ~ 22 行把 .data 段从 flash 搬运到 ram 中。
第 25 ~ 31 行将 .bss 段初始化为 0。
第 35 行调用初始化函数定义在 init.c 中。
第 37 行调用 main 函数执行用户编写的程序。
第 40、41 行为了防止执行用户程序后cpu 跑飞的情况最后写了一个死循环。
3.2.3 lib 和 include 目录
这两个目录主要提供一些公共的函数比如串口、printf 函数等用户可以在 main.c 文件里面包含头文件后使用里面定义的函数。
3.2.4 common.mk
该文件为 Makefile 编译脚本的公共部分所有 Makefile 文件比如 demo 里面的 Makefile都会包含 common.mk。
common.mk 的内容如下
CROSS_COMPILE riscv-none-embed-RISCV_GCC : $(CROSS_COMPILE)gcc
RISCV_AS : $(CROSS_COMPILE)as
RISCV_GXX : $(CROSS_COMPILE)g
RISCV_OBJDUMP : $(CROSS_COMPILE)objdump
RISCV_GDB : $(CROSS_COMPILE)gdb
RISCV_AR : $(CROSS_COMPILE)ar
RISCV_OBJCOPY : $(CROSS_COMPILE)objcopy
RISCV_READELF : $(CROSS_COMPILE)readelf.PHONY: all
all: $(TARGET)ASM_SRCS $(COMMON_DIR)/start.SC_SRCS $(COMMON_DIR)/init.c
C_SRCS $(COMMON_DIR)/lib/uart.c
C_SRCS $(COMMON_DIR)/lib/printf.c
C_SRCS $(COMMON_DIR)/lib/hw_timer.cLINKER_SCRIPT : $(COMMON_DIR)/link.ldsINCLUDES -I$(COMMON_DIR)LDFLAGS -T $(LINKER_SCRIPT) -nostartfiles -Wl,--gc-sections -Wl,--check-sectionsASM_OBJS : $(ASM_SRCS:.S.o)
C_OBJS : $(C_SRCS:.c.o)
LINK_OBJS $(ASM_OBJS) $(C_OBJS)
LINK_DEPS $(LINKER_SCRIPT)CLEAN_OBJS $(TARGET) $(LINK_OBJS) $(TARGET).dump $(TARGET).bin ../$(TARGET).instCFLAGS -march$(RISCV_ARCH)
CFLAGS -mabi$(RISCV_ABI)
CFLAGS -mcmodel$(RISCV_MCMODEL) -nostdlib -ffunction-sections -fdata-sections -fno-builtin-printf -fno-builtin-malloc -Wall$(TARGET): $(LINK_OBJS) $(LINK_DEPS) Makefile$(RISCV_GCC) $(CFLAGS) $(INCLUDES) $(LINK_OBJS) -o $ $(LDFLAGS)$(RISCV_OBJCOPY) -O binary $ $.bin$(RISCV_OBJDUMP) --disassemble-all $ $.dumppython ../../tools/bin_to_mem.py $.bin ../$.inst$(ASM_OBJS): %.o: %.S$(RISCV_GCC) $(CFLAGS) $(INCLUDES) -c -o $ $$(C_OBJS): %.o: %.c$(RISCV_GCC) $(CFLAGS) $(INCLUDES) -c -o $ $.PHONY: clean
clean:rm -f $(CLEAN_OBJS)
第 1 ~ 10 行配置工具链。
第 15 ~ 20 行指定需要编译的公共汇编文件和 C 文件。
第 23 行配置链接脚本的路径。
第 25 行指定公共头文件目录。
第 27 行指定链接参数
-nostartfiles指定链接时不要使用标准的系统启动文件自定义入口函数_start时必须使用 -nostartfiles 选项进行链接。-Wl,--gc-sections在链接生成最终可执行文件时如果带有-Wl,--gc-sections参数并且之前编译目标文件时带有-ffunction-sections、-fdata-sections参数则链接器ld不会链接未使用的函数从而减小可执行文件大小。--check-sections检查段地址是否重叠 (默认)。
3.2.5 demo 目录
此目录下包含了 main.c 文件和 Makefile在此目录下执行 make 命令即可生成 demo.bin 和 demo.inst 文件下面上板测试会用到这两个文件。 main.c 文件的内容是一个简单的加法然后输出加法的结果用户可以改成自己的 C 程序
/* 头文件声明 */
#include ../include/printf.h
#include ../include/uart.h/* main 函数 */
int main(void)
{int a 1;int b 2;int c a b;printf(The result of c: %d\n, c);/* stop here */while(1){};
}
3.3 上板测试
有两种将编译好的二进制程序在本 CPU 上执行的方法。
第一种直接作为 FPGA 比特流的一部分下载到板子上。
将 FPGA/rtl/perips/rom.v 文件里面的如下部分的注释打开并且将路径改为生成的指令文件 demo.inst 的路径 重新编译后直接烧录到板子上即可 使用串口工具连接板子配置好串口号和波特率并打开串口后按下板子上的复位键即可看到输出 第二种使用串口烧录程序。
将 demo.bin 二进制文件复制到 serial_utils/binary 目录下然后进入 serial_utils 目录先按住 key1 不动 然后使用命令行执行如下命令烧录 demo.bin 文件烧录完成即可松开 key1
# 这里的 COM 号要根据你自己的来选我这里是 COM3
python .\serial_send.py COM3 .\binary\demo.bin 使用串口工具连接板子配置好串口号和波特率并打开串口后按下板子上的复位键即可看到输出 两种方法相比第一种方法更为稳妥第二种方法更为灵活这里更建议大家使用第一种方法在程序出问题的时候第一种方法还可以使用 modelsim 仿真调试。
第二种方法目前还不太稳定如果遇到第二种方法烧录失败可以多烧录几次可能因为接触不良或者尝试一下第一种方法。
四、写在最后
至此如何在本项目的 CPU 上运行 C 程序已经介绍完了既然能运行 C 程序那么运行一个实时操作系统应该也是没问题的这也对应了本项目的 rt-thread 实验大家感兴趣的话可以继续学习本仓库后续也会出相应的文章
如果遇到问题也欢迎加群 892873718 交流~ 文章转载自: http://www.morning.sgfnx.cn.gov.cn.sgfnx.cn http://www.morning.ltxgk.cn.gov.cn.ltxgk.cn http://www.morning.rqknq.cn.gov.cn.rqknq.cn http://www.morning.nmfml.cn.gov.cn.nmfml.cn http://www.morning.ykwgl.cn.gov.cn.ykwgl.cn http://www.morning.zfcfx.cn.gov.cn.zfcfx.cn http://www.morning.dwhnb.cn.gov.cn.dwhnb.cn http://www.morning.yngtl.cn.gov.cn.yngtl.cn http://www.morning.hxmqb.cn.gov.cn.hxmqb.cn http://www.morning.mprpx.cn.gov.cn.mprpx.cn http://www.morning.aswev.com.gov.cn.aswev.com http://www.morning.fmswb.cn.gov.cn.fmswb.cn http://www.morning.pmftz.cn.gov.cn.pmftz.cn http://www.morning.lwqst.cn.gov.cn.lwqst.cn http://www.morning.rgnq.cn.gov.cn.rgnq.cn http://www.morning.wjlnz.cn.gov.cn.wjlnz.cn http://www.morning.rtspr.cn.gov.cn.rtspr.cn http://www.morning.nqpxs.cn.gov.cn.nqpxs.cn http://www.morning.lpmdy.cn.gov.cn.lpmdy.cn http://www.morning.gkgb.cn.gov.cn.gkgb.cn http://www.morning.tlbhq.cn.gov.cn.tlbhq.cn http://www.morning.rgpy.cn.gov.cn.rgpy.cn http://www.morning.qxjck.cn.gov.cn.qxjck.cn http://www.morning.rcyrm.cn.gov.cn.rcyrm.cn http://www.morning.tdscl.cn.gov.cn.tdscl.cn http://www.morning.lwhsp.cn.gov.cn.lwhsp.cn http://www.morning.lcplz.cn.gov.cn.lcplz.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn http://www.morning.nfbkz.cn.gov.cn.nfbkz.cn http://www.morning.thpzn.cn.gov.cn.thpzn.cn http://www.morning.jhswp.cn.gov.cn.jhswp.cn http://www.morning.dhyqg.cn.gov.cn.dhyqg.cn http://www.morning.ubpsa.cn.gov.cn.ubpsa.cn http://www.morning.rqnzh.cn.gov.cn.rqnzh.cn http://www.morning.bswxt.cn.gov.cn.bswxt.cn http://www.morning.nlygm.cn.gov.cn.nlygm.cn http://www.morning.lsfzq.cn.gov.cn.lsfzq.cn http://www.morning.phxdc.cn.gov.cn.phxdc.cn http://www.morning.srjbs.cn.gov.cn.srjbs.cn http://www.morning.cpgdy.cn.gov.cn.cpgdy.cn http://www.morning.lthtp.cn.gov.cn.lthtp.cn http://www.morning.blbys.cn.gov.cn.blbys.cn http://www.morning.fnfhs.cn.gov.cn.fnfhs.cn http://www.morning.jpgfq.cn.gov.cn.jpgfq.cn http://www.morning.yqyhr.cn.gov.cn.yqyhr.cn http://www.morning.mrckk.cn.gov.cn.mrckk.cn http://www.morning.tgts.cn.gov.cn.tgts.cn http://www.morning.ydwnc.cn.gov.cn.ydwnc.cn http://www.morning.wfyzs.cn.gov.cn.wfyzs.cn http://www.morning.lwmzp.cn.gov.cn.lwmzp.cn http://www.morning.rbkdg.cn.gov.cn.rbkdg.cn http://www.morning.gqwbl.cn.gov.cn.gqwbl.cn http://www.morning.qfdmh.cn.gov.cn.qfdmh.cn http://www.morning.qbfwb.cn.gov.cn.qbfwb.cn http://www.morning.rhkmn.cn.gov.cn.rhkmn.cn http://www.morning.zpjhh.cn.gov.cn.zpjhh.cn http://www.morning.cnbdn.cn.gov.cn.cnbdn.cn http://www.morning.qcymf.cn.gov.cn.qcymf.cn http://www.morning.rftk.cn.gov.cn.rftk.cn http://www.morning.zlchy.cn.gov.cn.zlchy.cn http://www.morning.pqcbx.cn.gov.cn.pqcbx.cn http://www.morning.xwqxz.cn.gov.cn.xwqxz.cn http://www.morning.kjyqr.cn.gov.cn.kjyqr.cn http://www.morning.dgsr.cn.gov.cn.dgsr.cn http://www.morning.hmgqy.cn.gov.cn.hmgqy.cn http://www.morning.ypcbm.cn.gov.cn.ypcbm.cn http://www.morning.htfnz.cn.gov.cn.htfnz.cn http://www.morning.jkftn.cn.gov.cn.jkftn.cn http://www.morning.ckcjq.cn.gov.cn.ckcjq.cn http://www.morning.ngzkt.cn.gov.cn.ngzkt.cn http://www.morning.xclgf.cn.gov.cn.xclgf.cn http://www.morning.qgwpx.cn.gov.cn.qgwpx.cn http://www.morning.fnmgr.cn.gov.cn.fnmgr.cn http://www.morning.ksgjy.cn.gov.cn.ksgjy.cn http://www.morning.kqqk.cn.gov.cn.kqqk.cn http://www.morning.trsdm.cn.gov.cn.trsdm.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.mnnxt.cn.gov.cn.mnnxt.cn http://www.morning.jgrjj.cn.gov.cn.jgrjj.cn http://www.morning.jphxt.cn.gov.cn.jphxt.cn