RISC-V开发环境搭建

RISC-V开发环境搭建

目录

  • [1.1 QEMU模拟器](#1.1 QEMU模拟器)
    • [1.1.1 QEMU简介](#1.1.1 QEMU简介)
    • [1.1.2 QEMU Virt实验平台特性](#1.1.2 QEMU Virt实验平台特性)
    • [1.1.3 QEMU优势](#1.1.3 QEMU优势)
  • [1.2 开发环境配置](#1.2 开发环境配置)
    • [1.2.1 系统要求](#1.2.1 系统要求)
    • [1.2.2 安装QEMU](#1.2.2 安装QEMU)
    • [1.2.3 安装RISC-V编译器](#1.2.3 安装RISC-V编译器)
      • [1.2.3.1 官方工具链安装](#1.2.3.1 官方工具链安装)
      • [1.2.3.2 验证编译器安装](#1.2.3.2 验证编译器安装)
      • [1.2.3.3 编译测试程序](#1.2.3.3 编译测试程序)
    • [1.2.4 验证安装](#1.2.4 验证安装)
  • [1.3 调试工具](#1.3 调试工具)
    • [1.3.1 GDB调试器](#1.3.1 GDB调试器)
      • [1.3.1.1 远程调试](#1.3.1.1 远程调试)
    • [1.3.2 VS Code调试配置](#1.3.2 VS Code调试配置)
      • [1.3.2.1 安装必要扩展](#1.3.2.1 安装必要扩展)
      • [1.3.2.2 配置launch.json](#1.3.2.2 配置launch.json)
      • [1.3.2.3 调试步骤](#1.3.2.3 调试步骤)
  • [VS Code调试界面示例](#VS Code调试界面示例)

1.1 模拟器选择

1.1.1 QEMU模拟器

QEMU Virt实验平台模拟的是一款通用的RISC-V开发板,为RISC-V架构学习和开发提供了完整的仿真环境。

1.1.2 NEMU模拟器

NEMU是一个轻量级的RISC-V模拟器,由中科院计算所开发,专门用于教学和学习RISC-V架构。

1.1.3 QEMU Virt实验平台特性

QEMU Virt平台支持以下硬件特性:

  • 最多支持8个RV32GC/RV64GC通用处理器核心
  • 支持CLINT本地中断控制器
  • 支持PLIC中断控制器
  • 支持NOR Flash
  • 支持兼容NS16550的串口
  • 支持RTC
  • 支持8个VirtIO-MMIO传输设备
  • 支持1个PCIe主机桥接设备
  • 支持fw_cfg,用于从QEMU获取固件配置信息

1.1.4 QEMU优势

QEMU基本能满足我们学习RISC-V体系结构的要求,而且免费,调试方便

主要优势:

  1. 免费开源:无需购买昂贵的硬件开发板
  2. 功能完整:支持完整的RISC-V指令集和硬件特性
  3. 调试便利:提供丰富的调试功能和接口
  4. 学习友好:适合初学者理解RISC-V架构
  5. 可扩展性:支持多种RISC-V配置和扩展

1.1.5 NEMU优势

主要优势:

  1. 轻量级:代码简洁,易于理解和学习
  2. 教学友好:专门为教学设计,注释详细
  3. 调试便利:提供丰富的调试功能和接口
  4. 开源免费:完全开源,适合学习研究
  5. 中文支持:有中文文档和社区支持

1.2 开发环境配置

1.2.1 系统要求

  • 操作系统:Ubuntu 24.04 LTS
  • 内存:建议8GB以上
  • 存储:至少10GB可用空间

1.2.2 安装模拟器

1.2.2.1 安装QEMU
bash 复制代码
sudo apt update
sudo apt install qemu-system-misc
1.2.2.2 安装NEMU

NEMU是一个轻量级的RISC-V模拟器,适合教学和学习使用:

bash 复制代码
# 克隆NEMU源码
git clone https://github.com/OpenXiangShan/NEMU.git
cd NEMU

# 安装依赖
sudo apt install build-essential libreadline-dev

# 配置和编译
make menuconfig
make xxxx_defconfig
make -j$(nproc)

# 安装到系统路径(可选)
sudo make install

验证NEMU安装:

bash 复制代码
# 检查NEMU版本
riscv64-nemu-interpreter --version

1.2.3 安装RISC-V编译器

1.2.3.1 官方工具链安装
bash 复制代码
# 安装RISC-V GNU工具链
sudo apt update
sudo apt install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu

# 安装RISC-V GDB调试器
sudo apt install gdb-multiarch

# 安装其他开发工具
sudo apt install make cmake git
1.2.3.2 验证编译器安装
bash 复制代码
# 检查RISC-V GCC版本
riscv64-linux-gnu-gcc --version

# 检查RISC-V GDB版本
riscv64-linux-gnu-gdb --version

# 检查工具链路径
which riscv64-linux-gnu-gcc
1.2.3.3 编译测试程序

创建C语言环境初始化汇编文件:start.s

c 复制代码
.section .text.boot

.globl _start
_start:
	/* Disable M-mode interrupts */
	csrw mie, zero

	/* Set up stack, stack size is 4KB */
	la sp, stacks_start
	li t0, 4096
	add sp, sp, t0

	csrw mscratch, sp
	tail main

.section .data
.align  12
.global stacks_start
stacks_start:
	.skip 4096

创建测试文件 hello.c

c 复制代码
#define UART        0x10000000
#define UART_SIZE   4096

/* THR:transmitter holding register */
#define UART_DAT    (UART+0x00) /* Data register */
#define UART_IER    (UART+0x01) /* Interrupt enable register */
#define UART_IIR    (UART+0x02) /* Interrupt identification register (read only) */
#define UART_FCR    (UART+0x02) /* FIFO control register (write only) */
#define UART_LCR    (UART+0x03) /* Line control register */
#define UART_MCR    (UART+0x04) /* Modem control register */
#define UART_LSR    (UART+0x05) /* Line status register */
#define UART_MSR    (UART+0x06) /* Modem status register */

#define UART_DLL    (UART+0x00)  /* Divisor latch register low 8 bits */
#define UART_DLM    (UART+0x01)  /* Divisor latch register high 8 bits */

#define UART_LSR_ERROR  0x80  /* Error */
#define UART_LSR_EMPTY  0x40  /* Transmit FIFO and shift register empty */
#define UART_LSR_TFE    0x20  /* Transmit FIFO empty */
#define UART_LSR_BI	    0x10  /* Break interrupt */
#define UART_LSR_FE	    0x08  /* Framing error (no stop bit received) */
#define UART_LSR_PE	    0x04  /* Parity error */
#define UART_LSR_OE	    0x02  /* Overrun error */
#define UART_LSR_DR	    0x01  /* Data ready in FIFO */

#define __arch_getl(a)			(*(volatile unsigned int *)(a))
#define __arch_putl(v,a)		(*(volatile unsigned int *)(a) = (v))
#define __arch_getb(a)			(*(volatile unsigned char *)(a))
#define __arch_putb(v,a)		(*(volatile unsigned char *)(a) = (v))
#define __arch_getq(a)			(*(volatile unsigned long *)(a))
#define __arch_putq(v,a)		(*(volatile unsigned long *)(a) = (v))
#define dmb()		            __asm__ __volatile__ ("" : : : "memory")

#define readl(c)	({ unsigned int  __v = __arch_getl((unsigned long)c); dmb(); __v; })
#define writel(v,c)	({ unsigned int  __v = v; dmb(); __arch_putl(__v, (unsigned long)c);})
#define readb(c)	({ unsigned char  __v = __arch_getb(c); dmb(); __v; })
#define writeb(v,c)	({ unsigned char  __v = v; dmb(); __arch_putb(__v,c);})
#define readq(c)	({ unsigned long  __v = __arch_getq(c); dmb(); __v; })
#define writeq(v,c)	({ unsigned long  __v = v; dmb(); __arch_putq(__v,c);})

#define UART_DEFAULT_CLOCK 1843200 
#define UART_DEFAULT_BAUD  115200

// UART send character function
void uart_send(char c)
{
    while((readb(UART_LSR) & UART_LSR_EMPTY) == 0)
        ;
    writeb(c, UART_DAT);
}

// UART send string function
void uart_send_string(char *str)
{
    int i;
    for (i = 0; str[i] != '\0'; i++)
        uart_send((char) str[i]);
}

int main(void)
{
  unsigned int divisor = UART_DEFAULT_CLOCK / (16 * UART_DEFAULT_BAUD);

    /* disable interrupt */
	writeb(0, UART_IER);
	/* Enable DLAB (set baud rate divisor) */
	writeb(0x80, UART_LCR);
	writeb((unsigned char)divisor, UART_DLL);
	writeb((unsigned char)(divisor >> 8), UART_DLM);
	/* 8 bits, no parity, one stop bit */
	writeb(0x3, UART_LCR);
	/* Enable FIFO, clear FIFO, set 14-byte threshold */
	writeb(0xc7, UART_FCR);
	/* Enable receive buffer full interrupt */
	writeb(0x1, UART_IER);
    // Output hello world
    uart_send_string("hello world\n");
    
    // Infinite loop to keep program running
    while(1) {
        // Other functionality can be added here
    }
}

创建链接脚本

c 复制代码
ENTRY(_start)

SECTIONS
{
    /* 设置sbi的加载入口地址为0x80000000 */
	. = 0x80000000,

	.text.boot : { *(.text.boot) }
	.text : { *(.text) }
	.rodata : { *(.rodata) }
	.data : { *(.data) }
	. = ALIGN(0x8);
	bss_begin = .;
	.bss : { *(.bss*) } 
	bss_end = .;
}

编译测试:

bash 复制代码
# 编译为RISC-V可执行文件
riscv64-linux-gnu-gcc -save-temps=obj -g -O0 -Wall -nostdlib -mcmodel=medany -mabi=lp64 -march=rv64imafd -fno-PIE -fno-omit-frame-pointer -Wno-builtin-declaration-mismatch -c hello.c -o hello.o
riscv64-linux-gnu-as -g -march=rv64imafd -mabi=lp64 -o start.o start.s
riscv64-linux-gnu-ld -T linker.ld -o hello.elf hello.o start.o

# 查看文件信息
file hello.elf

# 使用QEMU运行
qemu-system-riscv64 -nographic -machine virt -m 128M  -bios hello.elf 

1.2.4 验证安装

验证QEMU:

bash 复制代码
qemu-system-riscv64 --version

验证NEMU:

bash 复制代码
riscv64-nemu-interpreter --version

验证编译器:

bash 复制代码
riscv64-linux-gnu-gcc --version
riscv64-linux-gnu-gdb --version

1.3 调试工具

1.3.1 GDB调试器

1.3.1.1 远程调试

QEMU支持GDB远程调试:

bash 复制代码
# 启动QEMU并开启GDB调试端口
qemu-system-riscv64 -machine virt -cpu rv64 -m 128M -nographic -bios hello.elf -s -S

# 在另一个终端连接GDB
riscv64-linux-gnu-gdb
(gdb) target remote :1234

1.3.2 VS Code调试配置

VS Code提供了强大的调试功能,可以方便地调试RISC-V程序。通过配置launch.json文件,可以实现图形化的调试体验。

1.3.2.1 安装必要扩展

在VS Code中安装以下扩展:

  1. C/C++ (Microsoft) - 提供C/C++语言支持
  2. C/C++ Extension Pack - 包含调试、智能感知等功能
  3. RISC-V (可选) - RISC-V汇编语言支持

安装方法:

  • 打开VS Code
  • Ctrl+Shift+X 打开扩展面板
  • 搜索并安装上述扩展
1.3.2.2 配置launch.json

在项目根目录创建 .vscode/launch.json 文件:

json 复制代码
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "调试RV64裸机程序(QEMU)",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/hello.elf",
            "cwd": "${workspaceFolder}",
            "MIMode": "gdb",
            "miDebuggerPath": "gdb-multiarch",
            "miDebuggerArgs": "--interpreter=mi",
            "stopAtEntry": true,
            "externalConsole": false,
            "setupCommands": [
                {
                    "description": "设置架构为RISC-V 64位",
                    "text": "set architecture riscv:rv64",
                    "ignoreFailures": true
                },
                {
                    "description": "加载符号文件",
                    "text": "file ${workspaceFolder}/hello.elf",
                    "ignoreFailures": true
                },
                {
                    "description": "连接到QEMU GDB服务器",
                    "text": "target remote localhost:1234",
                    "ignoreFailures": false
                },
                {
                    "description": "在_start函数设置断点",
                    "text": "break _start",
                    "ignoreFailures": true
                },
                {
                    "description": "在汇编文件设置断点",
                    "text": "break sbi_boot.S:_start",
                    "ignoreFailures": true
                },
                {
                    "description": "为gdb启用整齐打印",
                    "text": "set print pretty on",
                    "ignoreFailures": true
                },
                {
                    "description": "设置反汇编格式",
                    "text": "set disassembly-flavor intel",
                    "ignoreFailures": true
                },
                {
                    "description": "启用汇编单步调试",
                    "text": "set step-mode on",
                    "ignoreFailures": true
                },
                {
                    "description": "显示汇编代码",
                    "text": "set disassemble-next-line on",
                    "ignoreFailures": true
                },
                {
                    "description": "设置寄存器显示格式",
                    "text": "set print registers on",
                    "ignoreFailures": true
                },
                {
                    "description": "设置源码目录",
                    "text": "directory ${workspaceFolder}",
                    "ignoreFailures": true
                },
                {
                    "description": "启用源码和汇编混合显示",
                    "text": "set disassemble-next-line auto",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

配置说明:

  • program: 指定要调试的ELF文件路径
  • miDebuggerPath: 使用gdb-multiarch调试器
  • setupCommands: 自动执行的GDB命令
    • 设置RISC-V 64位架构
    • 加载符号文件
    • 连接到QEMU的GDB服务器(端口1234)
    • 在程序入口点设置断点
1.3.2.3 调试步骤

步骤1:启动QEMU调试服务器

在终端中运行以下命令启动QEMU并开启GDB调试端口:

bash 复制代码
qemu-system-riscv64 -machine virt -cpu rv64 -m 128M -nographic -bios hello.elf -s -S

参数说明:

  • -s: 开启GDB调试服务器(默认端口1234)
  • -S: 启动时暂停,等待GDB连接

步骤2:在VS Code中开始调试

  1. 在VS Code中打开项目文件夹
  2. F5 或点击调试面板的"开始调试"按钮
  3. 选择"调试RV64裸机程序(QEMU)"配置
  4. VS Code将自动连接到QEMU的GDB服务器

步骤3:调试功能使用

  • 设置断点: 在代码行号左侧点击设置断点
  • 单步执行 : 使用 F10 (Step Over) 或 F11 (Step Into)
  • 继续执行 : 使用 F5 (Continue)
  • 查看变量: 在"变量"面板查看当前作用域的变量
  • 查看寄存器 : 在"调试控制台"输入 info registers 查看RISC-V寄存器
  • 查看内存 : 在"调试控制台"输入 x/10x $sp 查看栈内存

调试技巧:

  1. 程序入口调试 : 程序从 _start 开始执行,这是设置第一个断点的好地方
  2. 汇编调试: 可以查看RISC-V汇编指令的执行过程
  3. 内存布局 : 使用 info proc mappings 查看内存映射
  4. 寄存器观察 : 重点观察 pc(程序计数器)、sp(栈指针)等关键寄存器

VS Code调试界面示例

下图展示了在VS Code中调试RISC-V程序的界面:

界面说明:

  • 左侧面板: 显示变量、监视、调用栈等调试信息
  • 中央代码区: 显示源代码,支持断点设置和单步执行
  • 底部调试控制台: 可以输入GDB命令进行高级调试
  • 调试工具栏: 提供继续、单步、重启等调试控制按钮
相关推荐
NiKo_W5 小时前
Linux 初识
linux·运维·服务器
磊灬泽9 小时前
【日常错误】鼠标无反应
linux·windows
Miracle&14 小时前
2.TCP深度解析:握手、挥手、状态机、流量与拥塞控制
linux·网络·tcp/ip
专注API从业者14 小时前
Python/Java 代码示例:手把手教程调用 1688 API 获取商品详情实时数据
java·linux·数据库·python
Ribou14 小时前
Ubuntu 24.04.2安装k8s 1.33.4 配置cilium
linux·ubuntu·kubernetes
tan180°15 小时前
Boost搜索引擎 网络库与前端(4)
linux·网络·c++·搜索引擎
Mr. Cao code16 小时前
Docker:颠覆传统虚拟化的轻量级革命
linux·运维·ubuntu·docker·容器
抓饼先生16 小时前
Linux control group笔记
linux·笔记·bash
挺6的还17 小时前
25.线程概念和控制(二)
linux