Configure miniUART to print characters to terminal
From rpi3 docs, IO physical address started from 0x3F000000.
Physical addresses range from 0x3F000000 to 0x3FFFFFFF for peripherals. The bus
addresses for peripherals are set up to map onto the peripheral bus address range
starting at 0x7E000000. Thus a peripheral advertised here at bus address
0x7Ennnnnn is available at physical address 0x3Fnnnnnn.
And the docs said miniuart register virtual address is at:
Address | Reg Name | Description |
---|---|---|
0x7E21 5000 | AUX_IRQ | Auxiliary Interrupt status |
0x7E21 5004 | AUX_ENABLES | Auxiliary enables |
0x7E21 5040 | AUX_MU_IO_REG | Mini Uart I/O Data |
So we define the registers we need for miniuart:
/* ARM aux peripheral physical address start */
#define BASE_ADDRESS 0x3f000000
#define AUX_IRQ (volatile unsigned int*) (0x7E215000 - BASE_ADDRESS)
#define AUX_ENABLES (volatile unsigned int*) (0x7E215004 - BASE_ADDRESS)
#define AUX_MU_IO_REG (volatile unsigned int*) (0x7E215040 - BASE_ADDRESS)
#define AUX_MU_IER_REG (volatile unsigned int*) (0x7E215044 - BASE_ADDRESS)
#define AUX_MU_IIR_REG (volatile unsigned int*) (0x7E215048 - BASE_ADDRESS)
#define AUX_MU_LCR_REG (volatile unsigned int*) (0x7E21504C - BASE_ADDRESS)
#define AUX_MU_MCR_REG (volatile unsigned int*) (0x7E215050 - BASE_ADDRESS)
#define AUX_MU_LSR_REG (volatile unsigned int*) (0x7E215054 - BASE_ADDRESS)
#define AUX_MU_MSR_REG (volatile unsigned int*) (0x7E215058 - BASE_ADDRESS)
#define AUX_MU_SCRATCH (volatile unsigned int*) (0x7E21505C - BASE_ADDRESS)
#define AUX_MU_CNTL_REG (volatile unsigned int*) (0x7E215060 - BASE_ADDRESS)
#define AUX_MU_STAT_REG (volatile unsigned int*) (0x7E215064 - BASE_ADDRESS)
#define AUX_MU_BAUD_REG (volatile unsigned int*) (0x7E215068 - BASE_ADDRESS)
Initialize uart
void
uartinit(void)
{
*AUX_ENABLES |= 1; /* Enable mini UART */
*AUX_MU_CNTL_REG = 0; /* Disable transmitter and receiver during configuration */
*AUX_MU_LCR_REG = 3; /* 8-bit mode */
*AUX_MU_MCR_REG = 0; /* Disable RTS line */
*AUX_MU_IER_REG = 0; /* Disable interrupts */
*AUX_MU_IIR_REG = 6; /* Clear receive/transmit FIFO */
*AUX_MU_BAUD_REG = 270; /* Set baud rate to 115200 */
*AUX_MU_CNTL_REG = 3; /* Enable transmitter and receiver */
}
Then we write uartputc
and uartgetc
to interact with uart:
/**
* Write to uart
*
* Check AUX_MU_LSR_REG’s Transmitter empty field.
* If set, write to AUX_MU_IO_REG
*/
void
uartputc(int c)
{
while(!(*AUX_MU_LSR_REG & 0x20));
*AUX_MU_IO_REG = c;
}
/**
* Read from uart
*
* Check AUX_MU_LSR_REG’s data ready field.
* If set, read from AUX_MU_IO_REG
*/
int
uartgetc(void)
{
while(!(*AUX_MU_LSR_REG & 0x01));
int c = *AUX_MU_IO_REG;
c = (c == '\r') ? '\n' : c;
return c;
}
Add code to start.c
void
start()
{
uartputc('j');
uartputc('a');
uartputc('c');
uartputc('k');
}
Remember to compile uart.c
:
SRCS = start.c uart.c
Add -serial null -serial stdio
to qemu. Because default qemu will output stdin/stdout to uart0,
we need to connect stdin/stdout to miniuart(uart1).
qemu-system-aarch64 -M raspi3b -kernel kernel.img -display none -serial null -serial stdio
Finally we can see the output on the terminal:
$ make qemu
qemu-system-aarch64 -M raspi3b -kernel kernel.img -display none -serial null -serial stdio
jack