大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是**IAR下調試信息輸出機制之半主機(Semihosting)**。
在嵌入式世界里,輸出打印信息是一種非常常用的輔助調試手段,借助打印信息,我們可以比較容易地定位和分析程序問題。在嵌入式應用設計里實現打印信息輸出的方式有很多,本系列將以 IAR 環境為例逐一介紹 ARM Cortex-M 內核 MCU 下打印信息輸出方法。
上一篇文章 《IAR下調試信息輸出機制之硬件UART外設》 里我們介紹了利用 MCU 芯片內的硬件 UART 外設去做打印輸出的方式,這種方式很簡單,還可以脫離在線調試環境去使用,但畢竟占用了芯片內部的外設資源,而且調試的時候還需要額外連接串口線路,稍微麻煩了一點。今天痞子衡介紹一種在 IDE 中結合仿真器直接做打印輸出的方式,即半主機(Semihosting)技術。
- Note:本文使用的 IAR EWARM 軟件版本是 v9.10.2。
一、打印輸出整體框圖
老規矩先簡介下本文介紹的打印輸出方法整體軟硬件框圖,硬件上主要是 PC 主機、MCU 目標板、一個 ARM 仿真器(DAP-Link 或者 J-Link 都可以)。
軟件上 PC 這邊就需要 IAR 開發環境即可,這里在編譯目標板 MCU 應用程序時,除了需要包含打印輸出相關代碼(標準 I/O 庫接口),底層接口實現必須選用 IAR 的 Semihosting 庫。當 MCU 程序運行起來后(需要保持在線調試狀態),仿真器實時捕獲代碼里的打印輸出需求,將這種 I/O 訪問需求轉移到 PC 主機上來完成,然后我們在 IAR 的 Terminal I/O 窗口里可以看到打印信息。
上圖里 MCU 應用程序中的 printf() 打印輸出需求到底是如何轉移到 PC 主機上 IAR 軟件里去完成的,這是本文要研究的重點。
二、Semihosting機制原理
Semihosting 中文為半主機(半托管),這是一個在嵌入式系統中已經流傳了幾十年的技術。Semihosting 技術將應用程序中的 I/O 請求通過一定的通道傳送到主機(host),由主機上的資源響應應用程序的 I/O 請求,而不是像在主機上執行本地應用程序一樣,由應用程序所在的硬件系統來響應應用程序 I/O請求,簡單說就是將目標板的輸入/輸出請求從應用程序代碼傳遞到遠程運行調試器的主機上的一種機制。
ARM 也定義了這種 Semihosting 機制實現,我們在目標 ARM 開發板代碼中加入 printf,scanf 等函數調用時,這些函數并不是鏈接到目標開發板的庫函數,而是遠程主機交叉編譯器中帶有的庫函數,并且這些庫函數被編譯時會額外插入軟件中斷指令,早期的 ARMv7 架構下軟中斷使用得是 SVC 指令(SWI 指令),而對于 Cortex-M 處理器(ARMv6-M 或者 ARMv7-M 架構),這個軟件中斷指令是 BKPT。
我們可以在 ARMv6/7/8-M Architecture RM 手冊里找到 BKPT 指令定義,其中 imm8 是指定存儲在指令中的 8 位值,這個值會被處理器忽略,但是調試器可以使用它來存儲關于斷點的附加信息。
如果 BKPT 指令是用于觸發 Semihosting 請求,其 imm8 值應該被設為 0xAB,這是 ARM 的規定。因此當仿真器捕獲到 BKPT #0xab 指令時便會讓主機響應接下來的 I/O 請求。
現在可以觸發 Semihosting 請求了,但是 I/O 請求的種類很多,主機該如何區分呢?別擔心,ARM 還規定了 R0 寄存器來存放請求的類型(編譯器應該在 BKPT 指令前,將請求類型值放到 R0 寄存器里),我們可以在 ARM 開發者官網找到這些請求類型定義以及詳細解釋。整個 Semihosting 機制至此就清楚了,各大 IDE 只需要按照這個規定去具體實現即可。
https://developer.arm.com/documentation/dui0471/g/Semihosting/Semihosting-operations?lang=en
關于更詳細的 Semihosting 機制,可以參考 SEGGER 整理的一篇博客 https://wiki.segger.com/Semihosting 。
三、IAR 對 Semihosting 機制的支持
IAR 對 ARM 定義的 Semihosting 機制是有完善支持的,我們按上一篇的老辦法,看工程編譯鏈接后生成的 .map 文件,找到 IAR 實現 Semihosting 的相關源文件。
3.1 Terminal I/O 查看打印效果
繼續以上一篇文章使用的 SDK_2.11.0_EVK-MIMXRT1060oardsevkmimxrt1060demo_appshello_worldiar 工程為例,簡單改造一下工程里 hello_world.c 文件里的 main() 函數,將原來代碼全部刪掉(原來的打印輸出涉及恩智浦 SDK 封裝,本文沒必要關心其實現),只要如下一句打印即可:
#include?
int?main(void)
{
????printf("hello?world.
");
????while?(1);
}
然后注意工程選項里 Library low-level interface implementation 選項,這里我們選 Semihosted 方式,并且 stdout/stderr 選擇 Via semihosting。這時候底層 I/O 完全由 IAR 內置 Semihosting 庫來搞定了。
我們將 MCU 目標板供上電,并連接調試器在線跑起來看看效果,在 IAR 菜單欄 View 里打開 Terminal I/O 窗口,全速運行,可以看到有 hello world. 字樣打印輸出,沒有真實的串口線路物理連接,照樣能實現打印了。
3.2 Semihosting庫I/O響應設計
IAR 的 Terminal I/O 窗口里怎么就能看到打印輸出的呢?我們在 IAR SystemsEmbedded Workbench 9.10.2armdocEWARM_DevelopmentGuide.ENU 手冊里找到了玄機,其實是 IAR 里的調試組件 C-SPY 負責響應調試器捕捉到的來自 MCU 的 I/O 訪問需求,并負責解釋 Semihosting 庫源碼,然后模擬了對應 I/O 操作。
3.3 Semihosting庫相關源碼實現
現在我們再來查看生成的 hello_world.map 文件,除了 dl7M_tln.a 部分多了 XShttio.o 目標文件外,還增加了 shb_l.a 庫(里面有一系列 .o 文件),這些增加的 .o 文件均是 Semihosting 庫相關源碼實現。我們可以在 IAR SystemsEmbedded Workbench 9.10.2armsrclibsemihosting 目錄下找到 XShttio.c 源文件,這個主要是 ARM 標準 Semihosting 實現層,但是偏 IAR 這一層的 iarttio.o、iarwrite.o、iarwstd.o 并沒有公開源碼,這可能屬于 IAR 軟件商業機密了吧。
*******************************************************************************
*** MODULE SUMMARY
***
Module ro code ro data rw data
------ ------- ------- -------
dl7M_tln.a: [10]
XShttio.o 8 8 8
abort.o 6
exit.o 4
low_level_init.o 4
printf.o 40
putchar.o 32
xfail_s.o 64 4
xprintfsmall_nomb.o 1'281
xprout.o 22
-----------------------------------------------
Total: 1'461 8 12
shb_l.a: [13]
dwrite.o 30
exit.o 20
iarttio.o 124
iarwrite.o 34
iarwstd.o 32
write.o 16
-----------------------------------------------
Total: 256
3.4 從反匯編文件看Semihosting實現
最后我們再從工程反匯編文件角度看一下 Semihosting 機制是不是如第二小節原理里介紹得那樣,先借助 IAR 小工具 ielfdumparm.exe 將工程可執行文件 hello_world.out 轉換成反匯編文件 hello_world.dump。
ielfdumparm.exe --source --code .hello_world.out -o .hello_world.dump
然后使用任意文本編輯器打開這個反匯編文件 hello_world.dump,在里面搜索 BKPT 指令,確實能夠看到插入了多處軟中斷指令用于觸發 Semihosting,并且軟中斷指令前都裝載了 R0 寄存器,痞子衡截取的片段里 R0 裝載的值是 5,從 ARM 文檔里查詢,這對應了 SYS_WRITE 訪問請求。
至此,IAR下調試信息輸出機制之半主機(Semihosting)痞子衡便介紹完畢了,掌聲在哪里~~~
?
審核編輯 :李倩
?
評論
查看更多