在嵌入式系統(tǒng)開發(fā)領域,MCU(微控制單元)芯片在眾多智能設備中發(fā)揮著核心的控制作用,其性能優(yōu)化與功能拓展一直是技術(shù)發(fā)展的重要方向。OpenOCD(Open On-Chip Debugger)作為一個功能強大的開源調(diào)試工具,廣泛應用于嵌入式系統(tǒng)開發(fā)中,為系統(tǒng)調(diào)試與程序燒錄提供了重要支持。
隨著自研MCU芯片項目的不斷推進,如何實現(xiàn)其與OpenOCD的無縫對接成為關(guān)鍵問題之一。而閃存驅(qū)動作為連接MCU與外部存儲、實現(xiàn)數(shù)據(jù)高效存儲與讀取的核心組件,其與OpenOCD的適配對于提升整個系統(tǒng)開發(fā)效率具有重要意義。
本文檔系統(tǒng)性地記錄了國科安芯自研MCU芯片AS32A601添加閃存驅(qū)動至OpenOCD工具的完整過程。通過深入研究OpenOCD的架構(gòu)與工作原理,結(jié)合芯片的技術(shù)手冊,詳細闡述了驅(qū)動編寫、調(diào)試與驗證的各個步驟。旨在為后續(xù)類似項目開發(fā)提供詳實的參考與借鑒,助力嵌入式開發(fā)人員高效實現(xiàn)MCU芯片與OpenOCD的適配,加速產(chǎn)品研發(fā)與技術(shù)迭代進程。
OpenOCD編譯環(huán)境搭建
注:本節(jié)所有內(nèi)容是基于Windows系統(tǒng)開發(fā),如使用其他操作系統(tǒng)請自行參考。
安裝MSYS2
- 安裝MSYS2需要進入到MSYS2官網(wǎng)下載安裝包,進入官網(wǎng)后如圖點擊msys2-x86_64-20240727.exe鏈接下載安裝包。
安裝步驟:
- 雙擊運行安裝程序,出現(xiàn)如圖所示信息單擊下一步:
- 選擇軟件安裝路徑后單擊下一步:
- 出現(xiàn)如圖界面單擊下一步:
- 等待安裝(可能比較慢請耐心等待):
- 如果在安裝過程中出現(xiàn)卡在50%一直不動等情況,可以單擊取消
添加環(huán)境
- 打開我的電腦,進到安裝目錄找到mingw64文件夾,點擊進去找到bin文件夾點擊進去復制路徑。
- 設置環(huán)境變量:將上面復制的路徑添加到環(huán)境變量中。
- 更換鏡像源:更換國內(nèi)鏡像源,此處參考阿里鏡像源上關(guān)于msys2的方式進行更換
- 編輯 /etc/pacman.d/mirrorlist.mingw32 ,在文件開頭添加:Server = https://mirrors.aliyun.com/msys2/mingw/i686
- 編輯 /etc/pacman.d/mirrorlist.mingw64 ,在文件開頭添加:Server = https://mirrors.aliyun.com/msys2/mingw/x86_64
- 編輯 /etc/pacman.d/mirrorlist.msys ,在文件開頭添加:Server = https://mirrors.aliyun.com/msys2/msys/$arch
- 編輯 /etc/pacman.d/mirrorlist.ucrt64 ,在文件開頭添加:Server = https://mirrors.aliyun.com/msys2/mingw/ucrt64
安裝依賴環(huán)境
點擊相應的命令行軟件打開命令行窗口,這里選擇“MSYS2 MINGW64”。
- 更新MSYS2,使用以下指令更新系統(tǒng)文件。直至無更新內(nèi)容為止。
- 安裝依賴環(huán)境 不同的操作系統(tǒng)對安裝的環(huán)境有些許的差異要求,一般而言不同的軟件依賴包有差異的一般為64bit的標識為“x86_64”,而32bit系統(tǒng)一般為“i686”。
- 64bit操作命令(使用時直接復制粘貼即可):
- 32bit操作命令(使用時直接復制粘貼即可):
OpenOCD源碼獲取和編譯流程
下載OpenOCD
- 打開MSYS2 MINGW64命令行窗口,跳轉(zhuǎn)到要克隆的地址,使用 git clone 命令將最新的OpenOCD源碼克隆至本地。(此處給出一個git clone命令操作示例,如需下載其他版本的源碼,請自行修改克隆地址) git clone https://github.com/riscv-collab/riscv-openocd.git
注:后續(xù)所有輸入指令操作,都是在MSYS2 MINGW64命令行窗口中操作,需要將路徑跳轉(zhuǎn)到克隆源碼地址下,如圖所示。
- 在MSYS2 MINGW64命令行窗口,使用cd命令將路徑跳轉(zhuǎn)到剛克隆的源碼地址下,輸入./bootstrap 命令克隆OpenOCD的依賴文件,如下圖所示。
配置OpenOCD
- 使用以下指令配置OpenOCD,這里選擇“--enable-jlink”配置,其他配置(只要安裝了相應的依賴)會默認開啟:
- 配置過程需要一些依賴包,如果配置過程中提示依賴包版本不足或沒有安裝,需要先安裝依賴包。 根據(jù)提示,使用pacman -Ss + 搜索依賴包名,即可找到依賴包。 使用pacman -S + 安裝依賴包名,即可完成依賴包的安裝。
- 安裝依賴包完成后,繼續(xù)執(zhí)行第一步操作,直至配置完成。
OpenOCD添加驅(qū)動
文件總覽
以AS32I601為例,在整個自研芯片集成過程中,需要編寫并測試如下設備支持文件:
- 閃存驅(qū)動程序文件(as32i601.c)
- 內(nèi)置閃存驅(qū)動程序列表文件(drivers.c)
- 編譯腳本文件(Makefile.am)
- 運行腳本文件(as32i601.cfg)
目前,文件實現(xiàn)了編程功能,其他功能可后續(xù)補充,不影響實際功能使用。
設備支持文件存放結(jié)構(gòu)如下:
- openocdsrcflashnoras32i601.c
- openocdsrcflashnordrivers.c
- openocdsrcflashnorMakefile.am
- openocdtcltargetas32i601.cfg
文件詳解
閃存驅(qū)動程序文件(as32i601.c)
由于本芯片特性,燒錄過程無法通過RAM運行片上代碼的形式進行代碼燒錄,因此選用了OpenOCD直接操作qspi寄存器的形式完成flash代碼燒錄,根據(jù)文件中flash_driver結(jié)構(gòu)體所示,該文件需要完成flash_bank_command、erase、write、probe函數(shù)編寫,其他函數(shù)可采用官方庫文件函數(shù)和參考其他芯片驅(qū)動文件即可。
flash_driver結(jié)構(gòu)體,該結(jié)構(gòu)體定義了OpenOCD閃存驅(qū)動程序所需的所有回調(diào)。
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為閃存驅(qū)動程序名稱,這個名稱需要放到內(nèi)置閃存驅(qū)動程序列表文件(drivers.c)中。 第二行,as32i601為定義的閃存名字,此名稱會在運行腳本文件(as32i601.cfg)中用到。 第四行,flash_bank_command為通過腳本文件傳入的信息,用于獲取芯片信息,如芯片地址等。 第五行,erase為擦除函數(shù),該函數(shù)傳入擦除的起始塊和終止塊,需要在此函數(shù)中實現(xiàn)擦除功能。 第六行,write為寫入函數(shù),該函數(shù)傳入寫入的起始地址、寫入的數(shù)據(jù)和寫入的大小,需要在此函數(shù)中實現(xiàn)寫入功能。
flash_bank_command實現(xiàn)函數(shù)為as32i601_flash_bank_command,可以在此函數(shù)中獲取閃存庫命令。
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結(jié)構(gòu)體申請一段內(nèi)存。 第七行,通過命令行獲取控制器基地址,并賦值給priv->ctrlAddress。
erase實現(xiàn)函數(shù)為as32i601_erase,此函數(shù)中實現(xiàn)擦除功能。
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為實際芯片扇區(qū)大小除以行大小所得到的值。因為此版本的OpenOCD會將bin文件大小按照所設置的扇區(qū)大小對齊,所以此文件將扇區(qū)參數(shù)設置為行大小。 第十七到二十四行為扇區(qū)擦除操作,這里不過多進行講解,as32i601_Write_Reg函數(shù)是通過OpenOCD底層函數(shù)target_write_memory封裝而來,可以根據(jù)自己需求進行自定義封裝。
write實現(xiàn)函數(shù)為as32i601_write,此函數(shù)中實現(xiàn)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狀態(tài)和傳入?yún)?shù)是否正確。 第三十八行,為將八位數(shù)組轉(zhuǎn)換為32位數(shù)據(jù),此函數(shù)為OpenOCD底層函數(shù)。 第二十三行到第四十九行,為寫入操作,這里不過多進行講解。
probe實現(xiàn)函數(shù)為as32i601_probe,通過此函數(shù)設定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文件大小計算出所用扇區(qū)數(shù)量,因為此版本的OpenOCD會將bin文件大小按照所設置的扇區(qū)大小對齊,所以此文件將扇區(qū)參數(shù)設置為行大小。 第九行到第十五行,初始化扇區(qū)信息。
內(nèi)置閃存驅(qū)動程序列表文件(drivers.c)
此文件列表中定義了所有閃存,需要將芯片信息加入其中。 (注:不同版本的OpenOCD此處可能略有差異,需要根據(jù)實際情況進行修改)
externconststructflash_driveras32i601_flash;staticconststructflash_driver * constflash_drivers[] = { &aduc702x_flash, &aducm360_flash, ... &as32i601_flash, ... &xmc4xxx_flash, &w600_flash, &rsl10_flash, NULL, };
第一行和第七行,根據(jù)格式添加信息,此處為as32i601_flash。
編譯腳本文件(Makefile.am)
此文件為編譯腳本文件,添加驅(qū)動文件路徑。
NOR_DRIVERS = %D%/aduc702x.c %D%/aducm360.c ... %D%/str9xpec.c %D%/swm050.c %D%/as32i601.c ... %D%/xmc1xxx.c %D%/xmc4xxx.c
第七行,根據(jù)格式添加閃存驅(qū)動程序文件(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
第一行,為適配器驅(qū)動,此處為jlink。 第三行,為適配器傳輸速度,此處為1000。 第五行,為傳輸方式,此處為jtag。 第七行,為復位配置。 第九行,為芯片類型,此處為riscv。 第十行,設置IR寄存器長度和ID號。 第十五行,設置sram的物理地址和長度(以自定義芯片為例)。 第十八行,設置閃存名稱(此名稱和閃存驅(qū)動程序文件中的name名稱對應)、閃存地址、大小和控制寄存器基地址(以自定義芯片為例)。
編譯OpenOCD
- 使用如下指令編譯OpenOCD
注:若編譯過程中出現(xiàn)如圖所示錯誤,說明calloc函數(shù)所使用的頭文件與實際代碼使用版本不匹配,需要將頭文件修改為正確的版本。
- 編譯錯誤失敗原因分析:void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);
- 本地庫環(huán)境修改
- mingw下載
- 打開MinGW官網(wǎng),點擊此處下載安裝包
- 雙擊進行軟件安裝
- 等待安裝完成
- 編譯器安裝
- 勾選“mingw32-gcc”
- 點擊“Apply Changes”
- 點擊“Apply”
- 等待更新完成
- 添加環(huán)境變量 打開系統(tǒng)環(huán)境變量設置窗口,如圖所示,將安裝路徑“C:MinGWbin和C:MinGWinclude”填入環(huán)境變量。(若安裝在其他路徑,可自行修改添加)
- 編譯完成后,為后續(xù)使用方便,可以將相應文件復制出來完成對OpenOCD的打包壓縮。
閃存驅(qū)動使用流程與驗證
注:本章所有輸入指令操作,都是在cmd窗口中進行。
閃存下載
打開cmd窗口,使用如下命令,將bin文件下載到芯片中。(注:文件路徑可使用絕對路徑也可使用相對路徑,在相應環(huán)境中進行修改即可) ./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" 命令執(zhí)行后,會彈出一個窗口,等待窗口自動執(zhí)行完成后結(jié)束。(此處包含了運行調(diào)試信息,若無需此信息,可自行在閃存驅(qū)動程序文件將對應代碼注釋即可)
運行界面如圖所示:
閃存讀取
打開cmd窗口,使用如下命令,連接OpenOCD并讀取閃存,判斷是否下載正確。
./openocd/bin/openocd.exe -f ./openocd/target/as32i601.cfg telnet localhost 4444 halt mdw 0x1000000040
第一行:在cmd窗口輸入此命令,連接OpenOCD,OpenOCD會監(jiān)聽4444端口。 第三行:在一個新的窗口輸入此命令,會連接telnet端口。 第五行:在telnet窗口輸入此命令,操作為停止cpu。 第七行:在telnet窗口輸入此命令,讀取0x10000000地址的數(shù)據(jù),讀取寬度為40個字。
將讀取的數(shù)據(jù)和bin文件進行對比,如果一致,則說明驅(qū)動程序執(zhí)行正確,二進制文件下載成功。(注:此處需要多讀取幾個地址,以確保整個bin文件下載正確)
運行界面如圖所示: OpenOCD連接界面:
讀取內(nèi)存界面:
審核編輯 黃宇
-
芯片
+關(guān)注
關(guān)注
459文章
51984瀏覽量
434143 -
mcu
+關(guān)注
關(guān)注
146文章
17750瀏覽量
358706 -
閃存
+關(guān)注
關(guān)注
16文章
1831瀏覽量
115648
發(fā)布評論請先 登錄
發(fā)力智能化下半場,比亞迪自研自動駕駛芯片或年底流片
瑞薩電子推出采用自研RISC-V CPU內(nèi)核的通用32位MCU
《電子發(fā)燒友電子設計周報》聚焦硬科技領域核心價值 第10期:2025.05.6--2025.05.9
蘋果自研射頻芯片?OPPO自研NPU芯片!芯片的國產(chǎn)替代需要跨越三個誤區(qū)!
閃存頁面擦除操作永遠不會完成的原因?怎么解決?
華為Mate 40系列采用的“自研閃存”來自長江存儲
樹莓派不講武德,自研雙核MCU Pico,STM32哭暈在廁所!

OPPO放棄自研芯片,終止哲庫業(yè)務
蘋果自研Wi-Fi芯片之路充滿挑戰(zhàn)
比亞迪最快于11月實現(xiàn)自研算法量產(chǎn),推進智駕芯片自研進程
基于 IAR Embedded Workbench 的自研 MCU 芯片軟件函數(shù)與變量內(nèi)存布局優(yōu)化精控方法

RISC-V JTAG:開啟MCU 芯片調(diào)試之旅

評論