当前位置: 首页 > news >正文

自研MCU芯片闪存驱动的实现:OpenOCD详细过程记录与操作指南

在嵌入式系统开发领域,MCU(微控制单元)芯片在众多智能设备中发挥着核心的控制作用,其性能优化与功能拓展一直是技术发展的重要方向。OpenOCD(Open On-Chip Debugger)作为一个功能强大的开源调试工具,广泛应用于嵌入式系统开发中,为系统调试与程序烧录提供了重要支持。

随着自研MCU芯片项目的不断推进,如何实现其与OpenOCD的无缝对接成为关键问题之一。而闪存驱动作为连接MCU与外部存储、实现数据高效存储与读取的核心组件,其与OpenOCD的适配对于提升整个系统开发效率具有重要意义。

本文档系统性地记录了国科安芯自研MCU芯片AS32A601添加闪存驱动至OpenOCD工具的完整过程。通过深入研究OpenOCD的架构与工作原理,结合芯片的技术手册,详细阐述了驱动编写、调试与验证的各个步骤。旨在为后续类似项目开发提供详实的参考与借鉴,助力嵌入式开发人员高效实现MCU芯片与OpenOCD的适配,加速产品研发与技术迭代进程。

OpenOCD编译环境搭建

注:本节所有内容是基于Windows系统开发,如使用其他操作系统请自行参考。

安装MSYS2

  • 安装MSYS2需要进入到MSYS2官网下载安装包,进入官网后如图点击msys2-x86_64-20240727.exe链接下载安装包。

安装步骤:

  • 双击运行安装程序,出现如图所示信息单击下一步:

  • 选择软件安装路径后单击下一步:

  • 出现如图界面单击下一步:

  • 等待安装(可能比较慢请耐心等待):

  • 如果在安装过程中出现卡在50%一直不动等情况,可以单击取消

添加环境

  1. 打开我的电脑,进到安装目录找到mingw64文件夹,点击进去找到bin文件夹点击进去复制路径。

  2. 设置环境变量:将上面复制的路径添加到环境变量中。

  • 更换镜像源:更换国内镜像源,此处参考阿里镜像源上关于msys2的方式进行更换

  1. 编辑 /etc/pacman.d/mirrorlist.mingw32 ,在文件开头添加:Server = https://mirrors.aliyun.com/msys2/mingw/i686

  2. 编辑 /etc/pacman.d/mirrorlist.mingw64 ,在文件开头添加:Server = https://mirrors.aliyun.com/msys2/mingw/x86_64

  3. 编辑 /etc/pacman.d/mirrorlist.msys ,在文件开头添加:Server = https://mirrors.aliyun.com/msys2/msys/$arch

  4. 编辑 /etc/pacman.d/mirrorlist.ucrt64 ,在文件开头添加:Server = https://mirrors.aliyun.com/msys2/mingw/ucrt64

安装依赖环境

点击相应的命令行软件打开命令行窗口,这里选择“MSYS2 MINGW64”。

  1. 更新MSYS2,使用以下指令更新系统文件。直至无更新内容为止。

  2. 安装依赖环境 不同的操作系统对安装的环境有些许的差异要求,一般而言不同的软件依赖包有差异的一般为64bit的标识为“x86_64”,而32bit系统一般为“i686”。

  3. 64bit操作命令(使用时直接复制粘贴即可):

  4. 32bit操作命令(使用时直接复制粘贴即可):

OpenOCD源码获取和编译流程

下载OpenOCD

  1. 打开MSYS2 MINGW64命令行窗口,跳转到要克隆的地址,使用 git clone 命令将最新的OpenOCD源码克隆至本地。(此处给出一个git clone命令操作示例,如需下载其他版本的源码,请自行修改克隆地址) git clone https://github.com/riscv-collab/riscv-openocd.git

注:后续所有输入指令操作,都是在MSYS2 MINGW64命令行窗口中操作,需要将路径跳转到克隆源码地址下,如图所示。

  1. 在MSYS2 MINGW64命令行窗口,使用cd命令将路径跳转到刚克隆的源码地址下,输入./bootstrap 命令克隆OpenOCD的依赖文件,如下图所示。

配置OpenOCD

  1. 使用以下指令配置OpenOCD,这里选择“--enable-jlink”配置,其他配置(只要安装了相应的依赖)会默认开启:

  2. 配置过程需要一些依赖包,如果配置过程中提示依赖包版本不足或没有安装,需要先安装依赖包。 根据提示,使用pacman -Ss + 搜索依赖包名,即可找到依赖包。 使用pacman -S + 安装依赖包名,即可完成依赖包的安装。

  3. 安装依赖包完成后,继续执行第一步操作,直至配置完成。

OpenOCD添加驱动

文件总览

以AS32I601为例,在整个自研芯片集成过程中,需要编写并测试如下设备支持文件:

  1. 闪存驱动程序文件(as32i601.c)

  2. 内置闪存驱动程序列表文件(drivers.c)

  3. 编译脚本文件(Makefile.am)

  4. 运行脚本文件(as32i601.cfg)

目前,文件实现了编程功能,其他功能可后续补充,不影响实际功能使用。

设备支持文件存放结构如下:

  1. \openocd\src\flash\nor\as32i601.c

  2. \openocd\src\flash\nor\drivers.c

  3. \openocd\src\flash\nor\Makefile.am

  4. \openocd\tcl\target\as32i601.cfg

文件详解

闪存驱动程序文件(as32i601.c)

由于本芯片特性,烧录过程无法通过RAM运行片上代码的形式进行代码烧录,因此选用了OpenOCD直接操作qspi寄存器的形式完成flash代码烧录,根据文件中flash_driver结构体所示,该文件需要完成flash_bank_command、erase、write、probe函数编写,其他函数可采用官方库文件函数和参考其他芯片驱动文件即可。

flash_driver结构体,该结构体定义了OpenOCD闪存驱动程序所需的所有回调。

conststructflash_driveras32i601_flash = { .name = "as32i601", .commands = as32i601_command_handlers, .flash_bank_command = as32i601_flash_bank_command, .erase = as32i601_erase, .protect = as32i601_protect, .write = as32i601_write, .read = default_flash_read, .probe = as32i601_probe, .auto_probe = as32i601_probe, .erase_check = default_flash_blank_check, .info = get_as32i601_info, .free_driver_priv = default_flash_free_driver_priv, };

第一行,as32i601_flash为闪存驱动程序名称,这个名称需要放到内置闪存驱动程序列表文件(drivers.c)中。 第二行,as32i601为定义的闪存名字,此名称会在运行脚本文件(as32i601.cfg)中用到。 第四行,flash_bank_command为通过脚本文件传入的信息,用于获取芯片信息,如芯片地址等。 第五行,erase为擦除函数,该函数传入擦除的起始块和终止块,需要在此函数中实现擦除功能。 第六行,write为写入函数,该函数传入写入的起始地址、写入的数据和写入的大小,需要在此函数中实现写入功能。

flash_bank_command实现函数为as32i601_flash_bank_command,可以在此函数中获取闪存库命令。

FLASH_BANK_COMMAND_HANDLER(as32i601_flash_bank_command) { if (CMD_ARGC < 7) return ERROR_COMMAND_SYNTAX_ERROR; bank->driver_priv = malloc(sizeof(as32i601_priv)); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], priv->ctrlAddress); LOG_INFO("as32i601 probe: 0x%x ctrlAddress", priv->ctrlAddress); priv->probed = 0; return ERROR_OK; }

第六行,为as32i601_priv结构体申请一段内存。 第七行,通过命令行获取控制器基地址,并赋值给priv->ctrlAddress。

erase实现函数为as32i601_erase,此函数中实现擦除功能。

intas32i601_erase(struct flash_bank *bank, unsignedint first, unsignedint last) { structtarget *target = bank->target; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } LOG_INFO("as32i601_erase"); // flash memory mass erase as32i601_flashStop(bank); for(unsignedint sector = first; sector <= last; sector += 0x400) { as32i601_Write_Reg(bank, QSPI_CR, 0x00000031); as32i601_Write_Reg(bank, QSPI_CCR, 0x00000106); as32i601_WaitNotBusy(bank); as32i601_Write_Reg(bank, QSPI_CCR, 0x000025d8); as32i601_Write_Reg(bank, QSPI_AR, bank->sectors[sector].offset); as32i601_WaitNotBusy(bank); as32i601_read_flashstatus(bank); bank->sectors[sector].is_erased = 1; } return ERROR_OK; }

第十五行,0x400为实际芯片扇区大小除以行大小所得到的值。因为此版本的OpenOCD会将bin文件大小按照所设置的扇区大小对齐,所以此文件将扇区参数设置为行大小。 第十七到二十四行为扇区擦除操作,这里不过多进行讲解,as32i601_Write_Reg函数是通过OpenOCD底层函数target_write_memory封装而来,可以根据自己需求进行自定义封装。

write实现函数为as32i601_write,此函数中实现bin文件写入功能。

staticintas32i601_write(struct flash_bank *bank, constuint8_t *buffer,uint32_t offset, uint32_t count) { structtarget *target = bank->target; if(target->state != TARGET_HALTED){ LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } if(offset & 0x03){ LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset); return ERROR_FLASH_DST_BREAKS_ALIGNMENT; } if(count & 0x3){ LOG_ERROR("size 0x%" PRIx32 " breaks required 4-byte alignment", count); return ERROR_FLASH_DST_BREAKS_ALIGNMENT; } as32i601_flashStop(bank); uint32_t addr = offset; LOG_INFO("as32i601 probe: %d count, 0%d addr", count, addr); while(count >= 256) { as32i601_Write_Reg(bank, QSPI_CR, 0x00000031); as32i601_Write_Reg(bank, QSPI_CCR, 0x00000106); as32i601_WaitNotBusy(bank); as32i601_Write_Reg(bank, QSPI_CCR, 0x00802502); as32i601_Write_Reg(bank, QSPI_AR, addr); as32i601_Write_Reg(bank, QSPI_DLR, 256-1); // flash memory word programfor(uint32_t address = 0; address < 256; address += 4) { constuint8_t *t_buffer = buffer + address; uint32_t value = buf_get_u32(t_buffer, 0, 32); as32i601_Write_Reg(bank, QSPI_DR, value); } buffer += 256; as32i601_WaitNotBusy(bank); as32i601_read_flashstatus(bank); LOG_INFO("as32i601 probe: %d count", count); addr = addr + 0x100; count = count - 0x100; } LOG_INFO("as32i601 flash write success"); return ERROR_OK; }

第五行到第十六行,为判断当时cpu状态和传入参数是否正确。 第三十八行,为将八位数组转换为32位数据,此函数为OpenOCD底层函数。 第二十三行到第四十九行,为写入操作,这里不过多进行讲解。

probe实现函数为as32i601_probe,通过此函数设定flash信息。

staticintas32i601_probe(struct flash_bank *bank) { LOG_INFO("as32i601 qspi_probe"); if(!priv->probed){ bank->num_sectors = bank->size/(256); uint32_t offset = 0; bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); for (unsignedint i = 0; i < bank->num_sectors; i++) { bank->sectors[i].offset = offset; bank->sectors[i].size = 256; offset += bank->sectors[i].size; bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = 1; } priv->probed = 1; } return ERROR_OK; }

第六行,通过bin文件大小计算出所用扇区数量,因为此版本的OpenOCD会将bin文件大小按照所设置的扇区大小对齐,所以此文件将扇区参数设置为行大小。 第九行到第十五行,初始化扇区信息。

内置闪存驱动程序列表文件(drivers.c)

此文件列表中定义了所有闪存,需要将芯片信息加入其中。 (注:不同版本的OpenOCD此处可能略有差异,需要根据实际情况进行修改)

externconststructflash_driveras32i601_flash;staticconststructflash_driver * constflash_drivers[] = { &aduc702x_flash, &aducm360_flash, ... &as32i601_flash, ... &xmc4xxx_flash, &w600_flash, &rsl10_flash, NULL, };

第一行和第七行,根据格式添加信息,此处为as32i601_flash。

编译脚本文件(Makefile.am)

此文件为编译脚本文件,添加驱动文件路径。

NOR_DRIVERS = \ %D%/aduc702x.c \ %D%/aducm360.c \ ... %D%/str9xpec.c \ %D%/swm050.c \ %D%/as32i601.c \ ... %D%/xmc1xxx.c \ %D%/xmc4xxx.c

第七行,根据格式添加闪存驱动程序文件(as32i601.c)。

运行脚本文件(as32i601.cfg)

此文件为运行脚本文件,包含芯片配置信息。

adapter driver jlink adapter speed 1000 transport select jtag reset_config srst_nogate set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10002FFFset _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME $_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 0x80000 -work-area-backup 1set _FLASHNAME $_CHIPNAME.flash flash bank $_FLASHNAME as32i601 0x100000000x0100000000 $_TARGETNAME.00x42100800 init halt

第一行,为适配器驱动,此处为jlink。 第三行,为适配器传输速度,此处为1000。 第五行,为传输方式,此处为jtag。 第七行,为复位配置。 第九行,为芯片类型,此处为riscv。 第十行,设置IR寄存器长度和ID号。 第十五行,设置sram的物理地址和长度(以自定义芯片为例)。 第十八行,设置闪存名称(此名称和闪存驱动程序文件中的name名称对应)、闪存地址、大小和控制寄存器基地址(以自定义芯片为例)。

编译OpenOCD

  1. 使用如下指令编译OpenOCD

注:若编译过程中出现如图所示错误,说明calloc函数所使用的头文件与实际代码使用版本不匹配,需要将头文件修改为正确的版本。

  1. 编译错误失败原因分析:void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);

  2. 本地库环境修改

  3. mingw下载

  4. 打开MinGW官网,点击此处下载安装包

  5. 双击进行软件安装

  6. 等待安装完成

  7. 编译器安装

  8. 勾选“mingw32-gcc”

  9. 点击“Apply Changes”

  10. 点击“Apply”

  11. 等待更新完成

  12. 添加环境变量 打开系统环境变量设置窗口,如图所示,将安装路径“C:\MinGW\bin和C:\MinGW\include”填入环境变量。(若安装在其他路径,可自行修改添加)

  13. 编译完成后,为后续使用方便,可以将相应文件复制出来完成对OpenOCD的打包压缩。

闪存驱动使用流程与验证

注:本章所有输入指令操作,都是在cmd窗口中进行。

闪存下载

打开cmd窗口,使用如下命令,将bin文件下载到芯片中。(注:文件路径可使用绝对路径也可使用相对路径,在相应环境中进行修改即可) ./openocd/bin/openocd.exe -f ./openocd/target/as32i601.cfg -c "program ./SV32C601_Software/demo/gpio/GPIO_IO_test/OBJ/bin/gpio_io_test.bin 0x10000000" -c "shutdown" 命令执行后,会弹出一个窗口,等待窗口自动执行完成后结束。(此处包含了运行调试信息,若无需此信息,可自行在闪存驱动程序文件将对应代码注释即可)

运行界面如图所示:

闪存读取

打开cmd窗口,使用如下命令,连接OpenOCD并读取闪存,判断是否下载正确。

./openocd/bin/openocd.exe -f ./openocd/target/as32i601.cfg telnet localhost 4444 halt mdw 0x1000000040

第一行:在cmd窗口输入此命令,连接OpenOCD,OpenOCD会监听4444端口。 第三行:在一个新的窗口输入此命令,会连接telnet端口。 第五行:在telnet窗口输入此命令,操作为停止cpu。 第七行:在telnet窗口输入此命令,读取0x10000000地址的数据,读取宽度为40个字。

将读取的数据和bin文件进行对比,如果一致,则说明驱动程序执行正确,二进制文件下载成功。(注:此处需要多读取几个地址,以确保整个bin文件下载正确)

运行界面如图所示: OpenOCD连接界面:

读取内存界面:

相关文章:

  • 关于vue-office在vue3工程中的引用报错问题
  • 京东京言-AI项目实现逻辑
  • windows 部署 Kafka3.x KRaft 模式 不依赖 ZooKeeper
  • [计算机科学#10]:早期的计算机编程方式
  • 立式筒仓式发酵槽实验台试验装置
  • Java网络编程:理解URI、URL和URN
  • 全球森林数据如何分析?基于R语言森林生态系统结构、功能与稳定性分析与可视化
  • 加湿器WT588F02B-8S语音芯片应用方案:让加湿器会说话
  • 云平台搭建
  • 微服务不注册到nacos的方法
  • SpringBoot的自动配置和起步依赖原理
  • Android开发报错解决
  • 正则表达式实战指南:原理、口诀与高频场景案例
  • 姚琛全新特别版EP上线 携手金牌制作人诠释夏日浪漫
  • Python 基础知识
  • 正则表达式(Regular Expression)详解
  • 配置VS的DLL文件引用目录(两种方案,无需每次生成dll后手动将其复制到exe目录下)
  • 表面等离子共振技术(SPR)在小分子与蛋白质相互作用研究中的应用
  • MyBatis 动态 SQL 完整笔记
  • 世界少棒联盟(Little League Baseball)·棒球1号位
  • 讲座预告|全球贸易不确定情况下企业创新生态构建
  • 一热就出汗 VS 热死都不出汗的人,哪个更健康?
  • 宁合两大都市圈交汇之城含山:要想身体好,常往含山跑
  • 吴清:推动公募基金高质量发展的行动方案今天将会发布
  • 巴基斯坦军方:印度导弹袭击已造成至少3人死亡
  • 住宿行业迎“最火五一”:数千家酒店连续3天满房,民宿预订量创历史新高