全志D1s开发板RISC-V裸机开发环境搭建指南

发布时间:2026/7/17 3:52:01
全志D1s开发板RISC-V裸机开发环境搭建指南 1. 全志D1s开发板裸机开发环境概述第一次接触全志D1s开发板时我完全被它独特的RISC-V架构吸引住了。作为一款基于阿里平头哥C906内核的芯片D1s在IoT和嵌入式领域有着独特的优势。但真正开始裸机开发时环境搭建这个看似简单的第一步就给了我一个下马威。裸机开发与常规的Linux环境开发完全不同它意味着我们需要从零开始构建整个运行环境没有现成的操作系统支持所有的硬件操作都需要直接通过寄存器来完成。这种开发方式虽然原始但能让我们对硬件有最直接的控制权也是理解计算机底层工作原理的最佳途径。在开始之前我们需要明确几个关键点首先D1s采用的是RISC-V指令集架构这意味着传统的ARM工具链在这里不适用其次裸机开发需要特殊的编译工具和调试方法最后环境搭建的完整性直接决定了后续开发的顺畅程度。2. 开发环境准备2.1 硬件准备清单工欲善其事必先利其器。在开始搭建环境前我们需要准备以下硬件设备全志D1s开发板核心板底板USB转TTL串口模块推荐使用CH340或CP2102芯片的版本5V/2A电源适配器Micro USB数据线TF卡建议8GB以上Class10速度等级杜邦线若干可选JTAG调试器如DAP-Link或J-Link这里特别提醒一下串口模块的选择很重要。我曾经因为贪便宜买了个劣质的PL2303芯片的串口模块结果在调试过程中出现了各种莫名其妙的通信问题最后不得不重新购买CH340的模块才解决。2.2 软件工具准备软件方面我们需要准备以下工具链RISC-V交叉编译工具链官方推荐使用平头哥提供的工具链也可以选择从源码编译riscv-gnu-toolchain代码编辑器VSCode PlatformIO插件或者Eclipse RISC-V插件烧录工具PhoenixSuit全志官方烧录工具xfel工具命令行烧录工具串口调试工具Windows: Putty/TeraTermLinux: minicom/picocommacOS: screen/minicom其他辅助工具Git用于源码管理Make构建系统OpenOCD调试用注意工具链的版本兼容性非常重要。我曾经遇到过因为工具链版本不匹配导致的奇怪编译错误建议使用官方推荐的版本。3. 工具链安装与配置3.1 安装RISC-V交叉编译工具链对于Linux用户推荐Ubuntu 20.04 LTS可以按照以下步骤安装# 添加平头哥工具链仓库 sudo add-apt-repository ppa:thead-maintainers/tools sudo apt-get update # 安装基础编译工具 sudo apt-get install build-essential git wget flex bison libgmp-dev libmpfr-dev libmpc-dev texinfo # 安装RISC-V工具链 sudo apt-get install gcc-riscv64-unknown-elf gdb-riscv64-unknown-elf对于Windows用户建议使用WSL2安装Ubuntu后按照上述步骤操作或者直接下载预编译的工具链包。验证安装是否成功riscv64-unknown-elf-gcc --version3.2 配置开发环境环境变量配置是很多人容易忽略的一步。我们需要将工具链路径添加到系统PATH中# 编辑~/.bashrc文件 echo export PATH$PATH:/opt/riscv/bin ~/.bashrc source ~/.bashrc对于VSCode用户建议安装以下插件C/CRISC-V SupportCortex-DebugMakefile Tools3.3 获取D1s裸机开发SDK全志官方并没有提供专门的裸机开发SDK但我们可以从以下几个渠道获取相关资料官方BSP包中的裸机示例《RISC-V体系结构编程与实践》书籍配套代码GitHub上的开源项目如xboot等建议的获取方式git clone https://github.com/riscv-mcu/riscv-d1s-baremetal.git cd riscv-d1s-baremetal git submodule update --init4. 第一个裸机程序点亮LED4.1 硬件连接检查在开始编程前我们需要确认开发板的硬件连接将USB转TTL模块连接到开发板的UART0通常是PA4-TX, PA5-RX连接电源注意电压必须是5V确保启动模式选择开关设置在SPI Flash启动模式4.2 编写简单的LED闪烁程序创建一个简单的main.c文件#include stdint.h // 定义GPIO寄存器 #define GPIO_BASE 0x02000000 #define GPIO_CFG0 (*(volatile uint32_t *)(GPIO_BASE 0x00)) #define GPIO_DATA (*(volatile uint32_t *)(GPIO_BASE 0x10)) // 简单延时函数 void delay(uint32_t count) { while(count--); } int main() { // 配置PG6引脚为输出模式 GPIO_CFG0 ~(0xf 24); // 清除原有配置 GPIO_CFG0 | (0x1 24); // 设置为输出模式 while(1) { GPIO_DATA | (1 6); // 点亮LED delay(1000000); GPIO_DATA ~(1 6); // 熄灭LED delay(1000000); } return 0; }4.3 编写链接脚本创建link.ld文件定义内存布局MEMORY { RAM (rwx) : ORIGIN 0x40000000, LENGTH 64K } SECTIONS { .text : { *(.text*) } RAM .data : { *(.data*) } RAM .bss : { *(.bss*) } RAM _stack_top ORIGIN(RAM) LENGTH(RAM); }4.4 编写Makefile创建Makefile自动化构建过程CC riscv64-unknown-elf-gcc OBJCOPY riscv64-unknown-elf-objcopy CFLAGS -marchrv64imafdc -mabilp64d -nostdlib -fno-builtin -O1 all: led.bin led.elf: main.c $(CC) $(CFLAGS) -Tlink.ld -o $ $^ led.bin: led.elf $(OBJCOPY) -O binary $^ $ clean: rm -f *.elf *.bin flash: led.bin xfel ddr d1 xfel write 0x40000000 led.bin xfel exec 0x400000005. 程序烧录与调试5.1 使用xfel工具烧录xfel是全志系列芯片的通用命令行工具支持D1s的裸机程序烧录首先安装xfelgit clone https://github.com/xboot/xfel.git cd xfel make sudo make install烧录程序make flash这个过程会初始化DDR将程序写入内存跳转到程序入口执行5.2 串口调试输出为了便于调试我们可以添加串口输出功能。首先需要初始化UART0void uart_init() { // 配置PA4为UART0_TX, PA5为UART0_RX *(volatile uint32_t *)(0x02000000 0x30) ~(0xff 8); *(volatile uint32_t *)(0x02000000 0x30) | (0x22 8); // 配置UART0参数 *(volatile uint32_t *)(0x02500000 0x08) 0x0d; // 115200波特率 *(volatile uint32_t *)(0x02500000 0x04) 0x03; // 8N1 *(volatile uint32_t *)(0x02500000 0x0c) 0x01; // 使能UART } void uart_putc(char c) { while(!(*(volatile uint32_t *)(0x02500000 0x14) 0x02)); *(volatile uint32_t *)(0x02500000 0x00) c; } void uart_puts(const char *s) { while(*s) { uart_putc(*s); } }然后在main函数中调用uart_init(); uart_puts(Hello, D1s Baremetal!\r\n);5.3 常见问题排查程序烧录后无反应检查电源是否稳定确认烧录地址是否正确D1s通常为0x40000000检查串口连接是否正确串口无输出确认波特率设置为115200检查TX/RX线是否接反确认UART引脚配置正确LED不闪烁确认GPIO引脚号是否正确检查LED电路是否有上拉/下拉电阻用万用表测量GPIO电压变化调试技巧在关键代码处添加串口输出可以帮助定位问题所在。例如在GPIO操作前后打印状态信息。6. 进阶环境配置6.1 使用OpenOCD进行调试虽然xfel可以烧录程序但对于复杂调试我们需要更强大的工具安装OpenOCDsudo apt-get install openocd创建配置文件d1s.cfginterface jlink transport select jtag adapter_khz 1000 set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME riscv -chain-position $_TARGETNAME riscv set_reset_timeout_sec 10 riscv set_command_timeout_sec 10 init halt启动调试会话openocd -f d1s.cfg然后可以在VSCode中使用Cortex-Debug插件进行源码级调试。6.2 集成外设驱动一个完整的裸机环境通常需要以下驱动GPIOUARTSPII2CTimerPWMADC建议将这些驱动模块化组织drivers/ ├── gpio/ ├── uart/ ├── spi/ ├── i2c/ ├── timer/ └── adc/每个驱动提供标准的接口例如GPIO驱动可以提供void gpio_set_mode(uint32_t pin, uint32_t mode); void gpio_write(uint32_t pin, uint32_t value); uint32_t gpio_read(uint32_t pin);6.3 内存管理裸机环境下需要自己管理内存可以实现简单的malloc/free#define HEAP_SIZE 4096 static uint8_t heap[HEAP_SIZE]; static uint32_t heap_ptr 0; void *malloc(size_t size) { if(heap_ptr size HEAP_SIZE) return NULL; void *ptr heap[heap_ptr]; heap_ptr size; return ptr; } void free(void *ptr) { // 简单实现实际上不释放内存 }对于更复杂的需求可以考虑实现内存池或分块管理。7. 项目组织与构建系统7.1 项目目录结构一个良好的项目结构能大大提高开发效率project/ ├── build/ ├── drivers/ ├── include/ ├── lib/ ├── src/ │ ├── main.c │ └── startup.S ├── scripts/ ├── Makefile └── link.ld7.2 自动化构建扩展之前的Makefile支持多文件编译SRCS $(wildcard src/*.c) $(wildcard drivers/*/*.c) OBJS $(patsubst %.c,%.o,$(SRCS)) %.o: %.c $(CC) $(CFLAGS) -Iinclude -c $ -o $ project.elf: $(OBJS) src/startup.o $(CC) $(CFLAGS) -Tlink.ld -o $ $^7.3 启动代码RISC-V需要基本的启动代码创建startup.S.section .init .global _start _start: la sp, _stack_top call main j .这段代码会设置栈指针调用main函数进入死循环防止跑飞7.4 中断处理裸机环境下也可以处理中断需要配置中断向量表.section .vectors .global _vectors _vectors: j _start .word 0 .word 0 // 其他中断向量...然后在main函数中初始化中断控制器void interrupt_init() { // 配置PLIC *(volatile uint32_t *)(0x10000000 0x200004) 0x01; // 使能UART0中断 // 设置全局中断使能 asm volatile(csrs mstatus, 0x8); }8. 实用技巧与经验分享8.1 如何阅读全志芯片手册全志的芯片手册往往信息分散建议重点关注以下章节内存映射表Memory Map时钟系统CCUGPIO控制器UART控制器中断控制器PLIC一个小技巧使用PDF阅读器的搜索功能直接搜索寄存器名称或地址。8.2 寄存器操作最佳实践使用结构体映射寄存器组typedef struct { volatile uint32_t CFG[4]; volatile uint32_t DATA; volatile uint32_t DRV[2]; volatile uint32_t PUL[2]; } GPIO_Type; #define GPIO ((GPIO_Type *)0x02000000)使用位域定义typedef union { struct { uint32_t MODE:4; uint32_t PULL:2; uint32_t DRV:2; }; uint32_t val; } GPIO_Config;操作寄存器时先读后写uint32_t reg GPIO-CFG[0]; reg ~(0xf 12); // 清除原有配置 reg | (0x1 12); // 设置新配置 GPIO-CFG[0] reg;8.3 性能优化技巧使用-Os优化级别优化代码大小关键函数添加__attribute__((section(.fastcode)))频繁调用的函数声明为static inline使用memcpy/memset等内置函数避免浮点运算RISC-V需要软浮点8.4 调试技巧使用GPIO模拟逻辑分析仪#define DEBUG_PIN 10 void debug_pulse() { GPIO_DATA | (1 DEBUG_PIN); GPIO_DATA ~(1 DEBUG_PIN); }在关键代码处插入断点指令asm volatile(ebreak);使用串口打印调用栈void print_stack(uint32_t *sp, int depth) { uart_puts(Call stack:\r\n); for(int i0; idepth; i) { uart_printf(0x%08x\r\n, sp[i]); } }8.5 电源管理裸机环境下需要注意电源管理关闭未使用的外设时钟进入低功耗模式void enter_wfi() { asm volatile(wfi); }合理配置IO口状态以降低功耗9. 从裸机到RTOS当项目复杂度增加时可以考虑引入RTOS。以下是移植FreeRTOS的基本步骤下载FreeRTOS源码实现port.c中的架构相关代码配置内存管理实现必要的系统调用如printf初始化任务和调度器关键点堆大小配置上下文切换实现中断优先级设置系统时钟配置一个简单的任务示例void led_task(void *arg) { while(1) { GPIO_DATA ^ (1 6); vTaskDelay(500 / portTICK_PERIOD_MS); } } void main() { xTaskCreate(led_task, LED, 128, NULL, 1, NULL); vTaskStartScheduler(); while(1); }10. 资源推荐与后续学习10.1 推荐资源官方文档《D1s Datasheet》《C906 User Manual》书籍《RISC-V体系结构编程与实践》《计算机组成与设计 RISC-V 版》开源项目XBoot支持D1s的bootloaderRT-Thread支持D1s的RTOS10.2 进阶方向外设驱动开发硬件加速器使用如DSP、NPU低功耗优化安全启动实现固件升级方案10.3 社区支持全志官方论坛RISC-V国际基金会社区GitHub上的相关项目电子工程师社区如EEVblog我在实际开发中发现D1s的裸机开发虽然入门门槛较高但一旦掌握了基本环境搭建和调试方法就能充分发挥RISC-V架构的优势。特别是在需要直接硬件控制的场景下裸机开发提供了最大的灵活性和控制力。建议初学者从简单的GPIO控制开始逐步扩展到中断处理、外设驱动等更复杂的功能同时注意保持代码的模块化和可移植性。