GDB / ARMULATOR source code analysis
Author: Chen Gen
Author email: Anti_chen2000@sohu.com
Summary
GDB / Armulator is a GDB's own ARM7 simulator is a good tool for debugging the ARM program. It is understood that its original code structure is important for extending its IO function. This article introduces the startup from Armulator to its internal operation and Most of the original code feature of IO extensions.
Description
Source code is GDB-5.0.TAR GDB-5.0-Uclinux-armulator-20021127.patch
A. Tongxun between GDB
Armulator is generally coupled to GDB communication, one is to directly call the simulator's related functions inside the GDB, and another method is to connect the RDP protocol with the PIPE or Socket to connect GDB and AMULATOR. The first method is now GDB / Armulator is truly used (the second is the method of early use), and the function direct call method is analyzed below.
Function directly call
This method is to modify the original RDP method by Steve (Sac@cygnus.com). Steve himself is as follows: / ********************** *************************************** This Directory Contains The Standard Release of The Armulator fromadvanced Risc Machines, And Was ftp'd from .
ftp.cl.cam.ac.uk:/Arm/gnu
IT Likes To Use TCP / IP Between The Simulator and The Host, Which Isn, But Is A Pain To Use Under Anything Non-UNIX.
I've added created a new makefile.in (The Original In makefile.orig) To build a Version of the Simulator WITHOUT The TCP / IP stuff, And awrapper.c to link Directly Into gdb and the run command.
IT Should Be Possible (Barring Major Changes in the Layout Offe) To Upgrade The Simulator by Copying All The Filesout of a Release Into this Directory and renaming the makefile.
(Excepter That i change armos.c to work more simply with oursimulator rigs) ******************************************* ********************* / / GDB / TARGET.C, / GDB / Remote_Sim.c and in /sim/arm/wrapper.c is in Armulator and Several files in GDB play a variety of files. All GDB debug commands are finally called by function pointer in the target_ops structure defined in Target.h in /sim/arm/wrapper.c. The SIM_XXX function is complete. Previously these SIM_XXX functions were located in / sim / common, which is the key to establishing RDP communication. After the code is modified, the files in this directory are no longer useful, and it is replaced by Wrapper.c.
The following is an illustration of RDP communication and direct function calls:
To clear the execution process of Armulator, you will start from its startup. When you type target simulator to activate the Amulator, GDB first command line interpretation, and point the current_target pointer to the SIM variable, the armulator debug function set gives GDB Subsequent function call stacks are as follows:
- àgdbsim_open (...) in remote-sim.c .-- àsim_open (...) In /Sim/arm/wrapper.c / * Here Amulator analyzes the call parameters * / - à * current_target-> to_fetch_registers 1) / * This function pointer refers to SIM_FETCH_REGISTER (...) in /sim/arm/wrapper.c*/-- àsim_fetch_register (1) / * This function pointer is to complete the Target_Ops structure when pointing the current_target point to the SIM * / Sim_fetch_register (SD, RN, Memory, Length) {armword regval; init (); file: // In this, Amulator is initialized ...} To this Armulator is loaded, then GDB is through target_ops (definition In the Target.h), each function pointer in the structure is completed to the debugger.
B. Armulator internal mechanism
a. Initialization
From the above, the initialization entry of the entire simulator is the init () function in Wrapper.c, then what is it done?
(Armulator in the original GDB5.0 is analog ARM7DTMI, and the patch code modifies the MEMORY MAP and adds IO capability of Timer and UART to simulate AT91. Because the latter is enhanced by the former, so our analysis Valid)
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. # 1 static void # 2 init ( ) # 3 # 4 static int done; # 5 if (! Done) # 6 {# 7 armul_emulateinit (); file: // call this routine overce to set up the emulator's tables. # 8 state = arMUL_NewState (); # 9 State-> bigendsig = (BIG_ENDIAN? High: Low); # 10 rmul_memoryinit (state, mem_size); file: // Initial in the original code, but now useless # 11 armul_osinit (state); file: // pre-installed system Initialization # 12 armul_coproinit (state); file: // Coproadler initial # 13 state-> verbose = verbosit; # 14 done = 1; # 15 file: // the Below is added for at91 # 16 armul_selectprocessor (state, arm600 ); # 17 armul_setcpsr (state, user32mode); # 18 armul_reset (state); # 19} # 20}
Because this is a patch code, it is inevitable that there is no practical meaning in the actual 10-11 lines of the actual 10-11 line, and 12 lines are the initialization of the coprocessor, because there is no simulation of the coprocessor, so this is just Extension.
The initialization process of the focus is in arrmul_newstate (...). First it gives the simulator's core status structure arrmul_state allocates the space, which saves all aspects of the Armulator, including ARM registers, pipeline state, and more.
And give the initial value, we will use State in the future. Then call armul_reset (...) to make a more step-by-step setting. The latter mainly completes the allocation of the emulator memory structure and the load of the ROM image - / SIM / ARM / Armmem.c / mem_reset (...), IO device status initial - / sim / arm / armio.c / io_reset (...), you can add your initial code here. This completes the loading of Armulator. Everyone pays that 18 lines also calls armul_reset (...), which is a bug that makes the simulator two memory allocations, but wasting the system memory. You can delete it.)
Memory Map is the key to all simulators. When MEMULATOR is transplanted by AT91, the MEMORY MAP is first processed. Each memory area of Hamulator is described by the MEM_BANK_T structure:
typedef struct mem_bank_t {ARMword (* read_word) (ARMul_State * state, ARMword addr); void (* write_word) (ARMul_State * state, ARMword addr, ARMword data); unsigned long addr, len; char * filename;} mem_bank_t; file: // Define the entire memory of ArmMem.h, the array of Armulator, the array of this structure static mem_bank_t mem_banks [] Manage MAP is as follows: static mem_bank_t mem_banks [] = {* the yuk's below area to work around a uClinux / mount options problem * / {real_read_word, real_write_word, 0x01000000, 0x00400000,}, / * 2.4 * / {real_read_word, _write_word, 0x01400000, 0x00400000, "boot.rom"}, {real_read_word, real_write_word, 0x02000000, 0x00400000,}, / * 2.0 * / {real_read_word, real_write_word, 0x02400000, 0x00001000,}, / * yuk! * / {real_read_word, _write_word, 0x04000000, 0x00400000, "boot.rom"}, {real_read_word, real_write_word, 0x00000000, 0x00004000,}, { IO_READ_WORD, IO_WRITE_WORD, 0XF0000000, 0x10000000,}, {Fail_Read_Word, Fail_Write_Word, 0, 0}};
According to MEM_BANKS, MEM_RESET () will allocate space and load boot.rom files.
(The original memory is released by armul_memoryexit (), but the code after the patch has no release function, which is also a place that needs to be corrected)
a directive stream
After the Armulator is loaded, you will start waiting for the GDB to run commands. Final / sim/wrapper.c/sim_resume () is where the ARM instruction is started.
SIM_RESUME () selection according to GDB Select / Sim/arm/arminit.c/armul_doinStr () or /si/arm/arminit.c/armul_doprog () to call the pipeline simulation function / s/Arm/armemu.c/armul_emulate32 (). The difference betweenartul_doinstr () and armul_doprog () is a single step, a continuous execution instruction. Armul_doprog () is constantly judging whether state-> emulate is STOP, if so, the simulator will stop waiting for GDB Debugging. In ARM / Armemu.c, /arm/armvirt.c and /arm/armsupp.c analog instruction prefetch, instruction decoding, instruction execution, and data backup. These three files It can be said to be the core of Armulator!
b. Interrupt
The interrupt mechanism of Armulator is mainly achieved by two routines:
1.INTpending (): Used to detect whether each interrupt flag in State is set, thereby judging whether it is needed.
2.Armul_abort (): When interrupt is required, it is used to change the processor mode and point the PC to the corresponding interrupt vector.
In the execution of the pipeline function armul_emulate32 (), there are many call intreding () to detect interrupts. And ispending () is also very simple, it only determines four variables in State:
State-> Exception: Interrupt Enable Sign .State-> NRESETSIG: RESET interrupt signal .State-> Nirqsig: IRQ interrupt signal .State-> nfiqsig: FIQ interrupt signal. So when your virtual peripherals generate interrupts, you only Call /sim/arm/armio.c/ update_int (): static void update_int (armword requests = state-> oo.intsr & stat-> o.intmr; state-> nfiqsig = (Requests & 0x000f)? Low: high; state-> nirqsig = (Requests & 0xFF0)? Low: high;}
b. Read and write operation
Whether it is the CPU directive or the GDB debugging reads and writes memory or IO space, it will last to fall to /armvirt.c/getword (), /ARMVIRT.C/Putword () Both functions.
These two functions are immediately read and written in the original code. The process of patch code is as follows:
- аetword () / putword () - àMMU_READ_DATA () / MMU_WRITE_DATA () / MMU_WRITE_DATA () IN arMmmmu.c / * Perform MMU's address conversion and cache query * / --à real_read_data () / real_write_data (): Read and write RAM, ROMIO_READ_DATA () / IO_WRITE_DATA (): Read and write IO Space_Write_Data () / Fail_Read_Data () / Fail_Write_Data (): Illegal read and write, such as address errors, ROM read operations, etc.
It can be seen that the selection of the last few functions is determined by the read and write addresses in the corresponding MEM_BANK_T structure.
c. Equipment synchronization
The original intention of writing this article is to let readers can quickly enter the actual work of Armulator's transplant and IO extensions. So it is necessary to discuss the synchronization of IO devices and CPUs. I clearly we simulate equipment can't be too fast, too It can't be too slow. The normal instruction stream of the CPU will be blocked, and the real time of the operating system is unable to reflect the real-time performance of the operating system. This means that there is a proportional relationship of the speed and instruction stream, that is, some synchronization.
Armulator has a good interface:
Armul_scheduleevent (armul_state * state, unsigned long delay, unsigned (* what) ()) in armvirt.c
Its purpose is to register your synchronization routine, and each clock cycle is arMul_emulate32 () will call armbul_scheduleevent () to see if you need to synchronize your device. Maybe you have found / SIM / ARM / ARMIO in Armul_emulate32 (). C / IO_DO_CYCLE (), That is true that the AT91 Timer and UART are used to synchronize with the instruction stream, but I don't agree that you like this to put your synchronization routine directly into the instruction execution process, destroy the structure. Sex.
Source file description
The Original Files
The Modified Codes
File Descriptions
arminit.c
arminit.c
Initial code
Armemu.c
Armemu.c
Instruction stream simulation
armvirt.c
armvirt.c
Memory reading and writing
Armsupp.c
Armsupp.c
Auxiliary instruction stream simulation
Armcopro.c
Armcopro.c
Cooperation device simulation (negligible)
Armos.c
Armos.c
Initial operating system (negligible)
armmem.c
Memory management
Armmmu.c
MMU simulation
Armio.c
IO device simulation
Wrapper.c
Wrapper.c
And GDB communication routines