From 11a95bd16da3ba212ebbd64300c9136cee555bc1 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 7 Sep 2021 12:52:57 +0200 Subject: Cleanup UART code using structs for registers Resources: - https://www.youtube.com/watch?v=58f8Qfh-T6Q --- src/common.h | 219 +++++++++++++++++++++++++++++++++++------------------------ src/main.c | 2 +- src/start.s | 8 +++ 3 files changed, 141 insertions(+), 88 deletions(-) diff --git a/src/common.h b/src/common.h index c306c6c..a6af346 100644 --- a/src/common.h +++ b/src/common.h @@ -1,102 +1,147 @@ +#ifndef COMMON_RPIBM_H +#define COMMON_RPIBM_H + // Header definitions for memory mapped IO and common utilities. #include "shorthand.h" +// +// Utils. +// + +// Wait for N processor cycles before returning. Implemented in start.s. +void delay(u64 ticks); + +// Clear N bytes at the given address. Implemented in start.s. +void memzero(u64 src, u32 n); + #define MEM_BASE 0x3F000000 -// GPIO registers. -#define GPIO_GPFSEL0 ((vu32*)(MEM_BASE + 0x00200000)) -#define GPIO_GPFSEL1 ((vu32*)(MEM_BASE + 0x00200004)) -#define GPIO_GPFSEL2 ((vu32*)(MEM_BASE + 0x00200008)) -#define GPIO_GPFSEL3 ((vu32*)(MEM_BASE + 0x0020000C)) -#define GPIO_GPFSEL4 ((vu32*)(MEM_BASE + 0x00200010)) -#define GPIO_GPFSEL5 ((vu32*)(MEM_BASE + 0x00200014)) -#define GPIO_GPSET0 ((vu32*)(MEM_BASE + 0x0020001C)) -#define GPIO_GPSET1 ((vu32*)(MEM_BASE + 0x00200020)) -#define GPIO_GPCLR0 ((vu32*)(MEM_BASE + 0x00200028)) -#define GPIO_GPLEV0 ((vu32*)(MEM_BASE + 0x00200034)) -#define GPIO_GPLEV1 ((vu32*)(MEM_BASE + 0x00200038)) -#define GPIO_GPEDS0 ((vu32*)(MEM_BASE + 0x00200040)) -#define GPIO_GPEDS1 ((vu32*)(MEM_BASE + 0x00200044)) -#define GPIO_GPHEN0 ((vu32*)(MEM_BASE + 0x00200064)) -#define GPIO_GPHEN1 ((vu32*)(MEM_BASE + 0x00200068)) -#define GPIO_GPPUD ((vu32*)(MEM_BASE + 0x00200094)) -#define GPIO_GPPUDCLK0 ((vu32*)(MEM_BASE + 0x00200098)) -#define GPIO_GPPUDCLK1 ((vu32*)(MEM_BASE + 0x0020009C)) - -// Auxiliary registers (Mini UART and SPI). -#define AUX_ENABLE ((vu8*) (MEM_BASE + 0x00215004)) -#define AUX_MU_IO ((vu8*) (MEM_BASE + 0x00215040)) -#define AUX_MU_IER ((vu8*) (MEM_BASE + 0x00215044)) -#define AUX_MU_IIR ((vu8*) (MEM_BASE + 0x00215048)) -#define AUX_MU_LCR ((vu8*) (MEM_BASE + 0x0021504C)) -#define AUX_MU_MCR ((vu8*) (MEM_BASE + 0x00215050)) -#define AUX_MU_LSR ((vu8*) (MEM_BASE + 0x00215054)) -#define AUX_MU_MSR ((vu8*) (MEM_BASE + 0x00215058)) -#define AUX_MU_SCRATCH ((vu8*) (MEM_BASE + 0x0021505C)) -#define AUX_MU_CNTL ((vu8*) (MEM_BASE + 0x00215060)) -#define AUX_MU_STAT ((vu32*)(MEM_BASE + 0x00215064)) -#define AUX_MU_BAUD ((vu16*)(MEM_BASE + 0x00215068)) - -/** - * Set baud rate and characteristics (115200 8N1) and map to GPIO - */ -void uart_init() -{ - register unsigned int r; - - /* initialize UART */ - *AUX_ENABLE |=1; // enable UART1, AUX mini uart - *AUX_MU_CNTL = 0; - *AUX_MU_LCR = 3; // 8 bits - *AUX_MU_MCR = 0; - *AUX_MU_IER = 0; - *AUX_MU_IIR = 0xc6; // disable interrupts - *AUX_MU_BAUD = 270; // 115200 baud - /* map UART1 to GPIO pins */ - r=*GPIO_GPFSEL1; - r&=~((7<<12)|(7<<15)); // gpio14, gpio15 - r|=(2<<12)|(2<<15); // alt5 - *GPIO_GPFSEL1 = r; - *GPIO_GPPUD = 0; // enable pins 14 and 15 - r=150; while(r--) { asm volatile("nop"); } - *GPIO_GPPUDCLK0 = (1<<14)|(1<<15); - r=150; while(r--) { asm volatile("nop"); } - *GPIO_GPPUDCLK0 = 0; // flush GPIO setup - *AUX_MU_CNTL = 3; // enable Tx, Rx +// +// GPIO Registers. +// + +typedef struct GpioPinData { + vu32 reserved; + vu32 data[2]; +} GpioPinData; + +typedef struct GpioRegs { + vu32 func_select[6]; + GpioPinData output_set; + GpioPinData output_clear; + GpioPinData level; + GpioPinData ev_detect_status; + GpioPinData re_detect_enable; + GpioPinData fe_detect_enable; + GpioPinData hi_detect_enable; + GpioPinData lo_detect_enable; + GpioPinData async_re_detect; + GpioPinData async_fe_detect; + vu32 reserved; + vu32 pupd_enable; + vu32 pupd_enable_clocks[2]; +} GpioRegs; + +typedef enum GpioFunc { + GPIO_INPUT = 0, + GPIO_OUTPUT = 1, + GPIO_ALT0 = 4, + GPIO_ALT1 = 5, + GPIO_ALT2 = 6, + GPIO_ALT3 = 7, + GPIO_ALT4 = 3, + GPIO_ALT5 = 2, +} GpioFunc; + +#define GPIO ((GpioRegs *)(MEM_BASE + 0x00200000)) + +static inline void +gpio_pin_set_func(u8 pin, GpioFunc func) { + u8 start = (pin * 3) % 30; + u8 reg = pin / 10; + + u32 selector = GPIO->func_select[reg]; + selector &= ~(7 << start); + selector |= (func << start); + + GPIO->func_select[reg] = selector; } -/** - * Send a character - */ -void uart_send(unsigned int c) { - /* wait until we can send */ - do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x20)); - /* write the character to the buffer */ - *AUX_MU_IO=c; +static inline void +gpio_pin_enable(u8 pin) { + GPIO->pupd_enable = 0; + delay(150); + GPIO->pupd_enable_clocks[pin / 32] = 1 << (pin % 32); + delay(150); + GPIO->pupd_enable = 0; + GPIO->pupd_enable_clocks[pin / 32] = 0; } -/** - * Receive a character - */ -char uart_getc() { - char r; - /* wait until something is in the buffer */ - do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); - /* read it and return */ - r=(char)(*AUX_MU_IO); - /* convert carrige return to newline */ - return r=='\r'?'\n':r; +// +// AUX Registers. +// + +typedef struct AuxRegs { + vu32 irq_status; + vu32 enables; + vu32 reserved[14]; + vu32 mu_io; + vu32 mu_ier; + vu32 mu_iir; + vu32 mu_lcr; + vu32 mu_mcr; + vu32 mu_lsr; + vu32 mu_msr; + vu32 mu_scratch; + vu32 mu_control; + vu32 mu_status; + vu32 mu_baud_rate; +} AuxRegs; + +#define AUX ((AuxRegs *)(MEM_BASE + 0x00215000)) + +static inline void +uart_init() { + gpio_pin_set_func(14, GPIO_ALT5); + gpio_pin_set_func(15, GPIO_ALT5); + gpio_pin_enable(14); + gpio_pin_enable(15); + + AUX->enables = 1; + AUX->mu_control = 0; + AUX->mu_ier = 0; + AUX->mu_lcr = 3; + AUX->mu_mcr = 0; + + AUX->mu_baud_rate = 270; // RPI3: 115200 @ 250 MHz + // AUX->mu_baud_rate = 541; // RPI4: 115200 @ 500 MHz + AUX->mu_control = 3; + return; } -/** - * Display a string - */ -void uart_puts(char *s) { +static inline void +uart_putc(char c) { + while(!(AUX->mu_lsr & (1 << 5))); + AUX->mu_io = c; + return; +} + +static inline char +uart_getc() { + while(!(AUX->mu_lsr & 1)); + u8 c = AUX->mu_io & 0xFF; + return c == '\r' ? '\n' : c; +} + +static inline void +uart_puts(char *s) { while(*s) { - /* convert newline to carrige return + newline */ - if(*s=='\n') - uart_send('\r'); - uart_send(*s++); + if(*s == '\n') { + uart_putc('\r'); + } + uart_putc(*s++); } } + +#endif // COMMON_RPIBM_H diff --git a/src/main.c b/src/main.c index da354c1..aac605b 100644 --- a/src/main.c +++ b/src/main.c @@ -8,6 +8,6 @@ void main(void) { // Echo input to standard output. while(1) { - uart_send(uart_getc()); + uart_putc(uart_getc()); } } diff --git a/src/start.s b/src/start.s index b142397..f6598ce 100644 --- a/src/start.s +++ b/src/start.s @@ -10,12 +10,20 @@ _start: b halt // Helper function to clear x1 bytes starting at the x0 memory address. +.globl memzero memzero: str xzr, [x0], #8 subs x1, x1, #8 b.gt memzero ret +// Helper function wait for N cycles before returning. +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + master: // Clear bss. ldr x0, =__bss_start -- cgit v1.2.1