在嵌入式系统里,主要存在三种调试手段,一是使用硬件的LED显示灯,这种方式最原始,也最简单,只需要一个高低电平,就可以表示什么状态了,比如电源状态灯,网络连接灯等。二是使用串口通讯调试输出,由于串口通讯设置的参数最简单,连接线也最简单,编码也最简单,更何况在目前计算机环境里,没有串口基本不可能。三是使用调试器,比如JTAG等,一般比较复杂一些,需要硬件也多一些。在内核开发,或者嵌入式系统,最好使用串口调试输出,因为这些对多个CPU运行时,可以准确地输出,使用JTAG就不一定了。
#ifdefined(CONFIG_ARCH_SA1100)
.macro loadsp,rb
mov \rb,#0x80000000 @ physical base address
#ifdefCONFIG_DEBUG_LL_SER3
add \rb,\rb, #0x00050000 @ Ser3
#else
add \rb,\rb, #0x00010000 @ Ser1
#endif
.endm
这段代码主要用来计算串口的物理地址,以便可以选择不同的串口进行调试的操作。loadsp是宏的名称,rb是输入的参数,然后计算好的参数同样放在rb寄存器里返回。
#elifdefined(CONFIG_ARCH_S3C2410)
.macroloadsp, rb
mov \rb,#0x50000000
add \rb,\rb, #0x4000 * CONFIG_S3C_LOWLEVEL_UART_PORT
.endm
这段代码也是同上面的一样,计算选择不同的串口输入输出调试。
#else
.macro loadsp, rb
addruart\rb
.endm
这段代码里调用了一个宏addruart,使用这个宏来计算串口的地址,这个宏是来自下面的文件:
arch/arm/mach-s3c6400/include/mach/debug-macro.S
宏addruart是通过协处理器p15来判断选择不同的调试串口输出输入的。
#endif
#endif
#endif
到这里就完成了选择专用的调试输出,还是使用串口输出的工作。编写这段代码的意义就是可以根据不同的架构,不同的CPU类型,统计输出调试信息的地址。
.macro kputc,val
mov r0,\val
bl putc
.endm
这个宏kputc是调用putc来输出字符。
.macro kphex,val,len
mov r0,\val
mov r1,#\len
bl phex
.endm
这个宏kphex输出16进制格式内容。
.macro debug_reloc_start
#ifdefDEBUG
kputc #'\n'
kphex r6,8 /* processor id */
kputc #':'
kphex r7,8 /* architecture id */
#ifdefCONFIG_CPU_CP15
kputc #':'
mrc p15,0, r0, c1, c0
kphex r0,8 /* control reg */
#endif
kputc #'\n'
kphex r5,8 /* decompressed kernel start */
kputc #'-'
kphex r9,8 /* decompressed kernel end */
kputc #'>'
kphex r4,8 /* kernel execution address */
kputc #'\n'
#endif
.endm
这个宏debug_reloc_start是用来输出CPU的ID,CPU架构,以及解压重定位的信息。
.macro debug_reloc_end
#ifdefDEBUG
kphex r5,8 /* end of kernel */
kputc #'\n'
mov r0,r4
bl memdump /*dump 256 bytes at start of kernel */
#endif
.endm
这个宏debug_reloc_end是与上面的宏debug_reloc_start构成完成输出内核重定位的信息。有了这些调试的函数,就可以提供一个向外输出信息的通道,就可以把黑盒里的信息显示出来大家查看了,跟踪到整个内核到底在做什么事情,解决不正常运行的问题。