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命令进行高级调试
  • 调试工具栏: 提供继续、单步、重启等调试控制按钮
相关推荐
FoldWinCard6 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计7 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud1237 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质7 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX7 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.7 小时前
Linux系统管理
linux·运维
阿龙的工作记录8 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手8 小时前
Linux的基本指令
linux·运维·服务器
撩得Android一次心动10 小时前
Linux编程笔记3【个人用】
linux·笔记
OpenPomeloxCommunity10 小时前
3. Linux 同步机制之 spinlock 设计与实现(一):通用层
linux