RAM を一切使わずにプリントデバッグ
10 May 2013
Linux Kernel のコードを見ていると、 arch/arm/kernel/debug.S
にかなりローレベルなプリントデバッグ用のサブルーチンがありますね。
これをちょっと真似して、より少ない資源で、ARM の起動初期のプリントデバッグができるように改良したものを作ってみました。
printhex2
: 2桁の16進数を表示するprinthex4
: 4桁の16進数を表示するprinthex8
: 8桁の16進数を表示するprintunsigned
: unsigned な10進数を表示するprintsigned
: signed な10進数を表示するprintascii
: 文字列表示printch
: 一文字表示
表示したい数値もしくは文字列を r0
にセットして、上記のマクロを call すれば使えます。
printhex2
, printhex4
, printhex8
, printascii
, printch
は Linux Kernel にもあるのですが、Linux Kernel に含まれているバージョンとの違いは RAM を一切使用せず、破壊するレジスタは r0
, r1
, r2
, r3
のみです。
printunsigned
と printsigned
はオリジナルで作りました。
これら用途としては、ブートのかなり初期で、RAM もまだ使えない状態で、簡単にプリントデバッグしたいときのために作ったものなのです。
(LEDぐらいしか点灯しないとなるとかなりデバッグが苦しいですので。)
私は U-Boot で lowlevel_init
関数とかのデバッグ用途に使っていました。
以下、コードです。
レジスタビューは 16550 を想定してます。
最初に early_serial_init
を call する必要があります。
DEBUG_UART_BASE
, UART_SHIFT
, DIVISOR
あたりは自分の環境に合わせて変更して下さい。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#define DEBUG_UART_BASE 0xXXXXXXXXX
#define UART_SHIFT 1
/*
* DIVISER = ((BAUDCLK / 8 / BAUDRATE + 1) / 2)
*/
//#define DIVISOR 40 /* 19200 bps */
//#define DIVISOR 20 /* 38400 bps */
#define DIVISOR 7 /* 115200 bps */
#define UART_TX 0 /* Out: Transmit buffer */
#define UART_LCR 3 /* Out: Line Control Register */
#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
#define UART_LSR 5 /* In: Line Status Register */
#define UART_LSR_TEMT 0x40 /* Transmitter empty */
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
/*
* DLAB=1
*/
#define UART_DLL 0 /* Out: Divisor Latch Low */
#define UART_DLM 1 /* Out: Divisor Latch High */
/*
* Call the routine once
* before calling printhex8/4/2, printascii, printch.
*/
ENTRY(early_serial_init)
addruart r3
mov r0, #UART_LCR_DLAB
strb r0, [r3, #UART_LCR << UART_SHIFT]
mov r0, #(DIVISOR & 0xff) @ LSB of divisor
strb r0, [r3, #UART_DLL << UART_SHIFT]
mov r0, #((DIVISOR >> 8) & 0xff) @ MSB of divisor
strb r0, [r3, #UART_DLM << UART_SHIFT]
mov r0, #UART_LCR_WLEN8
strb r0, [r3, #UART_LCR << UART_SHIFT]
mov pc, lr
ENDPROC(early_serial_init)
.macro addruart, rx
ldr \rx, =DEBUG_UART_BASE
.endm
.macro senduart,rd,rx
strb \rd, [\rx, #UART_TX << UART_SHIFT]
.endm
.macro busyuart,rd,rx
1002: ldrb \rd, [\rx, #UART_LSR << UART_SHIFT]
and \rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE
teq \rd, #UART_LSR_TEMT | UART_LSR_THRE
bne 1002b
.endm
/*
* Useful debugging routines
*
* printhex8, printhex4, printhex2:
* print a number in 8, 4, 2 digits hexadecimal, respectively.
* r0 = number
* r1-r3 corrupted
*
* printunsigned, printsigned
* print a number in decimal.
* r0 = number
* r1-r3 corrupted
*
* printascii
* print a string
* r0 = pointer to string
* r0-r3 corrupted
*
* printch
* print a character
* r0 = character code
* r0-r3 corrupted
*/
ENTRY(printhex8)
mov r2, #28
b printhex
ENDPROC(printhex8)
ENTRY(printhex4)
mov r2, #12
b printhex
ENDPROC(printhex4)
ENTRY(printhex2)
mov r2, #4
printhex:
addruart r3
1: mov r1, r0, lsr r2
and r1, r1, #15
cmp r1, #10
addlt r1, r1, #'0'
addge r1, r1, #'a' - 10
senduart r1, r3
busyuart r1, r3
subs r2, r2, #4
bge 1b
mov pc, lr
ENDPROC(printhex2)
ENTRY(printunsigned)
mov r1, #1
mov r2, #0
1: mov r1, r1, lsl #1 @ r1 = 2 * r1
add r1, r1, r1, lsl #2 @ r1 = 5 * r1
add r2, r2, #1
cmp r2, #10 @ prevent overflow of r1
bge 2f
cmp r1, r0
bls 1b
2: mov r3, r2 @ number of digits
3: mov r1, #1
mov r2, #0
4: add r2, r2, #1
cmp r2, r3
movlt r1, r1, lsl #1 @ r1 = 2 * r1
addlt r1, r1, r1, lsl #2 @ r1 = 5 * r1
blt 4b
mov r2, #0
5: cmp r1, r0
subls r0, r0, r1
addls r2, r2, #1
blo 5b
add r2, r2, #'0'
addruart r1
senduart r2, r1
busyuart r2, r1
subs r3, r3, #1
bgt 3b
mov pc, lr
ENDPROC(printunsigned)
ENTRY(printsigned)
cmp r0, #0
bge printunsigned @ signed greater or equal
addruart r3
mov r1, #'-'
senduart r1, r3
busyuart r1, r3
rsb r0, r0, #0 @ r0 = 0 - r0
b printunsigned
ENDPROC(printsigned)
ENTRY(printascii)
addruart r3
b 2f
1: senduart r1, r3
busyuart r2, r3
teq r1, #'\n'
moveq r1, #'\r'
beq 1b
2: teq r0, #0
ldrneb r1, [r0], #1
teqne r1, #0
bne 1b
mov pc, lr
ENDPROC(printascii)
ENTRY(printch)
addruart r3
mov r1, r0
mov r0, #0
b 1b
ENDPROC(printch)
blog comments powered by Disqus