跳转至

RISC-V架构基础与开发环境搭建

学习目标

完成本教程后,你将能够:

  • 理解RISC-V指令集架构的基本概念和设计理念
  • 了解RISC-V的开源生态系统和主要芯片厂商
  • 掌握RISC-V工具链(GCC、OpenOCD)的安装和配置
  • 能够编写、编译和调试第一个RISC-V程序
  • 实现LED闪烁控制,验证开发环境
  • 掌握RISC-V程序的烧录和调试方法

前置要求

在开始本教程之前,你需要:

知识要求: - 了解C语言基础(变量、函数、指针) - 熟悉基本的数字电路知识 - 了解微控制器的基本概念 - 有一定的命令行操作经验

技能要求: - 能够使用文本编辑器编写代码 - 会使用基本的命令行工具 - 了解如何连接开发板和电脑

推荐但非必需: - 有ARM Cortex-M或其他MCU的开发经验 - 了解Makefile的基本语法 - 熟悉Git版本控制工具

准备工作

硬件准备

名称 数量 说明 参考价格
RISC-V开发板 1 GD32VF103C-START或类似 ¥50-100
LED灯 1 红色或绿色,3mm或5mm ¥0.5
电阻 1 220Ω或330Ω,¼W ¥0.1
面包板 1 小型即可 ¥5
杜邦线 若干 公对公、公对母 ¥5
USB数据线 1 Micro-USB或Type-C ¥5

推荐开发板: - GD32VF103C-START:兆易创新出品,基于RISC-V内核,性价比高 - Sipeed Longan Nano:小巧便携,集成LCD屏幕 - HiFive1 Rev B:SiFive官方开发板,功能强大

本教程以 GD32VF103C-START 为例进行讲解。

软件准备

操作系统要求: - Windows 10/11(64位) - Linux(Ubuntu 20.04+推荐) - macOS(10.15+)

必需软件: - RISC-V GCC工具链 - OpenOCD调试器 - 串口终端工具(PuTTY、minicom等)

第一部分:RISC-V架构基础

什么是RISC-V

RISC-V(读作"risk-five")是一个基于精简指令集(RISC)原则的开源指令集架构(ISA)。它由加州大学伯克利分校于2010年开发,现在由RISC-V国际基金会维护。

RISC-V的核心特点

  1. 完全开源
  2. 指令集规范完全开放
  3. 无需支付授权费用
  4. 任何人都可以实现RISC-V处理器

  5. 模块化设计

  6. 基础指令集(RV32I/RV64I)
  7. 可选扩展模块(M、A、F、D、C等)
  8. 灵活组合,满足不同需求

  9. 简洁高效

  10. 基础指令集仅47条指令
  11. 指令编码规整,易于解码
  12. 适合硬件实现和编译器优化

  13. 可扩展性强

  14. 支持自定义指令扩展
  15. 保留大量编码空间
  16. 适应未来技术发展

RISC-V vs ARM Cortex-M

让我们对比RISC-V和ARM Cortex-M,帮助你更好地理解RISC-V:

特性 RISC-V ARM Cortex-M
授权模式 完全开源,免费 商业授权,需付费
指令集 模块化,可定制 固定,不可修改
生态系统 快速发展中 成熟完善
芯片厂商 兆易创新、芯来科技等 ST、NXP、TI等
工具链 GCC、LLVM GCC、Keil、IAR
学习曲线 中等 中等
应用领域 嵌入式、IoT、AI 嵌入式、IoT

选择建议: - 学习和研究:RISC-V更适合,开源透明 - 商业产品:ARM生态更成熟,但RISC-V正在快速追赶 - 定制需求:RISC-V可以添加自定义指令

RISC-V指令集介绍

RISC-V指令集采用模块化设计,由基础指令集和多个扩展模块组成。

基础指令集

  1. RV32I:32位整数基础指令集
  2. 47条基础指令
  3. 支持整数运算、逻辑运算、分支跳转
  4. 32个通用寄存器(x0-x31)
  5. 适用于32位嵌入式系统

  6. RV64I:64位整数基础指令集

  7. 在RV32I基础上扩展到64位
  8. 增加64位数据操作指令
  9. 适用于高性能应用

常用扩展模块

扩展 名称 说明
M 乘除法 整数乘法和除法指令
A 原子操作 原子读-改-写指令,支持多核
F 单精度浮点 32位浮点运算
D 双精度浮点 64位浮点运算
C 压缩指令 16位压缩指令,提高代码密度

组合命名: - RV32I:仅基础指令集 - RV32IM:基础 + 乘除法 - RV32IMAC:基础 + 乘除法 + 原子 + 压缩 - RV32GC:通用组合(IMAFD + C)

GD32VF103使用的是RV32IMAC指令集

RISC-V寄存器组织

RISC-V有32个通用寄存器,每个寄存器32位(RV32)或64位(RV64)。

寄存器列表

寄存器 ABI名称 说明 调用约定
x0 zero 硬连线为0 常量0
x1 ra 返回地址 调用者保存
x2 sp 堆栈指针 被调用者保存
x3 gp 全局指针 -
x4 tp 线程指针 -
x5-x7 t0-t2 临时寄存器 调用者保存
x8 s0/fp 保存寄存器/帧指针 被调用者保存
x9 s1 保存寄存器 被调用者保存
x10-x11 a0-a1 函数参数/返回值 调用者保存
x12-x17 a2-a7 函数参数 调用者保存
x18-x27 s2-s11 保存寄存器 被调用者保存
x28-x31 t3-t6 临时寄存器 调用者保存

特殊寄存器: - x0 (zero):永远为0,写入无效 - x1 (ra):存储函数返回地址 - x2 (sp):堆栈指针,指向栈顶 - x10-x11 (a0-a1):函数参数和返回值

控制状态寄存器(CSR): - mstatus:机器状态寄存器 - mie:机器中断使能 - mtvec:机器陷阱向量基地址 - mepc:机器异常程序计数器

RISC-V基础指令示例

# 数据传送指令
li   a0, 100        # 加载立即数:a0 = 100
mv   a1, a0         # 寄存器传送:a1 = a0
lw   a0, 0(sp)      # 从内存加载字:a0 = *(sp + 0)
sw   a0, 4(sp)      # 存储字到内存:*(sp + 4) = a0

# 算术运算指令
add  a0, a1, a2     # 加法:a0 = a1 + a2
sub  a0, a1, a2     # 减法:a0 = a1 - a2
addi a0, a1, 10     # 立即数加法:a0 = a1 + 10

# 逻辑运算指令
and  a0, a1, a2     # 按位与:a0 = a1 & a2
or   a0, a1, a2     # 按位或:a0 = a1 | a2
xor  a0, a1, a2     # 按位异或:a0 = a1 ^ a2

# 移位指令
sll  a0, a1, a2     # 逻辑左移:a0 = a1 << a2
srl  a0, a1, a2     # 逻辑右移:a0 = a1 >> a2

# 分支指令
beq  a0, a1, label  # 相等则跳转:if (a0 == a1) goto label
bne  a0, a1, label  # 不等则跳转:if (a0 != a1) goto label
blt  a0, a1, label  # 小于则跳转:if (a0 < a1) goto label

# 跳转指令
jal  ra, function   # 跳转并链接:ra = PC+4; PC = function
jalr ra, 0(a0)      # 间接跳转:ra = PC+4; PC = a0 + 0
ret                 # 返回:PC = ra(伪指令,实际是 jalr x0, 0(ra))

开源生态与芯片厂商

主要RISC-V芯片厂商

  1. 兆易创新(GigaDevice)
  2. GD32VF103系列:基于芯来科技N200内核
  3. 主频108MHz,Flash最大128KB
  4. 兼容STM32F103引脚

  5. 芯来科技(Nuclei)

  6. N200/N300/N600系列内核
  7. 专注于RISC-V处理器IP
  8. 提供完整的开发工具链

  9. SiFive

  10. RISC-V先驱公司
  11. E系列(嵌入式)、U系列(应用)
  12. HiFive开发板

  13. 平头哥(T-Head)

  14. 玄铁系列处理器
  15. C906、C910等高性能内核
  16. 应用于AIoT领域

  17. 赛昉科技(StarFive)

  18. JH7100/JH7110 SoC
  19. 面向Linux应用
  20. VisionFive开发板

开源项目: - RISC-V GNU工具链:GCC、Binutils、GDB - OpenOCD:开源调试器 - QEMU:RISC-V模拟器 - Spike:RISC-V ISA模拟器

第二部分:开发环境搭建

步骤1:安装RISC-V GCC工具链

RISC-V GCC工具链包含编译器、汇编器、链接器和调试器,是开发RISC-V程序的基础。

Windows系统安装

方法1:使用预编译包(推荐)

  1. 下载RISC-V工具链:
  2. 访问:https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases
  3. 下载最新版本的Windows包(如:xpack-riscv-none-embed-gcc-10.2.0-1.2-win32-x64.zip)

  4. 解压到指定目录:

    C:\Tools\riscv-none-embed-gcc
    

  5. 添加到系统PATH:

  6. 右键"此电脑" → "属性" → "高级系统设置"
  7. 点击"环境变量"
  8. 在"系统变量"中找到"Path",点击"编辑"
  9. 添加:C:\Tools\riscv-none-embed-gcc\bin
  10. 点击"确定"保存

  11. 验证安装:

    riscv-none-embed-gcc --version
    

预期输出:

riscv-none-embed-gcc (xPack GNU RISC-V Embedded GCC x86_64) 10.2.0

方法2:使用MSYS2编译(高级)

如果需要最新版本或自定义配置,可以使用MSYS2从源码编译。

Linux系统安装(Ubuntu)

方法1:使用包管理器(推荐)

# 更新软件源
sudo apt update

# 安装RISC-V工具链
sudo apt install gcc-riscv64-unknown-elf

# 验证安装
riscv64-unknown-elf-gcc --version

方法2:从源码编译

# 安装依赖
sudo apt install autoconf automake autotools-dev curl python3 libmpc-dev \
    libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf \
    libtool patchutils bc zlib1g-dev libexpat-dev

# 克隆工具链仓库
git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain

# 配置(针对嵌入式,不需要Linux支持)
./configure --prefix=/opt/riscv --with-arch=rv32imac --with-abi=ilp32

# 编译(需要较长时间)
sudo make -j$(nproc)

# 添加到PATH
echo 'export PATH=/opt/riscv/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

macOS系统安装

# 安装Homebrew(如果未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装RISC-V工具链
brew tap riscv/riscv
brew install riscv-tools

# 验证安装
riscv64-unknown-elf-gcc --version

步骤2:安装OpenOCD调试器

OpenOCD(Open On-Chip Debugger)是一个开源的调试器,支持RISC-V。

Windows安装

  1. 下载OpenOCD:
  2. 访问:https://github.com/xpack-dev-tools/openocd-xpack/releases
  3. 下载Windows版本

  4. 解压到:

    C:\Tools\openocd
    

  5. 添加到PATH:

    C:\Tools\openocd\bin
    

  6. 验证安装:

    openocd --version
    

Linux安装

# Ubuntu/Debian
sudo apt install openocd

# 验证安装
openocd --version

macOS安装

brew install openocd

# 验证安装
openocd --version

步骤3:安装串口终端工具

Windows

推荐使用 PuTTYTera Term

  1. 下载PuTTY:https://www.putty.org/
  2. 安装并运行
  3. 选择"Serial"连接类型
  4. 设置波特率:115200

Linux

使用 minicomscreen

# 安装minicom
sudo apt install minicom

# 配置串口
sudo minicom -s

# 使用screen(更简单)
sudo apt install screen
screen /dev/ttyUSB0 115200

macOS

# 使用screen
screen /dev/tty.usbserial-* 115200

步骤4:安装驱动程序

GD32VF103开发板驱动

GD32VF103开发板通常使用CH340或FT232串口芯片。

Windows驱动安装

  1. CH340驱动:
  2. 下载:http://www.wch.cn/downloads/CH341SER_EXE.html
  3. 运行安装程序

  4. 验证:

  5. 连接开发板
  6. 打开"设备管理器"
  7. 查看"端口(COM和LPT)"下是否出现新的COM口

Linux驱动

大多数Linux发行版已内置CH340驱动,无需额外安装。

# 查看串口设备
ls /dev/ttyUSB*

# 添加用户到dialout组(避免需要sudo)
sudo usermod -a -G dialout $USER
# 注销后重新登录生效

环境验证

完成所有安装后,让我们验证开发环境是否正确配置。

# 1. 检查GCC工具链
riscv-none-embed-gcc --version
riscv-none-embed-objdump --version
riscv-none-embed-gdb --version

# 2. 检查OpenOCD
openocd --version

# 3. 检查串口设备
# Windows: 在设备管理器中查看
# Linux: ls /dev/ttyUSB*
# macOS: ls /dev/tty.usbserial-*

预期结果: - 所有命令都能正常执行 - 显示版本信息 - 串口设备可见

如果遇到问题,请参考本文末尾的"故障排除"部分。

第三部分:第一个RISC-V程序

电路连接

在开始编程之前,我们需要连接LED到开发板。

硬件连接图

GD32VF103开发板
    ┌─────────────────┐
    │                 │
    │  [PA7]  ●───────┼──→ 220Ω电阻 ──→ LED正极(长脚)
    │                 │
    │  [GND]  ●───────┼──→ LED负极(短脚)
    │                 │
    │  [USB]  ●       │
    └─────────────────┘
         └──→ 连接到电脑USB口

连接步骤

  1. 识别LED极性
  2. 长脚为正极(阳极)
  3. 短脚为负极(阴极)

  4. 连接电阻

  5. 将220Ω电阻一端连接到PA7引脚
  6. 另一端连接到LED正极

  7. 连接LED

  8. LED正极连接电阻
  9. LED负极连接GND

  10. 连接USB

  11. 使用USB线连接开发板到电脑

注意事项: - 确保电阻连接正确,保护LED - 检查LED极性,接反不会亮 - 连接前断开电源

创建项目

让我们创建第一个RISC-V项目:LED闪烁程序。

项目结构

led_blink/
├── src/
│   └── main.c          # 主程序
├── include/
│   └── gd32vf103.h     # 芯片头文件
├── startup/
│   └── startup.S       # 启动文件
├── link.ld             # 链接脚本
└── Makefile            # 编译脚本

步骤1:创建项目目录

# 创建项目目录
mkdir led_blink
cd led_blink

# 创建子目录
mkdir src include startup

步骤2:编写主程序

创建 src/main.c 文件:

/**
 * @file    main.c
 * @brief   GD32VF103 LED闪烁示例
 * @note    LED连接到PA7引脚
 * 
 * 硬件连接:
 * - PA7 -> 220Ω电阻 -> LED正极
 * - GND -> LED负极
 * 
 * 功能:LED以1Hz频率闪烁
 */

#include <stdint.h>

// GD32VF103寄存器定义
#define RCU_BASE        0x40021000
#define GPIOA_BASE      0x40010800

// RCU寄存器
#define RCU_APB2EN      (*(volatile uint32_t *)(RCU_BASE + 0x18))
#define RCU_APB2EN_PAEN (1 << 2)  // GPIOA时钟使能

// GPIO寄存器
#define GPIOA_CTL1      (*(volatile uint32_t *)(GPIOA_BASE + 0x04))
#define GPIOA_OCTL      (*(volatile uint32_t *)(GPIOA_BASE + 0x0C))

/**
 * @brief  简单延时函数
 * @param  count: 延时计数
 * @note   实际延时时间取决于系统时钟
 */
void delay(uint32_t count) {
    while(count--) {
        __asm__ volatile ("nop");  // 空操作
    }
}

/**
 * @brief  GPIO初始化
 * @note   配置PA7为推挽输出模式
 */
void gpio_init(void) {
    // 1. 使能GPIOA时钟
    RCU_APB2EN |= RCU_APB2EN_PAEN;

    // 2. 配置PA7为推挽输出,最大速度50MHz
    // CTL1控制PA8-PA15,PA7在CTL0中
    // 但为了示例简单,我们使用PA8(在CTL1中)
    // 清除PA8配置位(bit 0-3)
    GPIOA_CTL1 &= ~(0xF << 0);
    // 设置为推挽输出,50MHz(0011)
    GPIOA_CTL1 |= (0x3 << 0);
}

/**
 * @brief  设置LED状态
 * @param  state: 1=点亮,0=熄灭
 */
void led_set(uint8_t state) {
    if (state) {
        GPIOA_OCTL |= (1 << 8);   // PA8输出高电平
    } else {
        GPIOA_OCTL &= ~(1 << 8);  // PA8输出低电平
    }
}

/**
 * @brief  主函数
 */
int main(void) {
    // 初始化GPIO
    gpio_init();

    // 主循环:LED闪烁
    while (1) {
        led_set(1);           // 点亮LED
        delay(500000);        // 延时约500ms

        led_set(0);           // 熄灭LED
        delay(500000);        // 延时约500ms
    }

    return 0;
}

代码说明

  1. 寄存器定义
  2. 直接操作硬件寄存器
  3. 使用volatile防止编译器优化

  4. GPIO初始化

  5. 使能GPIOA时钟
  6. 配置PA8为推挽输出模式

  7. LED控制

  8. 通过OCTL寄存器控制输出电平
  9. 高电平点亮LED,低电平熄灭

  10. 延时函数

  11. 简单的软件延时
  12. 实际项目中应使用定时器

步骤3:创建启动文件

创建 startup/startup.S 文件:

/**
 * @file    startup.S
 * @brief   GD32VF103启动文件
 */

    .section .text.entry
    .globl _start

_start:
    # 1. 设置全局指针
    .option push
    .option norelax
    la gp, __global_pointer$
    .option pop

    # 2. 设置堆栈指针
    la sp, _stack_top

    # 3. 清零BSS段
    la a0, _sbss
    la a1, _ebss
    bgeu a0, a1, 2f
1:
    sw zero, (a0)
    addi a0, a0, 4
    bltu a0, a1, 1b
2:

    # 4. 调用main函数
    call main

    # 5. 如果main返回,进入死循环
1:
    j 1b

    .section .text
    .globl trap_entry
    .align 2
trap_entry:
    # 简单的陷阱处理:死循环
1:
    j 1b

步骤4:创建链接脚本

创建 link.ld 文件:

/**
 * @file    link.ld
 * @brief   GD32VF103链接脚本
 */

OUTPUT_ARCH("riscv")
ENTRY(_start)

MEMORY
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 128K
    RAM (rwx)   : ORIGIN = 0x20000000, LENGTH = 32K
}

SECTIONS
{
    .text : {
        *(.text.entry)
        *(.text*)
        *(.rodata*)
    } > FLASH

    .data : {
        _sdata = .;
        *(.data*)
        _edata = .;
    } > RAM AT > FLASH

    .bss : {
        _sbss = .;
        *(.bss*)
        *(COMMON)
        _ebss = .;
    } > RAM

    . = ALIGN(4);
    _stack_top = ORIGIN(RAM) + LENGTH(RAM);

    __global_pointer$ = MIN(_sdata + 0x800, MAX(_sdata + 0x800, _ebss - 0x800));
}

步骤5:创建Makefile

创建 Makefile 文件:

# 工具链前缀
PREFIX = riscv-none-embed-

# 工具定义
CC = $(PREFIX)gcc
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
SIZE = $(PREFIX)size

# 编译选项
ARCH = rv32imac
ABI = ilp32
CFLAGS = -march=$(ARCH) -mabi=$(ABI) -mcmodel=medany
CFLAGS += -Wall -O2 -g
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -I./include

# 链接选项
LDFLAGS = -T link.ld -nostartfiles
LDFLAGS += -Wl,--gc-sections

# 源文件
SRCS = src/main.c
ASMS = startup/startup.S

# 目标文件
OBJS = $(SRCS:.c=.o) $(ASMS:.S=.o)

# 目标程序
TARGET = led_blink

.PHONY: all clean flash

all: $(TARGET).bin $(TARGET).hex

$(TARGET).elf: $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
    $(SIZE) $@

$(TARGET).bin: $(TARGET).elf
    $(OBJCOPY) -O binary $< $@

$(TARGET).hex: $(TARGET).elf
    $(OBJCOPY) -O ihex $< $@

%.o: %.c
    $(CC) $(CFLAGS) -c -o $@ $<

%.o: %.S
    $(CC) $(CFLAGS) -c -o $@ $<

clean:
    rm -f $(OBJS) $(TARGET).elf $(TARGET).bin $(TARGET).hex

# 反汇编(用于调试)
disasm: $(TARGET).elf
    $(OBJDUMP) -d $< > $(TARGET).asm

编译程序

现在我们可以编译程序了。

# 在项目目录下执行
make

# 预期输出
riscv-none-embed-gcc -march=rv32imac -mabi=ilp32 ... -c -o src/main.o src/main.c
riscv-none-embed-gcc -march=rv32imac -mabi=ilp32 ... -c -o startup/startup.o startup/startup.S
riscv-none-embed-gcc ... -o led_blink.elf src/main.o startup/startup.o
   text    data     bss     dec     hex filename
    256       0       0     256     100 led_blink.elf
riscv-none-embed-objcopy -O binary led_blink.elf led_blink.bin
riscv-none-embed-objcopy -O ihex led_blink.elf led_blink.hex

编译成功标志: - 生成 led_blink.elf(可执行文件) - 生成 led_blink.bin(二进制文件) - 生成 led_blink.hex(十六进制文件) - 显示程序大小信息

常见编译错误

  1. 找不到工具链

    make: riscv-none-embed-gcc: Command not found
    
    解决:检查PATH环境变量

  2. 链接错误

    undefined reference to `__global_pointer$'
    
    解决:检查链接脚本是否正确

  3. 语法错误: 仔细检查代码,修正语法错误

烧录程序

方法1:使用OpenOCD(推荐)

  1. 创建OpenOCD配置文件 openocd.cfg
# GD32VF103配置文件
adapter driver ftdi
adapter speed 1000

transport select jtag

# GD32VF103目标配置
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME

# Flash配置
flash bank $_CHIPNAME.flash stm32f1x 0x08000000 0 0 0 $_TARGETNAME

init
halt
  1. 启动OpenOCD:
openocd -f openocd.cfg
  1. 在另一个终端中,使用GDB烧录:
riscv-none-embed-gdb led_blink.elf

# 在GDB中执行
(gdb) target remote localhost:3333
(gdb) load
(gdb) continue

方法2:使用dfu-util(USB DFU模式)

如果开发板支持DFU模式:

# 安装dfu-util
sudo apt install dfu-util  # Linux
brew install dfu-util      # macOS

# 进入DFU模式(按住BOOT0按钮,按下RESET)

# 烧录程序
dfu-util -a 0 -s 0x08000000 -D led_blink.bin

方法3:使用厂商工具

GD32VF103可以使用GD32 MCU Dfu Tool:

  1. 下载工具:http://www.gd32mcu.com/cn/download
  2. 安装并运行
  3. 选择bin文件
  4. 点击"下载"

测试验证

烧录完成后,观察LED:

预期现象: - LED以1Hz频率闪烁 - 亮500ms,灭500ms - 循环往复

如果LED不闪烁

  1. 检查硬件连接
  2. 检查LED极性
  3. 使用万用表测试PA8电压
  4. 检查程序是否正确烧录

调试程序

使用GDB进行调试:

# 1. 启动OpenOCD(在一个终端)
openocd -f openocd.cfg

# 2. 启动GDB(在另一个终端)
riscv-none-embed-gdb led_blink.elf

# 3. 在GDB中连接目标
(gdb) target remote localhost:3333

# 4. 加载程序
(gdb) load

# 5. 设置断点
(gdb) break main

# 6. 运行程序
(gdb) continue

# 7. 单步执行
(gdb) step

# 8. 查看寄存器
(gdb) info registers

# 9. 查看内存
(gdb) x/10x 0x40010800

# 10. 继续执行
(gdb) continue

常用GDB命令

命令 说明
break 设置断点
continue 继续执行
step 单步执行(进入函数)
next 单步执行(不进入函数)
print 打印变量值
info registers 查看寄存器
x 查看内存
quit 退出GDB

故障排除

问题1:工具链安装失败

症状

riscv-none-embed-gcc: command not found

可能原因: - PATH环境变量未正确设置 - 工具链未正确安装 - 使用了错误的工具链名称

解决方法

  1. 检查PATH:

    echo $PATH  # Linux/macOS
    echo %PATH% # Windows
    

  2. 手动添加PATH:

    # Linux/macOS
    export PATH=/opt/riscv/bin:$PATH
    
    # Windows(在命令提示符中)
    set PATH=C:\Tools\riscv-none-embed-gcc\bin;%PATH%
    

  3. 永久添加PATH:

  4. Linux/macOS:编辑 ~/.bashrc~/.zshrc
  5. Windows:通过系统设置添加

问题2:编译错误

症状

undefined reference to `__global_pointer$'

解决方法: - 检查链接脚本中是否定义了 __global_pointer$ - 确保使用了正确的编译选项 -mcmodel=medany

症状

relocation truncated to fit: R_RISCV_GPREL_I

解决方法: - 增大全局指针范围 - 或者不使用全局指针优化:-mno-relax

问题3:烧录失败

症状

Error: unable to open ftdi device

解决方法

  1. 检查USB连接
  2. 安装驱动程序
  3. Linux下添加udev规则:
# 创建udev规则文件
sudo nano /etc/udev/rules.d/99-openocd.rules

# 添加以下内容
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE="0666"

# 重新加载udev规则
sudo udevadm control --reload-rules
sudo udevadm trigger

问题4:LED不闪烁

检查清单

  1. 硬件连接
  2. LED极性正确
  3. 电阻已连接
  4. 引脚连接正确
  5. 电源已接通

  6. 程序烧录

  7. 程序编译成功
  8. 程序烧录成功
  9. 芯片未被锁定

  10. 代码检查

  11. GPIO时钟已使能
  12. GPIO配置正确
  13. 引脚号正确

  14. 硬件测试

    // 测试代码:持续点亮LED
    int main(void) {
        gpio_init();
        led_set(1);  // 持续点亮
        while(1);
    }
    

问题5:OpenOCD连接失败

症状

Error: unable to find a matching target

解决方法

  1. 检查配置文件
  2. 确认芯片型号
  3. 尝试降低JTAG速度:
    adapter speed 100  # 降低到100kHz
    

问题6:串口无法打开

Linux下权限问题

# 添加用户到dialout组
sudo usermod -a -G dialout $USER

# 注销后重新登录

Windows下端口被占用: - 关闭其他串口程序 - 在设备管理器中禁用再启用串口

进阶练习

完成基础教程后,尝试以下练习来巩固学习:

练习1:多LED流水灯

目标:控制3个LED实现流水灯效果

提示: - 使用PA6、PA7、PA8三个引脚 - 依次点亮,每个LED亮200ms

参考代码框架

void led_sequence(void) {
    // LED1亮
    led_set(1, 1);
    delay(200000);
    led_set(1, 0);

    // LED2亮
    led_set(2, 1);
    delay(200000);
    led_set(2, 0);

    // LED3亮
    led_set(3, 1);
    delay(200000);
    led_set(3, 0);
}

练习2:按键控制LED

目标:通过按键控制LED的开关

提示: - 配置一个GPIO为输入模式(带上拉) - 读取按键状态 - 按下时点亮LED,松开时熄灭

参考代码

// 配置按键引脚为输入
void button_init(void) {
    // 配置PC13为输入,上拉
    RCU_APB2EN |= (1 << 4);  // 使能GPIOC时钟
    // 配置为输入模式
    GPIOC_CTL1 &= ~(0xF << 20);
    GPIOC_CTL1 |= (0x8 << 20);  // 上拉输入
}

// 读取按键状态
uint8_t button_read(void) {
    return (GPIOC_ISTAT & (1 << 13)) ? 0 : 1;
}

练习3:使用定时器实现精确延时

目标:使用硬件定时器替代软件延时

提示: - 配置TIMER0 - 设置定时周期为1ms - 使用定时器中断

学习资源: - 参考GD32VF103数据手册的定时器章节 - 学习中断配置方法

练习4:串口输出调试信息

目标:通过串口输出"Hello RISC-V!"

提示: - 配置USART0 - 实现printf重定向 - 波特率设置为115200

参考代码

void uart_init(void) {
    // 使能USART0和GPIOA时钟
    RCU_APB2EN |= (1 << 14) | (1 << 2);

    // 配置PA9为USART0_TX(复用推挽输出)
    // 配置PA10为USART0_RX(浮空输入)

    // 配置波特率115200
    // 配置数据位、停止位、校验位

    // 使能USART0
}

void uart_putc(char c) {
    // 等待发送缓冲区空
    // 发送字符
}

总结

通过本教程,你已经学习了:

理论知识

  • ✅ RISC-V指令集架构的基本概念
  • ✅ RISC-V与ARM的对比
  • ✅ RISC-V寄存器组织和指令集
  • ✅ RISC-V开源生态和芯片厂商

实践技能

  • ✅ RISC-V开发环境的搭建(GCC、OpenOCD)
  • ✅ 第一个RISC-V程序的编写和编译
  • ✅ 程序的烧录和调试方法
  • ✅ LED控制的硬件连接和软件实现

开发流程

  • ✅ 项目创建和组织
  • ✅ 编译和链接过程
  • ✅ 烧录和测试方法
  • ✅ 调试技巧和故障排除

下一步学习

建议继续学习以下内容:

深入RISC-V

  1. 中断和异常处理
  2. 中断向量表配置
  3. 中断服务函数编写
  4. 中断优先级管理

  5. 定时器应用

  6. 基本定时器配置
  7. PWM输出
  8. 输入捕获

  9. 通信接口

  10. UART串口通信
  11. SPI接口使用
  12. I2C总线操作

高级主题

  1. RTOS移植
  2. FreeRTOS在RISC-V上的移植
  3. 任务调度和管理
  4. 信号量和队列

  5. 性能优化

  6. 代码优化技巧
  7. 汇编语言混合编程
  8. 缓存和流水线优化

  9. 自定义指令

  10. RISC-V自定义指令扩展
  11. 硬件加速器设计
  12. 协处理器接口

参考资料

官方文档

  1. RISC-V规范
  2. RISC-V ISA Specification
  3. RISC-V Privileged Architecture

  4. GD32VF103文档

  5. GD32VF103数据手册
  6. GD32VF103用户手册

工具文档

  1. GCC工具链
  2. RISC-V GNU Toolchain
  3. GCC Manual

  4. OpenOCD

  5. OpenOCD User Guide
  6. RISC-V Debug Specification

在线资源

  1. 社区和论坛
  2. RISC-V International
  3. RISC-V中文社区
  4. GD32 MCU社区

  5. 开源项目

  6. RISC-V软件生态
  7. Nuclei SDK
  8. GD32VF103固件库

推荐书籍

  1. 《RISC-V架构与嵌入式开发快速入门》
  2. 系统介绍RISC-V架构
  3. 包含丰富的实例

  4. 《Computer Organization and Design RISC-V Edition》

  5. David Patterson和John Hennessy著
  6. 计算机组成原理经典教材

  7. 《The RISC-V Reader》

  8. 简明的RISC-V入门指南
  9. 适合快速学习

视频教程

  1. RISC-V国际基金会YouTube频道
  2. 芯来科技RISC-V教程
  3. B站RISC-V相关视频

版权声明:本文为原创内容,遵循CC BY-NC-SA 4.0协议。

更新日志: - 2026-03-07:初始版本发布

反馈与建议:如果您在学习过程中遇到问题或有改进建议,欢迎通过平台反馈功能告诉我们。祝你在RISC-V的学习之旅中收获满满!