这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » [求助]请教如何重定位AT91RM9200异常向量表的问题?

共3条 1/1 1 跳转至

[求助]请教如何重定位AT91RM9200异常向量表的问题?

菜鸟
2006-02-18 03:48:44     打赏

各位大侠:

我已经使用U-boot作为bootloader在ATMEL AT91RM9200能够引导起Linux了,现在 想往这个U-boot里加入一段GDB-Stub程序,使得PC主机能够通过GDB和9200开发板(目标机)的stub调试桩交互来实现设置软件断点、单步执行等功能,简要说明一下其原理:(转载某网友的文章)

设计原理
1.1远程调试原理图:



当使用GDB(HOST)调试目标扳(TARGET)上的程序时,HOST需要通过UART或ETHERNET口与TARGET进行通讯,并传输命令;TARGET根据接收的包,解析命令,执行相应的动作,然后上传相应的包。
1.2基于STUB的调试方法原理图:



1.3 STUB的工作原理:
1、 Stub程序驻留在内核中,在系统完全启动之前先启动stub。
2、 Stub通过指定的Debug通讯协议(如GDB远程调试协议)与主机的Debug程序交互。
3、 Stub接管目标机的udef exception handler。
4、 Stub接管对目标机通讯口 (串口、并口或网口)的控制。
5、 实时调试要求目标机支持单步、断点等调试机制。

2、实现方法
2.1 几个主要函数:
在linux的源代码中的stub的主程序名一般为gdb-stub.c。它的主要函数为handle_exception(),它是断点异常的处理函数;breakpoint()的主要任务是启动stub;set_debug_traps()用于初始化stub。

2.2 stub的启动过程:
通常将set_debug_traps()放在irq.c中init_irq()函数的末尾,其后紧跟着breakpoint()。set_debug_traps()函数一直扫描串口(如果通过串口调试的话),直到接收到HOST发出的同步信号(即两个字符:’$’和’#’),然后退出。breakpoint()中是一些汇编指令,其中包括一个断点指令,当CPU执行了这个断点指令时,就会产生一个断点异常,从而将控制权交给stub。
Stub接到控制权后,会运行handle_exception(),从而与HOST进行通讯。具体的操作可以看源代码,很容易看懂。

2.3 断点指令
不同的CPU有不同的断点指令,它是一个汇编指令:mips的是break,它会产生一个断点异常。而对于ARM,在ARMv5及其以上版中有一个断点指令BKPT,而在低版本的指令集中,就没有该指令。解决方法是需要使用一个非定义指令代替断点指令,非定义指令就是ARM指令集中没有的指令,它会产生一个非定义指令(udef)异常,从而达到断点异常的效果。

对于ARM的breakpoint(),它的代码如下:
void breakpoint(void)
{
int i, j;
if (!initialized)
return;
__asm__ __volatile__("
.globl breakinst
nop
breakinst: nop
nop
nop
");
}


当然,这个代码不能产生异常,这就需要在set_debug_traps()添加一行代码:

void set_debug_traps(void)
{
…… //不知道在这里做了些什么?
*(unsigned long *)breakinst = BP;
……
}
其中,BP就是一个非定义指令:
#define BP 0xe7fddefe

这样在初始化时,执行完set_debug_traps(void)后,进入void breakpoint(void)就会产生一个udef异常,从而将控制权交给stub。
2.4 异常处理函数
其实handle_exception()是由底层由汇编指令写成的异常处理函数调用的。对于mips,它的底层处理函数是由gdb-low.S实现的。它的主函数是trap_low,它的功能是:当发生断点异常时,将所有寄存器存入堆栈中,并以此作为参数调用handle_exception(),当从函数handle_exception()返回时,再恢复原来的寄存器。
对于ARM来说,它没有断点异常,这就需要在它的udef异常处理函数do_undefinstr()中加入一些检查和调用handle_exception()的语句。

请教ARM的udef异常的底层处理函数该怎么写!?

2.5 设置断点和单步
调试程序时,最重要的莫过于设置断点和单步执行。而单步的最终的工作也就是设置断点。在stub中,设置断点时,要先将指定地址处的数据保存在一个变量中,然后再将该处替换成断点指令。当从异常处理函数返回时,内核继续运行,当运行到断点指令时,又会进入handle_exception(),handle_exception()首先会将原来的指令恢复到断点处,然后等待HOST的命令。具体的操作可以看源代码。

3、操作方法
1) 下载GDB的源代码,如gdb-5.0
2) 执行如下指令:
cd gdb-5.0
./configure --target=arm-linux
make(生成gdb)
make install(生成arm-linux-gdb,并存入/usr/local/bin/)
3) 将内核Image下载到目标板上,并运行。
4) 运行如下指令:
/usr/local/bin/arm-linux-gdb –b 9600 vmlinux
set remotedevice /dev/ttyS1
set debug remote 1(可选)
target remote /dev/ttyS1(当stub通过UART1打出提示信息后再运行该命令)
5) 运行GDB命令进行调试。

4、串行协议
在gdb的info页中有串行协议的详细解释。

由于时间仓促,不足之处还请各位指教!

但是遇到一个如何设置异常向量表的问题,就是想利用异常向量表中的未定义指令异常Undefined instruction 来产生未定义指令异常中断,从而转去执行handle_exception()程序。如何实现异常向量表的重定位(Flash —〉SDRAM)?U-boot中Start.S源代码:

#include "config.h"
#include "version.h"


/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/


.globl _start
_start: b reset
ldr pc, _undefined_instruction //如何利用这条指令
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq

_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq

.balignl 16,0xdeadbeef


/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/

/*
* CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h)
*/
_TEXT_BASE:
.word TEXT_BASE

.globl _armboot_start
_armboot_start:
.word _start

/*
* Note: _armboot_end_data and _armboot_end are defined
* by the (board-dependent) linker script.
* _armboot_end_data is the first usable FLASH address after armboot
*/
.globl _armboot_end_data
_armboot_end_data:
.word armboot_end_data
/*
* Note: armboot_end is defined by the (board-dependent) linker script
*/
.globl _armboot_end
_armboot_end:
.word armboot_end

/*
* _armboot_real_end is the first usable RAM address behind armboot
* and the various stacks
*/
.globl _armboot_real_end
_armboot_real_end:
.word 0x0badc0de

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif


/*
* the actual reset code
*/

reset:

/*
* set the cpu to SVC32 mode */
mrs r0,cpsr

bic r0,r0,#0x1f
orr r0,r0,#0x13

msr cpsr,r0

/*
* relocate exeception table */以下这一段时干什么用的?它怎么就能实现relocate exeception table 功能呢?


ldr r0, =_start
ldr r1, =0x0
mov r2, #16 //R2=16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex

/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
#endif

/* set up the stack */
ldr r0, _armboot_end

add r0, r0, #CONFIG_STACKSIZE
sub sp, r0, #12 /* leave 3 words for abort-stack */
ldr pc,_start_armboot

_start_armboot: .word start_armboot

/*
*************************************************************************
*
* CPU_init_critical registers
*
*************************************************************************
*/

cpu_init_crit:
# actually do nothing for now!
mov pc, lr
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/

@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72

#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52

#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0

#define MODE_SVC 0x13
#define I_BIT 0x80

/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/

.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC

ldr r2, _armboot_end
add r2, r2, #CONFIG_STACKSIZE
sub r2, r2, #8
ldmia r2, {r2 - r4} @ get pc, cpsr, old_r0
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC

add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_r
mov r0, sp
.endm

.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm

.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm

.macro get_bad_stack
ldr r13, _armboot_end @ setup our mode stack
add r13, r13, #CONFIG_STACKSIZE @ resides at top of normal stack
sub r13, r13, #8

str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]

mov r13, #MODE_SVC @ prepare SVC-Mode
msr spsr_c, r13
mov lr, pc
movs pc, lr
.endm

.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm

.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm

/*
* exception handlers
*/
.align 5
undefined_instruction: //想让这里转去执行自己的程序该做些什么呢?
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction

.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt

.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort

.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort

.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used

#ifdef CONFIG_USE_IRQ

.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs

.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs

#else

.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq

.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq

#endif

.align 5
.globl reset_cpu
reset_cpu:
mov pc, r0

请AT91高手多多指教,谢谢!




关键词: 求助     请教     如何     定位     AT91RM9200     异常    

菜鸟
2006-02-21 02:55:00     打赏
2楼

at91arm版主可否给解决一下此问题呢?关于GDB-STUB的内容补充:

http://www.hhcn.org/gdb.html里有些关于GDB-STUB的功能介绍的帖子。
http://www.hhcn.com/cgi-bin/topic.cgi?forum=2&topic=2576&show=0
http://www.ruijitek.com/bbs/dispbbs.asp?boardID=17&ID=114&page=1
http://www.ruijitek.com/bbs/dispbbs.asp?boardID=17&ID=115&page=1

谢谢大家!


菜鸟
2006-02-23 03:59:00     打赏
3楼
大家好!

当我使用JTAG配合ADS在AT91RM9200上测试一个小程序时单步执行了几步后出现了死机现象,重新启动后,打开超级终端后开发板就一点反应都没有了,好像Flash零地址没有了启动代码,这是什么原因呢?更奇怪的是我通过超级终端下Xmodem重新烧写引导代码也出现了错误。
我的9200板子在烧写boot.bin都出如下错误信息:
Uboot> xdownload 20000000
CCCCCCCC
xmodem done.
Uboot> fl 10000000 20000000 10000

start_sect=0x0,end_sect=0x0
************erase sector 0x0***********
----------program sector 0x0---------
Error Command Sequence!mflash_program_sector error : status read
flash not completed for error
Uboot>
这应该是Falsh的第一扇区坏了吧?使用JTAG后怎么会把Flash零地址的内容冲掉呢?而且又怎么会烧损Flash的第一扇区呢?其他扇区是完好的。请高手讲解原因。

谢谢!


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]