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 のみです。
printunsignedprintsigned はオリジナルで作りました。

これら用途としては、ブートのかなり初期で、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