在嵌入式系统里,主要存在三种调试手段,一是使用硬件的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构成完成输出内核重定位的信息。有了这些调试的函数,就可以提供一个向外输出信息的通道,就可以把黑盒里的信息显示出来大家查看了,跟踪到整个内核到底在做什么事情,解决不正常运行的问题。