Gdb/Armulator 源代碼分析
作者Email: Anti_chen2000@sohu.com 摘要 Gdb/Armulator 是Gdb自帶的arm7模擬器,是調試arm程序的一個(gè)好工具.而了解它的原碼結構對擴展它的IO功能有重要意義.本文介紹了從Armulator的啟動(dòng)到其內部運作和IO擴展的大部分原代碼功能. 說(shuō)明 源代碼用的是gdb-5.0.tar+ gdb-5.0-uclinux-armulator-20021127.patch A.和GDB間的通迅 Armulator一般和Gdb通訊有兩種方式,其一是在Gdb內部直接調用模擬器的相關(guān)函數,另一方法則是用pipe或socket傳遞RDP協(xié)議來(lái)連接Gdb和Amulator.而第一種方法是現在Gdb/Armulator所真正使用的(第二種是早期使用的方法),下面就分析了函數直接調用法. 函數直接調用 這個(gè)方法是由Steve (sac@cygnus.com) 修改原RDP方法而來(lái)的,Steve本人的描述如下: It likes to use TCP/IP between the simulator and the host, which is I've added created a new Makefile.in (the original in Makefile.orig) It should be possible (barring major changes in the layout of (Except that I changed armos.c to work more simply with our 以下是RDP 通訊和直接函數調用的圖示: 要清楚Armulator的執行過(guò)程就要從它的啟動(dòng)說(shuō)起,當你在Gdb中鍵入target sim 去激活Amulator后Gdb首先進(jìn)行命令行解釋,并將current_target指針指向sim變量,即將Armulator的調試函數集賦予Gdb,隨后的函數調用堆棧如下: --gdbsim_open (…) in remote-sim.c. 至此Armulator被裝載完畢,其后Gdb就是通過(guò)target_ops(定義在target.h)結構中的各個(gè)函數指針來(lái)完成對它的調試工. B.Armulator 內部機制 a.初始化 從上述可知整個(gè)模擬器的初始化入口是在wrapper.c中的init( )函數,那么它到底又做了些什么呢? (原始的Gdb5.0中的Armulator是模擬ARM7DTMI 的,而補丁代碼修改了memory map 并添加了timer 和uart 的IO能力使其能夠模擬AT91.因為后者是對前者的增強,所以我們的分析以后者為準) Once the armulator to reset ,the ARMul_NewState will be called.And its task is to malloc a ARMul_state stuct which saves the armulator’s states and initialize it .And the ARMul_MemoryInit() will malloc 4m ram for you. 因為這是補丁代碼,難免又冗余出現,實(shí)際10-11行的兩處掉用是沒(méi)有實(shí)際意義的,而12行是協(xié)處理器的初始化,因為并沒(méi)又模擬協(xié)處理器所以此處只是以備擴展. 重點(diǎn)的初始化過(guò)程是在A(yíng)RMul_NewState(…)中的.首先它給模擬器的核心狀態(tài)結構ARMul_State分配了空間,這個(gè)結構里保存了Armulator的所有方面的狀態(tài),包括arm寄存器,流水線(xiàn)狀態(tài)等等. 并賦予初值,我們以后就用state表示之.然后調用ARMul_Reset(…)進(jìn)行更近一步的設置.而后者又主要完成模擬器內存結構的分配和rom映象的加載--/sim/arm/armmem.c/mem_reset(…),IO設備的狀態(tài)初始―/sim/arm/armio.c/io_reset(…),你也可在這添加你的初始代碼.到這就完成了Armulator的裝載. (大家注意到18行也調用了ARMul_Reset(…),這是一個(gè)BUG,使得模擬器進(jìn)行了兩次內存分配,而浪費了系統內存.此處可刪去.) Memory map 是所有模擬器的關(guān)鍵.Armulator由AT91向其他MCU移植時(shí)Memory map又是首先要處理的.Armulator的各個(gè)內存區是由mem_bank_t結構來(lái)描述的: typedef struct mem_bank_t { 根據mem_banks,mem_reset( )將分配空間,加載boot.rom文件. (原來(lái)的內存是由ARMul_MemoryExit( )釋放的,但補丁后的代碼就沒(méi)了釋放功能,這也是需要糾正的地方) a.指令流 Armulator 加載完成后,就開(kāi)始等待Gdb的運行命令了.最終/sim/wrapper.c/sim_resume( )是啟動(dòng)arm指令執行的地方. Sim_resume( )根據Gdb的要求選擇用/sim/arm/arminit.c/ARMul_DoInstr()還是用/sim/arm/arminit.c/ARMul_DoProg()來(lái)調用 流水線(xiàn)模擬函數/sim/arm/armemu.c/ARMul_Emulate32().ARMul_DoInstr()和ARMul_DoProg()的區別就是一個(gè)單步執行,一個(gè)連續執行指令. ARMul_DoProg()又不停的判斷state->Emulate是否為STOP,如果是,模擬器又將停下等待Gdb的調試. 而在arm/armemu.c, /arm/armvirt.c 和 /arm/armsupp.c中的函數則模擬指令預取,指令譯碼,指令執行以及數據回寫(xiě)的功能.這三個(gè)文件時(shí)可以說(shuō)時(shí)Armulator的核心! b.中斷 Armulator 的中斷機制主要靠以下兩個(gè)例程實(shí)現: 1.IntPending(): 用來(lái)檢測state中的各個(gè)中斷標志是否置位,從而判斷是否又需要中斷. 2.ARMul_Abort():當需要中斷時(shí),用來(lái)改變處理器模式,并將pc指向相應的中斷向量. 在流水線(xiàn)函數ARMul_Emulate32()執行當中,有多處調用IntPending() 去檢測中斷.而Ispending() 也十分簡(jiǎn)單,它僅僅判斷state中的四個(gè)變量: State->Exception : 中斷使能標志. b.讀寫(xiě)操作 無(wú)論是CPU指令還是Gdb調試時(shí)讀寫(xiě)內存或IO空間,最后都將要落到/armvirt.c/getword(), /armvirt.c/putword()這兩個(gè)函數身上. 在原始代碼中這兩個(gè)函數馬上就進(jìn)行內存數組的讀寫(xiě)了.而補丁代碼的流程如下: --getword()/putword() 可以看出最后幾個(gè)函數的選擇是由讀寫(xiě)地址在相應的mem_bank_t結構中的讀寫(xiě)函數指針決定的. c.設備同步 寫(xiě)這篇文章的初衷是讓讀者能很快進(jìn)入Armulator的移植和IO擴展的實(shí)際工作中去.所以這里有必要討論一下IO設備和CPU的同步問(wèn)題.很顯然我們模擬的設備不能太快,也不能太慢.快了CPU正常的指令流將被堵塞,慢了就無(wú)法反映操作系統的實(shí)時(shí)性.也就是說(shuō)設備的速度和指令流要有個(gè)比例關(guān)系,即要有一定的同步. Armulator 中有個(gè)很好的接口: ARMul_ScheduleEvent (ARMul_State * state, unsigned long delay, unsigned (*what) ()) in armvirt.c 它的目的就是注冊你的同步例程,并且每個(gè)時(shí)鐘周期即ARMul_Emulate32()將調用ARMul_ScheduleEvent()查看是否需要同步你的設備. 也許你在A(yíng)RMul_Emulate32()中還發(fā)現了/sim/arm/armio.c/io_do_cycle(),沒(méi)錯它是AT91的timer和uart用來(lái)和指令流同步的函數,但我并不贊成你象這樣把自己的同步例程直接放入指令執行過(guò)程中,破壞代碼的結構性. a. 源文件描述
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tcp/ip相關(guān)文章:tcp/ip是什么
評論