nixos-riscv/bootrom/rbootrom.S
Rodrigo Arias Mallo 806023778b Initialize UART in bootrom
Unless the UART is properly initialized, the console won't display any
message until a next stage intializes it (OpenSBI) and then we will
start to see messages after uploading the next bootroms.

Follows the OpenSBI initialization for the UART setting the baud rate to
115200 and assuming a clock of 50 MHz.
2024-08-23 18:13:48 +02:00

190 lines
4.2 KiB
ArmAsm

/*
* Copyright (c) 2024, Barcelona Supercomputing Center (BSC)
* SPDX-License-Identifier: MIT
*
* RBOOTROM v1.0
* Modified by Rodrigo Arias Mallo <rodrigo.arias@bsc.es>
*
* This is a custom bootrom that prints some information to the UART when
* starting, as well as when it hangs. It assumes the UART is at UART_BASE and
* it will jump to DRAM_BASE to continue the boot.
*/
#define DRAM_BASE 0x80000000
#define UART_BASE 0x40001000
#define UART_BAUDRATE 115200
#define UART_CLOCK 50000000
#define UART_BDIV ((UART_CLOCK + 8 * UART_BAUDRATE) / (16 * UART_BAUDRATE))
#define UART_SHIFT 2
#define UART_RBR_OFFSET (0<<UART_SHIFT) /* In: Recieve Buffer Register */
#define UART_THR_OFFSET (0<<UART_SHIFT) /* Out: Transmitter Holding Register */
#define UART_DLL_OFFSET (0<<UART_SHIFT) /* Out: Divisor Latch Low */
#define UART_IER_OFFSET (1<<UART_SHIFT) /* I/O: Interrupt Enable Register */
#define UART_DLM_OFFSET (1<<UART_SHIFT) /* Out: Divisor Latch High */
#define UART_FCR_OFFSET (2<<UART_SHIFT) /* Out: FIFO Control Register */
#define UART_IIR_OFFSET (2<<UART_SHIFT) /* I/O: Interrupt Identification Register */
#define UART_LCR_OFFSET (3<<UART_SHIFT) /* Out: Line Control Register */
#define UART_MCR_OFFSET (4<<UART_SHIFT) /* Out: Modem Control Register */
#define UART_LSR_OFFSET (5<<UART_SHIFT) /* In: Line Status Register */
#define UART_MSR_OFFSET (6<<UART_SHIFT) /* In: Modem Status Register */
#define UART_SCR_OFFSET (7<<UART_SHIFT) /* I/O: Scratch Register */
#define UART_MDR1_OFFSET (8<<UART_SHIFT) /* I/O: Mode Register */
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
#define UART_LSR_DR 0x01 /* Receiver data ready */
.macro PUTC, ch
li a0, \ch
jal putchar
.endm
.section .text.start, "ax", @progbits
.globl _start
_start:
jal uart_init // Initialize console
jal print_hello // Print initial message on HART 0 only
la t0, _hang
csrw mtvec, t0 // Set the machine trap vector
csrr a0, mhartid // Load HART ID into a0
li s0, DRAM_BASE // Load next address into s0
jr s0 // Jump to s0
.section .text.hang, "ax", @progbits
.globl _hang
_hang:
PUTC '\n'
PUTC '\r'
PUTC 'R'
PUTC 'B'
PUTC 'O'
PUTC 'O'
PUTC 'T'
PUTC 'R'
PUTC 'O'
PUTC 'M'
PUTC ' '
PUTC 'H'
PUTC 'A'
PUTC 'N'
PUTC 'G'
PUTC '\n'
PUTC '\r'
/* Hang */
csrr a0, mhartid
1:
wfi
j 1b
.section .text, "ax", @progbits
putchar:
/* Wait until ready to transmit */
la t0, UART_BASE // Load UART base address
1:
lb t1, UART_LSR_OFFSET(t0) // Read Line Status Register
andi t1, t1, UART_LSR_THRE // Transmitter Holding Register Empty
beqz t1, 1b // Repeat if zero (not empty)
/* Write the character now */
sb a0, UART_THR_OFFSET(t0) // Transmit character
ret
uart_init:
/* Assume reg_shift = 2 and reg_width = 2 */
la t0, UART_BASE // Load UART base address
/* From OpenSBI v1.5 lib/utils/serial/uart8250.c */
la t1, 0x00 // Disable all interrupts
sb t1, UART_IER_OFFSET(t0)
la t1, 0x80 // Enable DLAB
sb t1, UART_LCR_OFFSET(t0)
la t1, UART_BDIV & 0xff // Set divisor low byte
sb t1, UART_DLL_OFFSET(t0)
la t1, (UART_BDIV >> 8) & 0xff // Set divisor high byte
sb t1, UART_DLM_OFFSET(t0)
la t1, 0x03 // 8 bits, no parity, one stop bit
sb t1, UART_LCR_OFFSET(t0)
la t1, 0x01 // Enable FIFO
sb t1, UART_FCR_OFFSET(t0)
la t1, 0x00 // No modem control DTR RTS
sb t1, UART_MCR_OFFSET(t0)
/* TODO: Clear line status */
/* TODO: Read receive buffer */
la t1, 0x00 // Set scratchpad to 0
sb t1, UART_SCR_OFFSET(t0)
ret
print_hello:
csrr t0, mhartid // Load HART ID into a0
beq t0, zero, 1f // Print message on HART 0 only
ret
1:
mv s0, ra // Save return address
PUTC '\n' // Identify bootroom
PUTC '\r'
PUTC 'R'
PUTC 'B'
PUTC 'O'
PUTC 'O'
PUTC 'T'
PUTC 'R'
PUTC 'O'
PUTC 'M'
PUTC ' '
PUTC 'v'
PUTC '1'
PUTC '.'
PUTC '0'
PUTC ' '
PUTC ':'
PUTC '^'
PUTC ')'
PUTC '\n'
PUTC '\r'
// Print jumping address
PUTC 'J'
PUTC 'u'
PUTC 'm'
PUTC 'p'
PUTC 'i'
PUTC 'n'
PUTC 'g'
PUTC ' '
PUTC 't'
PUTC 'o'
PUTC ' '
PUTC '0' // TODO: Compute from DRAM_BASE
PUTC 'x'
PUTC '8'
PUTC '0'
PUTC '0'
PUTC '0'
PUTC '_'
PUTC '0'
PUTC '0'
PUTC '0'
PUTC '0'
PUTC '.'
PUTC '.'
PUTC '.'
PUTC '\n'
PUTC '\r'
mv ra, s0 // Restore return address
ret