When I bring up a bare board, I have to fight without OS or libraries, or sometimes even without RAM. Most conservative solution in such case is ICE. But I do not like it because
- ICE is expensive.
- ICE is no use once debug monitor is up.
- ICE is usually hairy and buggy.
I use simple print to the serial I/O console because, to me, the most important information is where the program is and its status. In the boot code, the first thing I do is to initialize the serial I/O, and print at least one character, which shows the board got out of reset and started fetching instructions.
The boot code goes like this:
#include "mydebug.h"
// Initialize serial I/O.
la t0, SIO_BASE
li t1, ..... // baud rate, enable, etc.
li t2, .....
sw t1, PARAM1(t0)
sw t2, PARAM2(t0)
// Show the first message.
la t0, SIO_TXFIFO
li t1, 'H'
sw t1, (t0)
li t1, 'i'
sw t1, (t0)
li t1, 0x0d
sw t1, (t0)
li t1, 0x0a
sw t1, (t0)
// Test mydebug functions.
MYPRINTS("MYPRINTS: Hello world.\r\n")
MYPRINTS("MYPRINTD: ")
MYPRINTD(1234567890)
MYCRLF()
MYMARKER()
The output looks like this:
Hi MYPRINTS: Hello world. MYPRINTD: 1234567890 [reset.S:32]
The last line is from MYMARKER() macro, which I think most useful.
Once the program jumps to C, I can use a bit better ones.
#include "mydebug.h"
int main()
{
MYPRINTS("MYPRINTS: Hello from C.\r\n");
MYPRINTS("MYPRINTD: ")
MYPRINTD(1234567890)
MYCRLF()
MYMARKER()
}
The output looks like this:
MYPRINTS: Hello from C. MYPRINTD: 1234567890 [main.c:52 main()]
Here is mydebug.h. Use it at your own risk.
//
// For assembler program.
//
#ifdef __ASSEMBLER__
#define MYPRINTC(C) \
li t0, SIO_TXFIFO \
li t1, C; \
sw t1, (t0); \
#define MYCRLF() \
MYPRINTC(0x0d); \
MYPRINTC(0x0a)
#define MYPRINTS(S) \
b 1111f; \
nop; \
8888: .asciz S; \
.align 2; \
1111: la t2, 8888b; \
la t0, SIO_TXFIFO \
2222: lb t1, (t2); \
beqz t1, 9999f; \
nop; \
sw t1, (t0); \
addi t2, 1; \
b 2222b; \
nop; \
9999:
#define MYPRINTD(V) \
li t0, SIO_TXFIFO \
li t2, V /* value */; \
li t3, 1000000000 /* divisor */; \
li t4, 10; \
li t5, 1; /* leading zero flag */; \
1111: divu t2, t3; \
mflo t1 /* digit */; \
mfhi t2 /* mod */; \
beqz t5, 3333f; \
nop; \
beqz t1, 2222f; \
nop; \
move t5, zero; \
3333: addi t1, '0'; \
sw t1, (t0); \
2222: divu t3, t4; \
mflo t3; \
bnez t3, 1111b; \
nop
#define MYMARKER() \
MYPRINTC('['); \
MYPRINTS(__FILE__); \
MYPRINTC(':'); \
MYPRINTD(__LINE__); \
MYPRINTC(']'); \
MYCRLF()
//
// For C program.
//
#else // #ifdef __ASSEMBLER__
#define MYPRINTC(C) { \
*(volatile unsigned int*)SIO_TXFIFO = C; \
}
#define MYPRINTD(V) { \
char buf[20]; \
int n = 0; \
unsigned int val = (V); \
do { \
unsigned int digit = val % 10; \
buf[n++] = '0' + digit; \
val /= 10; \
} while (val); \
while (n > 0) MYPRINTC(buf[--n]); \
}
#define MYPRINTS(S) { \
const char* p = S; \
while (*p) MYPRINTC(*p++); \
}
#define MYMARKER() { \
MYPRINTS("["); \
MYPRINTS(__FILE__); \
MYPRINTS(":"); \
MYPRINTD(__LINE__); \
MYPRINTS(" "); \
MYPRINTS(__PRETTY_FUNCTION__); \
MYPRINTS("()]\r\n"); \
}
#endif // #ifdef __ASSEMBLER__