-Wextra \
-Wconversion \
-Wstrict-prototypes \
+ -Wmissing-prototypes \
-Wshadow \
-Wundef \
-Wdouble-promotion \
# --------------------------------------------------------------------------- #
-objs := kernel.o kentry.o
+objs := $(patsubst %.c, %.o, $(wildcard *.c))
lds := kernel.ld
LDFLAGS += -T $(lds)
--- /dev/null
+#include "link.h"
+#include "led.h"
+#include "sysclk.h"
+#include "uart.h"
+
+/* Initialize just enough to be able to handle a fault. */
+__attribute__ ((noinline, used)) static void
+sys_init (void)
+{
+ unsigned int *src, *dst;
+
+ /* Relocate .data section to SRAM */
+ src = &_data_lma_start;
+ dst = &_data_vma_start;
+
+ while (dst < &_data_vma_end)
+ *(dst++) = *(src++);
+
+ /* Initialize .bss */
+ dst = &_bss_vma_start;
+
+ while (dst < &_bss_vma_end)
+ *(dst++) = 0;
+
+ led_init ();
+ sysclk_init ();
+ uart_init ();
+}
+
+/* Startup */
+__attribute__ ((naked, noreturn)) static void
+reset_handler (void)
+{
+ /* Set FAULTMASK */
+ asm ("cpsid f");
+
+ /* This function is the core logic for reset_handler (). We just need it so
+ * that when we jump to kentry the stack is empty. */
+ asm ("bl sys_init");
+
+ /* Clear FAULTMASK */
+ asm ("cpsie f");
+
+ /* Jump to kernel entry code. When reset_handler () is called we are already
+ * running on Thread mode (Privileged), so we can just jump directly. */
+ asm ("b kentry");
+}
+
+__attribute__ ((section (".vector_table"), used))
+static unsigned long const vector_table[] = {
+ (unsigned long) &_sram_end,
+ (unsigned long) reset_handler,
+};
-#include "stm32f411.h"
+#include "uart.h"
+#include "led.h"
+
+__attribute__ ((noreturn)) void kentry (void);
static const char *text[] = {
"hello, friend.\r\n",
void
kentry (void)
{
- unsigned int line, off;
+ unsigned int line;
/* Test UART and LED ---------------------------------------------------- */
{
for (line = 0; line < sizeof text / sizeof *text; ++line)
{
- /* Print current line */
- off = 0;
- while (text[line][off])
- {
- /* TXE: Transmit data register empty */
- while (!(*USART1_SR & (1u << 7))); /* 0: Data is not transferred
- to the shift register */
-
- /* DR[8:0]: Data value */
- *USART1_DR = text[line][off++];
- }
-
- /* TC: Transmission complete */
- while (!(*USART1_SR & (1u << 6))); /* 0: Transmission is not
- complete */
-
- /* Blink LED */
- *GPIOC_ODR ^= (1u << 13);
+ kputs_busytc (text[line]);
+ led_toggle ();
}
}
}
+++ /dev/null
-#include "link.h"
-#include "stm32f411.h"
-
-__attribute__ ((noreturn)) extern void kentry (void);
-
-static void
-reset_handler (void)
-{
- unsigned int *src, *dst;
-
- /* Set FAULTMASK */
- asm ("cpsid f");
-
- /* Initialize .data and .bss in SRAM ------------------------------------ */
-
- /* Relocate .data section */
- src = &_data_lma_start;
- dst = &_data_vma_start;
-
- while (dst < &_data_vma_end)
- *(dst++) = *(src++);
-
- /* Initialize .bss */
- dst = &_bss_vma_start;
-
- while (dst < &_bss_vma_end)
- *(dst++) = 0;
-
- /* LED setup ------------------------------------------------------------ */
-
- /* GPIOCEN: IO port C clock enable */
- *RCC_AHB1ENR |= (1u << 2); /* 1: IO port C clock enabled */
- /* MODERy[1:0]: Port x configuration bits (y = 0..15) */
- *GPIOC_MODER &= ~(0b11u << 26); /* Clear PC13 bits */
- *GPIOC_MODER |= (1u << 26); /* 01: General purpose output mode */
-
- /* PC13: 0 = LED on
- * 1 = LED off */
-
- /* SYSCLK setup --------------------------------------------------------- */
-
- /* HSEBYP: HSE clock bypass */
- *RCC_CR &= ~(1u << 18); /* 0: HSE oscillator not bypassed */
- /* HSEON: HSE clock enable */
- *RCC_CR |= (1u << 16); /* 1: HSE oscillator ON */
- /* HSERDY: HSE clock ready flag */
- while (!(*RCC_CR & (1u << 17))); /* 0: HSE oscillator not ready */
- /* SW: System clock switch */
- *RCC_CFGR &= ~(0b11u); /* Clear SW bits */
- *RCC_CFGR |= (0b01u); /* 01: HSE oscillator selected as system clock */
- /* SWS: System clock switch status */
- while ((*RCC_CFGR & (0b11u << 2)) != (0b01u << 2)); /* 01: HSE oscillator used
- as the system clock */
- /* HSION: Internal high-speed clock enable */
- *RCC_CR &= ~(1u); /* 0: HSI oscillator OFF */
-
- /* UART setup ----------------------------------------------------------- */
-
- /* GPIOAEN: IO port A clock enable */
- *RCC_AHB1ENR |= (1u); /* 1: IO port A clock enabled */
- /* MODERy[1:0]: Port x configuration bits (y = 0..15) */
- *GPIOA_MODER &= ~(0b1111u << 18); /* Clear PA9 and PA10 bits */
- *GPIOA_MODER |= (0b1010u << 18); /* 10: Alternate function mode */
- /* AFRHy: Alternate function selection for port x bit y (y = 8..15) */
- *GPIOA_AFRH &= ~(0b11111111u << 4); /* Clear PA9 and PA10 bits */
- *GPIOA_AFRH |= (0b01110111u << 4); /* 0111: AF7 */
-
- /* PA9: USART1_TX
- * PA10: USART1_RX */
-
- /* USART1EN: USART1 clock enable */
- *RCC_APB2ENR |= (1u << 4); /* 1: USART1 clock enabled */
- /* UE: USART enable */
- *USART1_CR1 |= (1u << 13); /* 1: USART enable */
- /* M: Word length */
- *USART1_CR1 &= ~(1u << 12); /* 0: 1 Start bit, 8 Data bits, n Stop bit */
- /* PCE: Parity control enable */
- *USART1_CR1 &= ~(1u << 10); /* 0: Parity control disabled */
- /* STOP: STOP bits */
- *USART1_CR2 &= ~(0b11u << 12); /* 00: 1 Stop bit */
- /* Clock frequency: 25MHz
- * Desired baud rate: 1.2 KBps
- * USARTDIV = 1302.0625 */
- *USART1_BRR = (1302u << 4) | 1u; /* mantissa and fraction (/16) */
- /* TE: Transmitter enable */
- *USART1_CR1 |= (1u << 3); /* 1: Transmitter is enabled */
-
- /* Jump to kernel entry code -------------------------------------------- */
-
- /* Clear FAULTMASK */
- asm ("cpsie f");
-
- kentry ();
-}
-
-__attribute__ ((section (".vector_table"), used))
-static unsigned long const vector_table[] = {
- (unsigned long) &_sram_end,
- (unsigned long) reset_handler,
-};
--- /dev/null
+#include "led.h"
+#include "stm32f411.h"
+
+void
+led_init (void)
+{
+ /* GPIOCEN: IO port C clock enable */
+ *RCC_AHB1ENR |= (1u << 2); /* 1: IO port C clock enabled */
+ /* MODERy[1:0]: Port x configuration bits (y = 0..15) */
+ *GPIOC_MODER &= ~(0b11u << 26); /* Clear PC13 bits */
+ *GPIOC_MODER |= (1u << 26); /* 01: General purpose output mode */
+
+ /* PC13: 0 = LED on
+ * 1 = LED off */
+}
+
+void
+led_on (void)
+{
+ *GPIOC_ODR &= ~(1u << 13);
+}
+
+void
+led_off (void)
+{
+ *GPIOC_ODR |= (1u << 13);
+}
+
+void
+led_toggle (void)
+{
+ *GPIOC_ODR ^= (1u << 13);
+}
--- /dev/null
+#ifndef _LED_H_
+#define _LED_H_
+
+void led_init (void);
+void led_on (void);
+void led_off (void);
+void led_toggle (void);
+
+#endif /* _LED_H_ */
--- /dev/null
+#include "sysclk.h"
+#include "stm32f411.h"
+
+void
+sysclk_init (void)
+{
+ /* HSEBYP: HSE clock bypass */
+ *RCC_CR &= ~(1u << 18); /* 0: HSE oscillator not bypassed */
+
+ /* HSEON: HSE clock enable */
+ *RCC_CR |= (1u << 16); /* 1: HSE oscillator ON */
+
+ /* HSERDY: HSE clock ready flag */
+ while (!(*RCC_CR & (1u << 17))); /* 0: HSE oscillator not ready */
+
+ /* SW: System clock switch */
+ *RCC_CFGR &= ~(0b11u); /* Clear SW bits */
+ *RCC_CFGR |= (0b01u); /* 01: HSE oscillator selected as system clock */
+
+ /* SWS: System clock switch status */
+ while ((*RCC_CFGR & (0b11u << 2)) != (0b01u << 2)); /* 01: HSE oscillator used
+ as the system clock */
+ /* HSION: Internal high-speed clock enable */
+ *RCC_CR &= ~(1u); /* 0: HSI oscillator OFF */
+}
--- /dev/null
+#ifndef _SYSCLK_H_
+#define _SYSCLK_H_
+
+void sysclk_init (void);
+
+#endif /* _SYSCLK_H_ */
--- /dev/null
+#include "uart.h"
+#include "stm32f411.h"
+
+/* Wait for data to be transferred into the shift register. */
+static void
+busywait_txe (void)
+{
+ /* TXE: Transmit data register empty */
+ while (!(*USART1_SR & (1u << 7))); /* 0: Data is not transferred to the shift
+ register */
+}
+
+/* Wait for transmission of the last frame. */
+static void
+busywait_tc (void)
+{
+ /* TC: Transmission complete */
+ while (!(*USART1_SR & (1u << 6))); /* 0: Transmission is not complete */
+}
+
+static void
+tx (char c)
+{
+ /* DR[8:0]: Data value */
+ *USART1_DR = c;
+}
+
+void
+uart_init (void)
+{
+ /* GPIOAEN: IO port A clock enable */
+ *RCC_AHB1ENR |= (1u); /* 1: IO port A clock enabled */
+
+ /* MODERy[1:0]: Port x configuration bits (y = 0..15) */
+ *GPIOA_MODER &= ~(0b1111u << 18); /* Clear PA9 and PA10 bits */
+ *GPIOA_MODER |= (0b1010u << 18); /* 10: Alternate function mode */
+
+ /* AFRHy: Alternate function selection for port x bit y (y = 8..15) */
+ *GPIOA_AFRH &= ~(0b11111111u << 4); /* Clear PA9 and PA10 bits */
+ *GPIOA_AFRH |= (0b01110111u << 4); /* 0111: AF7 */
+
+ /* PA9: USART1_TX
+ * PA10: USART1_RX */
+
+ /* USART1EN: USART1 clock enable */
+ *RCC_APB2ENR |= (1u << 4); /* 1: USART1 clock enabled */
+
+ /* UE: USART enable */
+ *USART1_CR1 |= (1u << 13); /* 1: USART enable */
+
+ /* M: Word length */
+ *USART1_CR1 &= ~(1u << 12); /* 0: 1 Start bit, 8 Data bits, n Stop bit */
+
+ /* PCE: Parity control enable */
+ *USART1_CR1 &= ~(1u << 10); /* 0: Parity control disabled */
+
+ /* STOP: STOP bits */
+ *USART1_CR2 &= ~(0b11u << 12); /* 00: 1 Stop bit */
+
+ /* Clock frequency: 25MHz
+ * Desired baud rate: 1.2 KBps */
+
+ /* Mantissa and fraction (/16) */
+ *USART1_BRR = (1302u << 4) | 1u; /* USARTDIV = 1302.0625 */
+
+ /* TE: Transmitter enable */
+ *USART1_CR1 |= (1u << 3); /* 1: Transmitter is enabled */
+}
+
+void
+kputs_busytc (const char *str)
+{
+ while (*str)
+ {
+ busywait_txe ();
+ tx (*(str++));
+ }
+
+ busywait_tc ();
+}
--- /dev/null
+#ifndef _UART_H_
+#define _UART_H_
+
+void uart_init (void);
+
+/* Busy waiting, wait for Transmission Complete (TC). */
+void kputs_busytc (const char *str);
+
+#endif /* _UART_H_ */