From b7a21bf6a086748b4907c0577eaa114445995783 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 11 Sep 2018 00:07:38 -0700 Subject: [PATCH] altos/stm32f4: Start adding support for STM32F413 Enough to get clocks lit up at least. Signed-off-by: Keith Packard --- src/stm32f4/Makefile-flash.defs | 68 +++ src/stm32f4/Makefile-raw.defs | 13 + src/stm32f4/Makefile-stm32f4.defs | 52 +++ src/stm32f4/Makefile.defs | 13 + src/stm32f4/altos-loader.ld | 97 +++++ src/stm32f4/altos-raw.ld | 70 +++ src/stm32f4/altos.ld | 105 +++++ src/stm32f4/ao_arch.h | 104 +++++ src/stm32f4/ao_arch_funcs.h | 106 +++++ src/stm32f4/ao_interrupt.c | 239 ++++++++++ src/stm32f4/ao_timer.c | 280 ++++++++++++ src/stm32f4/registers.ld | 47 ++ src/stm32f4/stm32f4.h | 694 ++++++++++++++++++++++++++++++ 13 files changed, 1888 insertions(+) create mode 100644 src/stm32f4/Makefile-flash.defs create mode 100644 src/stm32f4/Makefile-raw.defs create mode 100644 src/stm32f4/Makefile-stm32f4.defs create mode 100644 src/stm32f4/Makefile.defs create mode 100644 src/stm32f4/altos-loader.ld create mode 100644 src/stm32f4/altos-raw.ld create mode 100644 src/stm32f4/altos.ld create mode 100644 src/stm32f4/ao_arch.h create mode 100644 src/stm32f4/ao_arch_funcs.h create mode 100644 src/stm32f4/ao_interrupt.c create mode 100644 src/stm32f4/ao_timer.c create mode 100644 src/stm32f4/registers.ld create mode 100644 src/stm32f4/stm32f4.h diff --git a/src/stm32f4/Makefile-flash.defs b/src/stm32f4/Makefile-flash.defs new file mode 100644 index 00000000..1a2aa75c --- /dev/null +++ b/src/stm32f4/Makefile-flash.defs @@ -0,0 +1,68 @@ +include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_flash_pins.h \ + ao_flash_stm_pins.h \ + ao_flash_task.h \ + ao_pins.h \ + ao_product.h \ + Makefile + +# +# Common AltOS sources +# +SRC = \ + ao_interrupt.c \ + ao_romconfig.c \ + ao_boot_chain.c \ + ao_boot_pin.c \ + ao_product.c \ + ao_notask.c \ + ao_timer.c \ + ao_usb_stm.c \ + ao_flash_stm.c \ + ao_flash_task.c \ + ao_flash_loader_stm.c + +OBJ=$(SRC:.c=.o) + +PRODUCT=AltosFlash +PRODUCT_DEF=-DALTOS_FLASH +IDPRODUCT=0x000a + +CFLAGS = $(PRODUCT_DEF) $(STM32F4_CFLAGS) -g -Os + +LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos-loader.ld + +PROGNAME=altos-flash +PROG=$(HARDWARE)-$(PROGNAME)-$(VERSION).elf +BIN=$(HARDWARE)-$(PROGNAME)-$(VERSION).bin + +MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin +FLASH_ADDR=0x08000000 + +all: $(PROG) $(BIN) + +$(PROG): Makefile $(OBJ) altos-loader.ld + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +$(BIN): $(PROG) + $(MAKEBIN) --output=$@ --base=$(FLASH_ADDR) $(PROG) + +ao_product.h: ao-make-product.5c $(TOPDIR)/Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ + +$(OBJ): $(INC) + +distclean: clean + +clean: + rm -f *.o $(HARDWARE)-$(PROGNAME)-*.elf $(HARDWARE)-$(PROGNAME)-*.bin + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/stm32f4/Makefile-raw.defs b/src/stm32f4/Makefile-raw.defs new file mode 100644 index 00000000..03d92e42 --- /dev/null +++ b/src/stm32f4/Makefile-raw.defs @@ -0,0 +1,13 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs + +LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf +MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin +FLASH_ADDR=0x08000000 + +LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos-raw.ld -n + +.DEFAULT_GOAL=all diff --git a/src/stm32f4/Makefile-stm32f4.defs b/src/stm32f4/Makefile-stm32f4.defs new file mode 100644 index 00000000..4346b7d8 --- /dev/null +++ b/src/stm32f4/Makefile-stm32f4.defs @@ -0,0 +1,52 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/Makedefs + +vpath % $(TOPDIR)/stm32f4:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR)/aes:$(TOPDIR):$(TOPDIR)/math +vpath make-altitude $(TOPDIR)/util +vpath make-kalman $(TOPDIR)/util +vpath kalman.5c $(TOPDIR)/kalman +vpath kalman_filter.5c $(TOPDIR)/kalman +vpath load_csv.5c $(TOPDIR)/kalman +vpath matrix.5c $(TOPDIR)/kalman +vpath ao-make-product.5c $(TOPDIR)/util + +.SUFFIXES: .elf .ihx + +.elf.ihx: + $(ELFTOHEX) --output=$@ $*.elf + +ifndef VERSION +include $(TOPDIR)/Version +endif + +ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex +CC=$(ARM_CC) + +WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align + +AO_CFLAGS=-I. -I$(TOPDIR)/stm32f4 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \ + -DNEWLIB_INTEGER_PRINTF_SCANF -Os -g \ + -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math \ + -isystem $(NEWLIB_NANO)/arm-none-eabi/include + +STM32F4_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m4 -mthumb \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS) + +NICKLE=nickle + +LIBS=-L$(NEWLIB_NANO)/arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard -lc -lm -lgcc + +V=0 +# The user has explicitly enabled quiet compilation. +ifeq ($(V),0) +quiet = @printf " $1 $2 $@\n"; $($1) +endif +# Otherwise, print the full command line. +quiet ?= $($1) + +.c.o: + $(call quiet,CC) -c $(CFLAGS) -o $@ $< diff --git a/src/stm32f4/Makefile.defs b/src/stm32f4/Makefile.defs new file mode 100644 index 00000000..be185a59 --- /dev/null +++ b/src/stm32f4/Makefile.defs @@ -0,0 +1,13 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs + +LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf +MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin +FLASH_ADDR=0x08000000 + +LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos.ld -n + +.DEFAULT_GOAL=all diff --git a/src/stm32f4/altos-loader.ld b/src/stm32f4/altos-loader.ld new file mode 100644 index 00000000..5d6e1f4b --- /dev/null +++ b/src/stm32f4/altos-loader.ld @@ -0,0 +1,97 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +MEMORY { + rom : ORIGIN = 0x08000000, LENGTH = 4K + ram : ORIGIN = 0x20000000, LENGTH = 256K +} + +INCLUDE registers.ld + +EXTERN (stm_interrupt_vector) + +SECTIONS { + /* + * Rom contents + */ + + .interrupt : { + __text_start__ = .; + *(.interrupt) /* Interrupt vectors */ + } > rom + + .text ORIGIN(rom) + 0x100 : { + ao_romconfig.o(.romconfig*) + ao_product.o(.romconfig*) + + *(.text*) /* Executable code */ + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + *(.rodata*) /* Constants */ + } > rom + __text_end__ = .; + + /* Boot data which must live at the start of ram so that + * the application and bootloader share the same addresses. + * This must be all uninitialized data + */ + .boot ORIGIN(ram) + SIZEOF(.interrupt) (NOLOAD) : { + __boot_start__ = .; + *(.boot) + __boot_end__ = .; + } >ram + + /* Functions placed in RAM (required for flashing) + * + * Align to 8 bytes as that's what the ARM likes text + * segment alignments to be, and if we don't, then + * we end up with a mismatch between the location in + * ROM and the desired location in RAM. I don't + * entirely understand this, but at least this appears + * to work... + */ + + .textram BLOCK(8): { + _start__ = .; + __text_ram_start__ = .; + *(.ramtext) + __text_ram_end = .; + } >ram AT>rom + + /* Data -- relocated to RAM, but written to ROM. + * also aligned to 8 bytes in case textram is empty + */ + .data BLOCK(8): { + *(.data) /* initialized data */ + _end__ = .; + } >ram AT>rom + + + .bss : { + __bss_start__ = .; + *(.bss) + *(COMMON) + __bss_end__ = .; + } >ram + + PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram)); + PROVIDE(end = .); +} + +ENTRY(start); + + diff --git a/src/stm32f4/altos-raw.ld b/src/stm32f4/altos-raw.ld new file mode 100644 index 00000000..21da5af5 --- /dev/null +++ b/src/stm32f4/altos-raw.ld @@ -0,0 +1,70 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +MEMORY { + rom (rx) : ORIGIN = 0x08000000, LENGTH = 1M + ram (!w) : ORIGIN = 0x20000000, LENGTH = 256k - 256 + stack (!w) : ORIGIN = 0x20000000 + 256k - 256, LENGTH = 256 +} + +INCLUDE registers.ld + +EXTERN (stm_interrupt_vector) + +SECTIONS { + /* + * Rom contents + */ + + .interrupt : { + __text_start__ = .; + *(.interrupt) /* Interrupt vectors */ + } > rom + + .text : { + *(.text*) /* Executable code */ + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + *(.rodata*) /* Constants */ + } > rom + __text_end__ = .; + + /* Data -- relocated to RAM, but written to ROM + */ + .data : { + _start__ = .; + *(.data) /* initialized data */ + . = ALIGN(4); + _end__ = .; + } >ram AT>rom + + .bss : { + __bss_start__ = .; + *(.bss) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } >ram + + PROVIDE(end = .); + + PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack)); +} + +ENTRY(start); + + diff --git a/src/stm32f4/altos.ld b/src/stm32f4/altos.ld new file mode 100644 index 00000000..2db0e387 --- /dev/null +++ b/src/stm32f4/altos.ld @@ -0,0 +1,105 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +MEMORY { + rom (rx) : ORIGIN = 0x08001000, LENGTH = 1M - 4k + ram (!w) : ORIGIN = 0x20000000, LENGTH = 256k - 256 + stack (!w) : ORIGIN = 0x20000000 + 256k - 256, LENGTH = 256 +} + +INCLUDE registers.ld + +EXTERN (stm_interrupt_vector) + +SECTIONS { + /* + * Rom contents + */ + + .interrupt ORIGIN(ram) : AT (ORIGIN(rom)) { + __interrupt_start__ = .; + __interrupt_rom__ = ORIGIN(rom); + *(.interrupt) /* Interrupt vectors */ + __interrupt_end__ = .; + } > ram + + .text ORIGIN(rom) + 0x100 : { + __text_start__ = .; + + /* Ick. What I want is to specify the + * addresses of some global constants so + * that I can find them across versions + * of the application. I can't figure out + * how to make gnu ld do that, so instead + * we just load the two files that include + * these defines in the right order here and + * expect things to 'just work'. Don't change + * the contents of those files, ok? + */ + ao_romconfig.o(.romconfig*) + ao_product.o(.romconfig*) + + *(.text*) /* Executable code */ + } > rom + + .ARM.exidx : { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > rom + + .rodata : { + *(.rodata*) /* Constants */ + } > rom + + __text_end__ = .; + + /* Boot data which must live at the start of ram so that + * the application and bootloader share the same addresses. + * This must be all uninitialized data + */ + .boot (NOLOAD) : { + __boot_start__ = .; + *(.boot) + . = ALIGN(4); + __boot_end__ = .; + } >ram + + /* Data -- relocated to RAM, but written to ROM + */ + .data : { + _start__ = .; + *(.data) /* initialized data */ + . = ALIGN(4); + _end__ = .; + } >ram AT>rom + + .bss : { + __bss_start__ = .; + *(.bss) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } >ram + + PROVIDE(end = .); + + PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack)); +} + +ENTRY(start); + + diff --git a/src/stm32f4/ao_arch.h b/src/stm32f4/ao_arch.h new file mode 100644 index 00000000..805d756d --- /dev/null +++ b/src/stm32f4/ao_arch.h @@ -0,0 +1,104 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_ARCH_H_ +#define _AO_ARCH_H_ + +#include +#include + +#ifndef AO_STACK_SIZE +#define AO_STACK_SIZE 512 +#endif + +#define AO_PORT_TYPE uint16_t + +#define ao_arch_nop() asm("nop") + +#define ao_arch_task_members\ + uint32_t *sp; /* saved stack pointer */ + +#define ao_arch_block_interrupts() asm("cpsid i") +#define ao_arch_release_interrupts() asm("cpsie i") + +#define ao_arch_naked_declare __attribute__((naked)) +#define ao_arch_naked_define + +/* + * ao_timer.c + * + * We'll generally use the HSE clock through the PLL + */ + +#define AO_STM_NVIC_CLOCK_PRIORITY 0xf0 /* low priority for clock */ + +#if AO_HSE +#define AO_PLLSRC AO_HSE +#endif + +#if AO_HSI +#define AO_PLLSRC STM_HSI_FREQ +#endif + +#if AO_PLL_M +#define AO_PLLIN (AO_PLLSRC / AO_PLL_M) +#endif + +#if AO_PLL1_N + +#define AO_PLL1_VCO (AO_PLLIN * AO_PLL1_N) +#define AO_PLL1_CLK_P (AO_PLL1_VCO / AO_PLL1_P) +#define AO_SYSCLK (AO_PLL1_CLK_P) + +# if AO_PLL1_Q +#define AO_PLL1_CLK_Q (AO_PLL1_VCO / AO_PLL1_Q) +# endif + +#else + +#define AO_SYSCLK AO_PLLSRC + +#endif + +#if AO_PLL2_N + +#define AO_PLL2_VCO (AO_PLLIN * AO_PLL2_N) + +# if AO_PLL2_Q +#define AO_PLL2_CLK_Q (AL_PLL2_VCO / AO_PLL2_Q) +# endif + +# if AO_PLL2_R +#define AO_PLL2_CLK_R (AL_PLL2_VCO / AO_PLL2_R) +# endif + +#endif + +#define AO_HCLK (AO_SYSCLK / AO_AHB_PRESCALER) +#define AO_P1CLK (AO_HCLK / AO_APB1_PRESCALER) +#if AO_ABP1_PRESCALER == 1 +#define AO_P1_TIMER_CLK AO_P1CLK +#else +#define AO_P1_TIMER_CLK (AO_P1CLK * 2) +#endif +#define AO_P2CLK (AO_HCLK / AO_APB2_PRESCALER) +#if AO_ABP2_PRESCALER == 1 +#define AO_P2_TIMER_CLK AO_P2CLK +#else +#define AO_P2_TIMER_CLK (AO_P2CLK * 2) +#endif +#define AO_SYSTICK (AO_HCLK) +#define AO_PANIC_DELAY_SCALE (AO_SYSCLK / 12000000) + +#endif /* _AO_ARCH_H_ */ diff --git a/src/stm32f4/ao_arch_funcs.h b/src/stm32f4/ao_arch_funcs.h new file mode 100644 index 00000000..252fe77a --- /dev/null +++ b/src/stm32f4/ao_arch_funcs.h @@ -0,0 +1,106 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_ARCH_FUNCS_H_ +#define _AO_ARCH_FUNCS_H_ + +/* GPIO functions */ + +#define ao_power_register(gpio) +#define ao_power_unregister(gpio) + +static inline void ao_enable_port(struct stm_gpio *port) +{ + if ((port) == &stm_gpioa) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPAEN); + ao_power_register(&ao_power_gpioa); + } else if ((port) == &stm_gpiob) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPBEN); + ao_power_register(&ao_power_gpiob); + } else if ((port) == &stm_gpioc) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPCEN); + ao_power_register(&ao_power_gpioc); + } else if ((port) == &stm_gpiod) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPDEN); + ao_power_register(&ao_power_gpiod); + } else if ((port) == &stm_gpioe) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPEEN); + ao_power_register(&ao_power_gpioe); + } else if ((port) == &stm_gpiof) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPFEN); + ao_power_register(&ao_power_gpiof); + } else if ((port) == &stm_gpiog) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPGEN); + ao_power_register(&ao_power_gpiog); + } else if ((port) == &stm_gpioh) { + stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPHEN); + ao_power_register(&ao_power_gpioh); + } +} + +static inline void ao_disable_port(struct stm_gpio *port) +{ + if ((port) == &stm_gpioa) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPAEN); + ao_power_unregister(&ao_power_gpioa); + } else if ((port) == &stm_gpiob) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPBEN); + ao_power_unregister(&ao_power_gpiob); + } else if ((port) == &stm_gpioc) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPCEN); + ao_power_unregister(&ao_power_gpioc); + } else if ((port) == &stm_gpiod) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPDEN); + ao_power_unregister(&ao_power_gpiod); + } else if ((port) == &stm_gpioe) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPEEN); + ao_power_unregister(&ao_power_gpioe); + } else if ((port) == &stm_gpiof) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPFEN); + ao_power_unregister(&ao_power_gpiof); + } else if ((port) == &stm_gpiog) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPGEN); + ao_power_unregister(&ao_power_gpiog); + } else if ((port) == &stm_gpioh) { + stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPHEN); + ao_power_unregister(&ao_power_gpioh); + } +} + +#define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v) + +#define ao_gpio_get(port, bit) stm_gpio_get(port, bit) + +#define ao_enable_output(port,bit,v) do { \ + ao_enable_port(port); \ + ao_gpio_set(port, bit, v); \ + stm_moder_set(port, bit, STM_MODER_OUTPUT);\ + } while (0) + +#define ao_gpio_set_mode(port,bit,mode) do { \ + if (mode == AO_EXTI_MODE_PULL_UP) \ + stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP); \ + else if (mode == AO_EXTI_MODE_PULL_DOWN) \ + stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN); \ + else \ + stm_pupdr_set(port, bit, STM_PUPDR_NONE); \ + } while (0) + +#define ao_enable_input(port,bit,mode) do { \ + ao_enable_port(port); \ + stm_moder_set(port, bit, STM_MODER_INPUT); \ + ao_gpio_set_mode(port, bit, mode); \ + } while (0) + +#endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/stm32f4/ao_interrupt.c b/src/stm32f4/ao_interrupt.c new file mode 100644 index 00000000..a00df54b --- /dev/null +++ b/src/stm32f4/ao_interrupt.c @@ -0,0 +1,239 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include "stm32f4.h" +#include +#include + +extern void main(void); +extern char __stack__; +extern char __text_start__, __text_end__; +extern char _start__, _end__; +extern char __bss_start__, __bss_end__; + +/* Interrupt functions */ + +void stm_halt_isr(void) +{ + ao_panic(AO_PANIC_CRASH); +} + +void stm_ignore_isr(void) +{ +} + +const void *stm_interrupt_vector[]; + +void start(void) +{ +#ifdef AO_BOOT_CHAIN + if (ao_boot_check_chain()) { +#ifdef AO_BOOT_PIN + ao_boot_check_pin(); +#endif + } +#endif + /* Enable FPU */ + stm_scb.cpacr |= ((STM_SCB_CPACR_FULL << STM_SCB_CPACR_FP0) | + (STM_SCB_CPACR_FULL << STM_SCB_CPACR_FP1)); + ao_arch_nop(); + /* Set interrupt vector table offset */ + stm_scb.vtor = (uint32_t) &stm_interrupt_vector; + memcpy(&_start__, &__text_end__, &_end__ - &_start__); + memset(&__bss_start__, '\0', &__bss_end__ - &__bss_start__); + main(); +} + +#define STRINGIFY(x) #x + +#define isr(name) \ + void __attribute__ ((weak)) stm_ ## name ## _isr(void); \ + _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr)) + +#define isr_halt(name) \ + void __attribute__ ((weak)) stm_ ## name ## _isr(void); \ + _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr)) + +isr(nmi) +isr_halt(hardfault) +isr_halt(memmanage) +isr_halt(busfault) +isr_halt(usagefault) +isr(svc) +isr(debugmon) +isr(pendsv) +isr(systick) +isr(wwdg) +isr(pvd) +isr(tamper_stamp) +isr(rtc_wkup) +isr(flash) +isr(rcc) +isr(exti0) +isr(exti1) +isr(exti2) +isr(exti3) +isr(exti4) +isr(dma1_stream0) +isr(dma1_stream1) +isr(dma1_stream2) +isr(dma1_stream3) +isr(dma1_stream4) +isr(dma1_stream5) +isr(dma1_stream6) +isr(adc) +isr(can1_tx) +isr(can1_rx0) +isr(can1_rx1) +isr(can1_sce) +isr(exti9_5) +isr(tim1_brk_tim9) +isr(tim1_up_tim10) +isr(tim_trg_com_tim11) +isr(tim1_cc) +isr(tim2) +isr(tim3) +isr(tim4) +isr(i2c1_evt) +isr(i2c1_err) +isr(i2c2_evt) +isr(i2c2_err) +isr(spi1) +isr(spi2) +isr(usart1) +isr(usart2) +isr(usart3) +isr(exti5_10) +isr(rtc_alarm) +isr(otg_fs_wkup) +isr(tim8_brk_tim12) +isr(tim8_up_tim13) +isr(tim8_trg_com_tim14) +isr(tim8_cc) +isr(dma1_stream7) +isr(fsmc) +isr(sdio) +isr(tim5) +isr(spi3) +isr(uart4) +isr(uart5) +isr(tim6_glb_it) +isr(tim7) +isr(dma2_stream0) +isr(dma2_stream1) +isr(dma2_stream2) +isr(dma2_stream3) +isr(dma2_stream4) +isr(dfsdm1_flt0) +isr(dfsdm1_flt1) +isr(can2_tx) +isr(can2_rx0) +isr(can2_rx1) +isr(can2_sce) +isr(otg_fs) +isr(dma2_stream5) +isr(dma2_stream6) +isr(dma2_stream7) +isr(usart6) +isr(i2c3_ev) +isr(i2c3_er) +isr(can3_tx) +isr(can3_rx0) +isr(can3_rx1) +isr(can3_sce) +isr(crypto) +isr(rng) +isr(fpu) +isr(uart7) +isr(uart8) +isr(spi4) +isr(spi5) +isr(sai1) +isr(uart9) +isr(uart10) +isr(quad_spi) +isr(i2cfmp1_ev) +isr(i2cfmp1_er) +isr(exti23) +isr(dfsdm2_flt0) +isr(dfsdm2_flt1) +isr(dfsdm2_flt2) +isr(dfsdm2_flt3) + +#define i(addr,name) [(addr)/4] = stm_ ## name ## _isr + +__attribute__ ((section(".interrupt"))) +const void *stm_interrupt_vector[] = { + [0] = &__stack__, + [1] = start, + i(0x08, nmi), + i(0x0c, hardfault), + i(0x10, memmanage), + i(0x14, busfault), + i(0x18, usagefault), + i(0x2c, svc), + i(0x30, debugmon), + i(0x38, pendsv), + i(0x3c, systick), + i(0x40, wwdg), + i(0x44, pvd), + i(0x48, tamper_stamp), + i(0x4c, rtc_wkup), + i(0x50, flash), + i(0x54, rcc), + i(0x58, exti0), + i(0x5c, exti1), + i(0x60, exti2), + i(0x64, exti3), + i(0x68, exti4), + i(0x6c, dma1_stream0), + i(0x70, dma1_stream1), + i(0x74, dma1_stream2), + i(0x78, dma1_stream3), + i(0x7c, dma1_stream4), + i(0x80, dma1_stream5), + i(0x84, dma1_stream6), + i(0x88, adc), + i(0x8c, can1_tx), + i(0x90, can1_rx0), + i(0x94, can1_rx1), + i(0x98, can1_sce), + i(0x9c, exti9_5), + i(0xa0, tim1_brk_tim9), + i(0xa4, tim1_up_tim10), + i(0xa8, tim_trg_com_tim11), + i(0xac, tim1_cc), + i(0xb0, tim2), + i(0xb4, tim3), + i(0xb8, tim4), + i(0xbc, i2c1_evt), + i(0xc0, i2c1_err), + i(0xc4, i2c2_evt), + i(0xc8, i2c2_err), + i(0xcc, spi1), + i(0xd0, spi2), + i(0xd4, usart1), + i(0xd8, usart2), + i(0xdc, usart3), + i(0xe0, exti5_10), + i(0xe4, rtc_alarm), + i(0xe8, otg_fs_wkup), + i(0xec, tim8_brk_tim12), + i(0xf0, tim8_up_tim13), +}; diff --git a/src/stm32f4/ao_timer.c b/src/stm32f4/ao_timer.c new file mode 100644 index 00000000..e96559f4 --- /dev/null +++ b/src/stm32f4/ao_timer.c @@ -0,0 +1,280 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include + +#ifndef HAS_TICK +#define HAS_TICK 1 +#endif + +#if HAS_TICK || defined(AO_TIMER_HOOK) + +#if HAS_TICK +volatile AO_TICK_TYPE ao_tick_count; + +AO_TICK_TYPE +ao_time(void) +{ + return ao_tick_count; +} +#endif + +#if AO_DATA_ALL +volatile uint8_t ao_data_interval = 1; +volatile uint8_t ao_data_count; +#endif + +void stm_systick_isr(void) +{ + ao_validate_cur_stack(); + if (stm_systick.csr & (1 << STM_SYSTICK_CSR_COUNTFLAG)) { +#if HAS_TICK + ++ao_tick_count; +#endif +#if HAS_TASK_QUEUE + if (ao_task_alarm_tick && (int16_t) (ao_tick_count - ao_task_alarm_tick) >= 0) + ao_task_check_alarm((uint16_t) ao_tick_count); +#endif +#if AO_DATA_ALL + if (++ao_data_count == ao_data_interval) { + ao_data_count = 0; +#if HAS_FAKE_FLIGHT + if (ao_fake_flight_active) + ao_fake_flight_poll(); + else +#endif + ao_adc_poll(); +#if (AO_DATA_ALL & ~(AO_DATA_ADC)) + ao_wakeup((void *) &ao_data_count); +#endif + } +#endif +#ifdef AO_TIMER_HOOK + AO_TIMER_HOOK; +#endif + } +} + +#if HAS_ADC +void +ao_timer_set_adc_interval(uint8_t interval) +{ + ao_arch_critical( + ao_data_interval = interval; + ao_data_count = 0; + ); +} +#endif + +#define SYSTICK_RELOAD ((AO_SYSTICK / 8) / 100 - 1) + +void +ao_timer_init(void) +{ + stm_systick.rvr = SYSTICK_RELOAD; + stm_systick.cvr = 0; + stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) | + (1 << STM_SYSTICK_CSR_TICKINT) | + (STM_SYSTICK_CSR_CLKSOURCE_AHB_8 << STM_SYSTICK_CSR_CLKSOURCE)); + stm_scb.shpr3 |= AO_STM_NVIC_CLOCK_PRIORITY << 24; +} + +#endif + +void +ao_clock_init(void) +{ + uint32_t cfgr; + uint32_t pllcfgr; + + /* Switch to HSI while messing about */ + stm_rcc.cr |= (1 << STM_RCC_CR_HSION); + while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY))) + ao_arch_nop(); + + stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) | + (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW); + + /* wait for system to switch to HSI */ + while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) != + (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)) + ao_arch_nop(); + + /* reset everything but the HSI selection and status */ + stm_rcc.cfgr &= (uint32_t)0x0000000f; + + /* reset everything but HSI */ + stm_rcc.cr &= 0x0000ffff; + + /* Disable and clear all interrupts */ + stm_rcc.cir = 0xffff0000; + +#if AO_HSE +#if AO_HSE_BYPASS + stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP); +#else + stm_rcc.cr &= ~(1 << STM_RCC_CR_HSEBYP); +#endif + /* Enable HSE clock */ + stm_rcc.cr |= (1 << STM_RCC_CR_HSEON); + while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY))) + asm("nop"); + +#define STM_RCC_CFGR_SWS_TARGET_CLOCK (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS) +#define STM_RCC_CFGR_SW_TARGET_CLOCK (STM_RCC_CFGR_SW_HSE) +#define STM_PLLSRC AO_HSE +#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK (1 << STM_RCC_CFGR_PLLSRC) +#else +#define STM_RCC_CFGR_SWS_TARGET_CLOCK (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS) +#define STM_RCC_CFGR_SW_TARGET_CLOCK (STM_RCC_CFGR_SW_HSI) +#define STM_PLLSRC STM_HSI +#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK (0 << STM_RCC_CFGR_PLLSRC) +#endif + +#if !AO_HSE || HAS_ADC || HAS_ADC_SINGLE + /* Enable HSI RC clock 16MHz */ + stm_rcc.cr |= (1 << STM_RCC_CR_HSION); + while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY))) + asm("nop"); +#endif + + /* Set flash latency to tolerate SYSCLK */ + +#define FLASH_LATENCY ((AO_SYSCLK - 1) / 25000000) + + /* Enable icache, dcache and prefetch. Set latency */ + stm_flash.acr = ((1 << STM_FLASH_ACR_DCEN) | + (1 << STM_FLASH_ACR_ICEN) | + (1 << STM_FLASH_ACR_PRFTEN) | + (FLASH_LATENCY << STM_FLASH_ACR_LATENCY)); + + /* Enable power interface clock */ + stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN); + +#if AO_SYSCLK <= 64000000 +#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_1 +#elif AO_SYSCLK <= 84000000 +#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_2 +#else +#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_1 +#endif + + /* Set voltage scale mode */ + stm_pwr.cr = ((stm_pwr.cr & ~(STM_PWR_CR_VOS_SCALE_MODE_MASK)) | + (VOS_SCALE_MODE << STM_PWR_CR_VOS)); + + /* HCLK */ + cfgr = stm_rcc.cfgr; + cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE); + cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE); + stm_rcc.cfgr = cfgr; + + /* APB1 Prescaler = AO_APB1_PRESCALER */ + cfgr = stm_rcc.cfgr; + cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1); + cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1); + stm_rcc.cfgr = cfgr; + + /* APB2 Prescaler = AO_APB2_PRESCALER */ + cfgr = stm_rcc.cfgr; + cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2); + cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2); + stm_rcc.cfgr = cfgr; + + /* Disable the PLL */ + stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON); + while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)) + asm("nop"); + + /* PLL1VCO */ + pllcfgr = stm_rcc.pllcfgr; + pllcfgr &= ~(STM_RCC_PLLCFGR_PLLM_MASK << STM_RCC_PLLCFGR_PLLM); + pllcfgr &= ~(STM_RCC_PLLCFGR_PLLN_MASK << STM_RCC_PLLCFGR_PLLN); + pllcfgr &= ~(STM_RCC_PLLCFGR_PLLP_MASK << STM_RCC_PLLCFGR_PLLP); + pllcfgr &= ~(STM_RCC_PLLCFGR_PLLQ_MASK << STM_RCC_PLLCFGR_PLLQ); + pllcfgr &= ~(STM_RCC_PLLCFGR_PLLR_MASK << STM_RCC_PLLCFGR_PLLR); + + pllcfgr |= (AO_PLL_M << STM_RCC_PLLCFGR_PLLM); + pllcfgr |= (AO_PLL1_N << STM_RCC_PLLCFGR_PLLN); +#if AO_PLL1_P + pllcfgr |= (AO_PLL1_P << STM_RCC_PLLCFGR_PLLP); +#endif +#if AO_PLL1_Q + pllcfgr |= (AO_PLL1_Q << STM_RCC_PLLCFGR_PLLQ); +#endif + /* PLL source */ + pllcfgr &= ~(1 << STM_RCC_PLLCFGR_PLLSRC); +#if AO_HSI + pllcfgr |= STM_RCC_PLLCFGR_PLLSRC_HSI; +#endif +#if AO_HSE + pllcfgr |= STM_RCC_PLLCFGR_PLLSRC_HSE; +#endif + stm_rcc.pllcfgr = pllcfgr; + + /* Enable the PLL and wait for it */ + stm_rcc.cr |= (1 << STM_RCC_CR_PLLON); + while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))) + asm("nop"); + + /* Switch to the PLL for the system clock */ + + cfgr = stm_rcc.cfgr; + cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW); + cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW); + stm_rcc.cfgr = cfgr; + for (;;) { + uint32_t c, part, mask, val; + + c = stm_rcc.cfgr; + mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS); + val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS); + part = c & mask; + if (part == val) + break; + } + +#if 0 + stm_rcc.apb2rstr = 0xffff; + stm_rcc.apb1rstr = 0xffff; + stm_rcc.ahbrstr = 0x3f; + stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN); + stm_rcc.apb2enr = 0; + stm_rcc.apb1enr = 0; + stm_rcc.ahbrstr = 0; + stm_rcc.apb1rstr = 0; + stm_rcc.apb2rstr = 0; +#endif + + /* Clear reset flags */ + stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF); + +#if DEBUG_THE_CLOCK + /* Output SYSCLK on PA8 for measurments */ + + stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN); + + stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0); + stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE); + stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz); + + stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE); + stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL); +#endif +} diff --git a/src/stm32f4/registers.ld b/src/stm32f4/registers.ld new file mode 100644 index 00000000..b36b2098 --- /dev/null +++ b/src/stm32f4/registers.ld @@ -0,0 +1,47 @@ +stm_tim2 = 0x40000000; +stm_tim3 = 0x40000400; +stm_tim4 = 0x40000800; +stm_tim5 = 0x40000c00; +stm_tim6 = 0x40001000; +stm_tim7 = 0x40001400; +stm_tim12 = 0x40001800; +stm_tim13 = 0x40001c00; +stm_tim14 = 0x40002000; +stm_lptim1 = 0x40002400; +stm_rtc = 0x40002800; +stm_wwdg = 0x40002c00; +stm_iwdg = 0x40003000; +stm_can1 = 0x40006400; +stm_can2 = 0x40006800; +stm_can3 = 0x40006c00; +stm_pwr = 0x40007000; +stm_dac1 = 0x40007400; +stm_uart7 = 0x40007800; +stm_uart8 = 0x40007c00; + +stm_gpioa = 0x40020000; +stm_gpiob = 0x40020400; +stm_gpioc = 0x40020800; +stm_gpiod = 0x40020c00; +stm_gpioe = 0x40021000; +stm_gpiof = 0x40021400; +stm_gpiog = 0x40021800; +stm_gpioh = 0x40021c00; + +stm_crc = 0x40023000; +stm_rcc = 0x40023800; +stm_flash = 0x40023c00; +stm_dma1 = 0x40026000; +stm_dma2 = 0x40026400; + +stm_systick = 0xe000e010; + +stm_ictr = 0xe000e004; +stm_nvic = 0xe000e100; + +stm_scb = 0xe000ed00; + +stm_mpu = 0xe000ed90; + +stm_flash_size = 0x1fff7a22; +stm_device_id = 0x1fff7a10; diff --git a/src/stm32f4/stm32f4.h b/src/stm32f4/stm32f4.h new file mode 100644 index 00000000..a351f086 --- /dev/null +++ b/src/stm32f4/stm32f4.h @@ -0,0 +1,694 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _STM32F4_H_ +#define _STM32F4_H_ + +#include + +typedef volatile uint32_t vuint32_t; +typedef volatile void * vvoid_t; +typedef volatile uint16_t vuint16_t; +typedef volatile uint8_t vuint8_t; + +struct stm_pwr { + vuint32_t cr; + vuint32_t csr; +}; + +extern struct stm_pwr stm_pwr; + +#define stm_pwr (*((struct stm_pwr *) 0x40007000)) + +#define STM_PWR_CR_FISSR 21 +#define STM_PWR_CR_FMSSR 20 +#define STM_PWR_CR_VOS 14 +#define STM_PWR_CR_VOS_SCALE_MODE_3 1 +#define STM_PWR_CR_VOS_SCALE_MODE_2 2 +#define STM_PWR_CR_VOS_SCALE_MODE_1 3 +#define STM_PWR_CR_VOS_SCALE_MODE_MASK 3 +#define STM_PWR_CR_ADCDC1 13 +#define STM_PWR_CR_MRLVDS 11 +#define STM_PWR_CR_LPLVDS 10 +#define STM_PWR_CR_FPDS 9 +#define STM_PWR_CR_DBP 8 +#define STM_PWR_CR_PLS 5 +#define STM_PWR_CR_PVDE 4 +#define STM_PWR_CR_CSBF 3 +#define STM_PWR_CR_CWUF 2 +#define STM_PWR_CR_PDDS 1 +#define STM_PWR_CR_LPDS 0 + +struct stm_rcc { + vuint32_t cr; + vuint32_t pllcfgr; + vuint32_t cfgr; + vuint32_t cir; + + vuint32_t ahb1rstr; + vuint32_t ahb2rstr; + vuint32_t ahb3rstr; + uint32_t pad_1c; + + vuint32_t apb1rstr; + vuint32_t apb2rstr; + vuint32_t pad_28; + vuint32_t pad_2c; + + vuint32_t ahb1enr; + vuint32_t ahb2enr; + vuint32_t ahbdnr; + vuint32_t pad_3c; + + vuint32_t apb1enr; + vuint32_t apb2enr; + vuint32_t pad_48; + vuint32_t pad_4c; + + vuint32_t ahb1lpenr; + vuint32_t ahb2lpenr; + vuint32_t ahb3lpenr; + vuint32_t pad_5c; + + vuint32_t apb1lpenr; + vuint32_t apb2lpenr; + vuint32_t pad_68; + vuint32_t pad_6c; + + vuint32_t bdcr; + vuint32_t csr; + vuint32_t pad_78; + vuint32_t pad_7c; + + vuint32_t sscgr; + vuint32_t plli2scfgr; + vuint32_t pad_88; + vuint32_t dckcfgr; + + vuint32_t ckgatenr; + vuint32_t dckcfgr2; +}; + +extern struct stm_rcc stm_rcc; + +#define stm_rcc (*((struct stm_rcc *) 0x40023800)) + +/* Internal HSI is 16MHz */ +#define STM_HSI_FREQ 16000000 + +#define STM_RCC_CR_PLLI2SRDY (27) +#define STM_RCC_CR_PLLI2SON (26) +#define STM_RCC_CR_PLLRDY (25) +#define STM_RCC_CR_PLLON (24) +#define STM_RCC_CR_CSSON (19) +#define STM_RCC_CR_HSEBYP (18) +#define STM_RCC_CR_HSERDY (17) +#define STM_RCC_CR_HSEON (16) +#define STM_RCC_CR_HSICAL (8) +#define STM_RCC_CR_HSITRIM (3) +#define STM_RCC_CR_HSIRDY (1) +#define STM_RCC_CR_HSION (0) + +#define STM_RCC_PLLCFGR_PLLM 0 +#define STM_RCC_PLLCFGR_PLLM_MASK 0x3f +#define STM_RCC_PLLCFGR_PLLN 6 +#define STM_RCC_PLLCFGR_PLLN_MASK 0x1ff +#define STM_RCC_PLLCFGR_PLLP 16 +#define STM_RCC_PLLCFGR_PLLP_MASK 0x3 +#define STM_RCC_PLLCFGR_PLLSRC 22 +#define STM_RCC_PLLCFGR_PLLSRC_HSI 0 +#define STM_RCC_PLLCFGR_PLLSRC_HSE 1 +#define STM_RCC_PLLCFGR_PLLQ 24 +#define STM_RCC_PLLCFGR_PLLQ_MASK 0xf +#define STM_RCC_PLLCFGR_PLLR 28 +#define STM_RCC_PLLCFGR_PLLR_MASK 0x7 + +#define STM_RCC_CFGR_MCO2 (30) +#define STM_RCC_CFGR_MCO2PRE (27) +#define STM_RCC_CFGR_MCO1PRE (24) +#define STM_RCC_CFGR_MCO1 (21) +#define STM_RCC_CFGR_RTCPRE (16) + +#define STM_RCC_CFGR_PPRE2 (13) +#define STM_RCC_CFGR_PPRE2_DIV_1 0 +#define STM_RCC_CFGR_PPRE2_DIV_2 4 +#define STM_RCC_CFGR_PPRE2_DIV_4 5 +#define STM_RCC_CFGR_PPRE2_DIV_8 6 +#define STM_RCC_CFGR_PPRE2_DIV_16 7 +#define STM_RCC_CFGR_PPRE2_MASK 7 + +#define STM_RCC_CFGR_PPRE1 (10) +#define STM_RCC_CFGR_PPRE1_DIV_1 0 +#define STM_RCC_CFGR_PPRE1_DIV_2 4 +#define STM_RCC_CFGR_PPRE1_DIV_4 5 +#define STM_RCC_CFGR_PPRE1_DIV_8 6 +#define STM_RCC_CFGR_PPRE1_DIV_16 7 +#define STM_RCC_CFGR_PPRE1_MASK 7 + +#define STM_RCC_CFGR_HPRE (4) +#define STM_RCC_CFGR_HPRE_DIV_1 0x0 +#define STM_RCC_CFGR_HPRE_DIV_2 0x8 +#define STM_RCC_CFGR_HPRE_DIV_4 0x9 +#define STM_RCC_CFGR_HPRE_DIV_8 0xa +#define STM_RCC_CFGR_HPRE_DIV_16 0xb +#define STM_RCC_CFGR_HPRE_DIV_64 0xc +#define STM_RCC_CFGR_HPRE_DIV_128 0xd +#define STM_RCC_CFGR_HPRE_DIV_256 0xe +#define STM_RCC_CFGR_HPRE_DIV_512 0xf +#define STM_RCC_CFGR_HPRE_MASK 0xf + +#define STM_RCC_CFGR_SWS (2) +#define STM_RCC_CFGR_SWS_HSI 0 +#define STM_RCC_CFGR_SWS_HSE 1 +#define STM_RCC_CFGR_SWS_PLL 2 +#define STM_RCC_CFGR_SWS_MASK 3 + +#define STM_RCC_CFGR_SW (0) +#define STM_RCC_CFGR_SW_HSI 0 +#define STM_RCC_CFGR_SW_HSE 1 +#define STM_RCC_CFGR_SW_PLL 2 +#define STM_RCC_CFGR_SW_MASK 3 + +#define STM_RCC_AHB1ENR_IOPAEN 0 +#define STM_RCC_AHB1ENR_IOPBEN 1 +#define STM_RCC_AHB1ENR_IOPCEN 2 +#define STM_RCC_AHB1ENR_IOPDEN 3 +#define STM_RCC_AHB1ENR_IOPEEN 4 +#define STM_RCC_AHB1ENR_IOPFEN 5 +#define STM_RCC_AHB1ENR_IOPGEN 6 +#define STM_RCC_AHB1ENR_IOPHEN 7 + +#define STM_RCC_APB1ENR_UART8EN 31 +#define STM_RCC_APB1ENR_UART7EN 30 +#define STM_RCC_APB1ENR_DACEN 29 +#define STM_RCC_APB1ENR_PWREN 28 +#define STM_RCC_APB1ENR_CAN3EN 27 +#define STM_RCC_APB1ENR_CAN2EN 26 +#define STM_RCC_APB1ENR_CAN1EN 25 +#define STM_RCC_APB1ENR_I2CFMP1EN 24 +#define STM_RCC_APB1ENR_I2C3EN 23 +#define STM_RCC_APB1ENR_I2C2EN 22 +#define STM_RCC_APB1ENR_I2C1EN 21 +#define STM_RCC_APB1ENR_UART5EN 20 +#define STM_RCC_APB1ENR_UART4EN 19 +#define STM_RCC_APB1ENR_USART3EN 18 +#define STM_RCC_APB1ENR_USART2EN 17 +#define STM_RCC_APB1ENR_SPI3EN 15 +#define STM_RCC_APB1ENR_SPI2EN 14 +#define STM_RCC_APB1ENR_WWDGEN 11 +#define STM_RCC_APB1ENR_RTCAPBEN 10 +#define STM_RCC_APB1ENR_LPTIMER1EN 9 +#define STM_RCC_APB1ENR_TIM14EN 8 +#define STM_RCC_APB1ENR_TIM13EN 7 +#define STM_RCC_APB1ENR_TIM12EN 6 +#define STM_RCC_APB1ENR_TIM7EN 5 +#define STM_RCC_APB1ENR_TIM6EN 4 +#define STM_RCC_APB1ENR_TIM5EN 3 +#define STM_RCC_APB1ENR_TIM4EN 2 +#define STM_RCC_APB1ENR_TIM3EN 1 +#define STM_RCC_APB1ENR_TIM2EN 0 + +#define STM_RCC_CSR_RMVF 24 + +struct stm_ictr { + vuint32_t ictr; +}; + +extern struct stm_ictr stm_ictr; + +#define stm_ictr (*((struct stm_ictr *) 0xe000e004)) + +#define STM_ICTR_ICTR_INTLINESNUM 0 +#define STM_ICTR_ICTR_INTLINESNUM_MASK 0xf + +struct stm_nvic { + vuint32_t iser[8]; /* 0x000 0xe000e100 Set Enable Register */ + + uint8_t _unused020[0x080 - 0x020]; + + vuint32_t icer[8]; /* 0x080 0xe000e180 Clear Enable Register */ + + uint8_t _unused0a0[0x100 - 0x0a0]; + + vuint32_t ispr[8]; /* 0x100 0xe000e200 Set Pending Register */ + + uint8_t _unused120[0x180 - 0x120]; + + vuint32_t icpr[8]; /* 0x180 0xe000e280 Clear Pending Register */ + + uint8_t _unused1a0[0x200 - 0x1a0]; + + vuint32_t iabr[8]; /* 0x200 0xe000e300 Active Bit Register */ + + uint8_t _unused220[0x300 - 0x220]; + + vuint32_t ipr[60]; /* 0x300 0xe000e400 Priority Register */ +}; + +extern struct stm_nvic stm_nvic; + +#define stm_nvic (*((struct stm_nvic *) 0xe000e100)) + +#define IRQ_REG(irq) ((irq) >> 5) +#define IRQ_BIT(irq) ((irq) & 0x1f) +#define IRQ_MASK(irq) (1 << IRQ_BIT(irq)) +#define IRQ_BOOL(v,irq) (((v) >> IRQ_BIT(irq)) & 1) + +static inline void +stm_nvic_set_enable(int irq) { + stm_nvic.iser[IRQ_REG(irq)] = IRQ_MASK(irq); +} + +static inline void +stm_nvic_clear_enable(int irq) { + stm_nvic.icer[IRQ_REG(irq)] = IRQ_MASK(irq); +} + +static inline int +stm_nvic_enabled(int irq) { + return IRQ_BOOL(stm_nvic.iser[IRQ_REG(irq)], irq); +} + +static inline void +stm_nvic_set_pending(int irq) { + stm_nvic.ispr[IRQ_REG(irq)] = IRQ_MASK(irq); +} + +static inline void +stm_nvic_clear_pending(int irq) { + stm_nvic.icpr[IRQ_REG(irq)] = IRQ_MASK(irq); +} + +static inline int +stm_nvic_pending(int irq) { + return IRQ_BOOL(stm_nvic.ispr[IRQ_REG(irq)], irq); +} + +static inline int +stm_nvic_active(int irq) { + return IRQ_BOOL(stm_nvic.iabr[IRQ_REG(irq)], irq); +} + +#define IRQ_PRIO_REG(irq) ((irq) >> 2) +#define IRQ_PRIO_BIT(irq) (((irq) & 3) << 3) +#define IRQ_PRIO_MASK(irq) (0xff << IRQ_PRIO_BIT(irq)) + +static inline void +stm_nvic_set_priority(int irq, uint8_t prio) { + int n = IRQ_PRIO_REG(irq); + uint32_t v; + + v = stm_nvic.ipr[n]; + v &= ~IRQ_PRIO_MASK(irq); + v |= (prio) << IRQ_PRIO_BIT(irq); + stm_nvic.ipr[n] = v; +} + +static inline uint8_t +stm_nvic_get_priority(int irq) { + return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0); +} + +struct stm_flash { + vuint32_t acr; + vuint32_t keyr; + vuint32_t optkeyr; + vuint32_t sr; + + vuint32_t cr; + vuint32_t optcr; + vuint32_t wrpr; +}; + +extern struct stm_flash stm_flash; + +#define stm_flash (*((struct stm_flash *) 0x40023c00)) + +#define STM_FLASH_ACR_DCRST 12 +#define STM_FLASH_ACR_ICRST 11 +#define STM_FLASH_ACR_DCEN 10 +#define STM_FLASH_ACR_ICEN 9 +#define STM_FLASH_ACR_PRFTEN 8 +#define STM_FLASH_ACR_LATENCY 0 + +struct stm_flash_size { + vuint16_t f_size; +}; + +extern struct stm_flash_size stm_flash_size; + +#define stm_flash_size (*((struct stm_flash_size *) 0x1fff7a22)) + +struct stm_gpio { + vuint32_t moder; + vuint32_t otyper; + vuint32_t ospeedr; + vuint32_t pupdr; + + vuint32_t idr; + vuint32_t odr; + vuint32_t bsrr; + vuint32_t lckr; + + vuint32_t afrl; + vuint32_t afrh; +}; + +#define STM_MODER_SHIFT(pin) ((pin) << 1) +#define STM_MODER_MASK 3 +#define STM_MODER_INPUT 0 +#define STM_MODER_OUTPUT 1 +#define STM_MODER_ALTERNATE 2 +#define STM_MODER_ANALOG 3 + +static inline void +stm_moder_set(struct stm_gpio *gpio, int pin, vuint32_t value) { + gpio->moder = ((gpio->moder & + ~(STM_MODER_MASK << STM_MODER_SHIFT(pin))) | + value << STM_MODER_SHIFT(pin)); +} + +static inline uint32_t +stm_moder_get(struct stm_gpio *gpio, int pin) { + return (gpio->moder >> STM_MODER_SHIFT(pin)) & STM_MODER_MASK; +} + +#define STM_OTYPER_SHIFT(pin) (pin) +#define STM_OTYPER_MASK 1 +#define STM_OTYPER_PUSH_PULL 0 +#define STM_OTYPER_OPEN_DRAIN 1 + +static inline void +stm_otyper_set(struct stm_gpio *gpio, int pin, vuint32_t value) { + gpio->otyper = ((gpio->otyper & + ~(STM_OTYPER_MASK << STM_OTYPER_SHIFT(pin))) | + value << STM_OTYPER_SHIFT(pin)); +} + +static inline uint32_t +stm_otyper_get(struct stm_gpio *gpio, int pin) { + return (gpio->otyper >> STM_OTYPER_SHIFT(pin)) & STM_OTYPER_MASK; +} + +#define STM_OSPEEDR_SHIFT(pin) ((pin) << 1) +#define STM_OSPEEDR_MASK 3 +#define STM_OSPEEDR_LOW 0 /* 2-8MHz */ +#define STM_OSPEEDR_MEDIUM 1 /* 12.5-50MHz */ +#define STM_OSPEEDR_FAST 2 /* 25-100MHz */ +#define STM_OSPEEDR_HIGH 3 /* 50-100MHz */ + +static inline void +stm_ospeedr_set(struct stm_gpio *gpio, int pin, vuint32_t value) { + gpio->ospeedr = ((gpio->ospeedr & + ~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) | + value << STM_OSPEEDR_SHIFT(pin)); +} + +static inline uint32_t +stm_ospeedr_get(struct stm_gpio *gpio, int pin) { + return (gpio->ospeedr >> STM_OSPEEDR_SHIFT(pin)) & STM_OSPEEDR_MASK; +} + +#define STM_PUPDR_SHIFT(pin) ((pin) << 1) +#define STM_PUPDR_MASK 3 +#define STM_PUPDR_NONE 0 +#define STM_PUPDR_PULL_UP 1 +#define STM_PUPDR_PULL_DOWN 2 +#define STM_PUPDR_RESERVED 3 + +static inline void +stm_pupdr_set(struct stm_gpio *gpio, int pin, uint32_t value) { + gpio->pupdr = ((gpio->pupdr & + ~(STM_PUPDR_MASK << STM_PUPDR_SHIFT(pin))) | + value << STM_PUPDR_SHIFT(pin)); +} + +static inline uint32_t +stm_pupdr_get(struct stm_gpio *gpio, int pin) { + return (gpio->pupdr >> STM_PUPDR_SHIFT(pin)) & STM_PUPDR_MASK; +} + +#define STM_AFR_SHIFT(pin) ((pin) << 2) +#define STM_AFR_MASK 0xf +#define STM_AFR_NONE 0 +#define STM_AFR_AF0 0x0 +#define STM_AFR_AF1 0x1 +#define STM_AFR_AF2 0x2 +#define STM_AFR_AF3 0x3 +#define STM_AFR_AF4 0x4 +#define STM_AFR_AF5 0x5 +#define STM_AFR_AF6 0x6 +#define STM_AFR_AF7 0x7 +#define STM_AFR_AF8 0x8 +#define STM_AFR_AF9 0x9 +#define STM_AFR_AF10 0xa +#define STM_AFR_AF11 0xb +#define STM_AFR_AF12 0xc +#define STM_AFR_AF13 0xd +#define STM_AFR_AF14 0xe +#define STM_AFR_AF15 0xf + +static inline void +stm_afr_set(struct stm_gpio *gpio, int pin, uint32_t value) { + /* + * Set alternate pin mode too + */ + stm_moder_set(gpio, pin, STM_MODER_ALTERNATE); + if (pin < 8) + gpio->afrl = ((gpio->afrl & + ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) | + value << STM_AFR_SHIFT(pin)); + else { + pin -= 8; + gpio->afrh = ((gpio->afrh & + ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) | + value << STM_AFR_SHIFT(pin)); + } +} + +static inline uint32_t +stm_afr_get(struct stm_gpio *gpio, int pin) { + if (pin < 8) + return (gpio->afrl >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK; + else { + pin -= 8; + return (gpio->afrh >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK; + } +} + +static inline void +stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) { + /* Use the bit set/reset register to do this atomically */ + gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin); +} + +static inline uint8_t +stm_gpio_get(struct stm_gpio *gpio, int pin) { + return (gpio->idr >> pin) & 1; +} + +static inline uint16_t +stm_gpio_get_all(struct stm_gpio *gpio) { + return gpio->idr; +} + +/* + * We can't define these in registers.ld or our fancy + * ao_enable_gpio macro will expand into a huge pile of code + * as the compiler won't do correct constant folding and + * dead-code elimination + */ + +extern struct stm_gpio stm_gpioa; +extern struct stm_gpio stm_gpiob; +extern struct stm_gpio stm_gpioc; +extern struct stm_gpio stm_gpiod; +extern struct stm_gpio stm_gpioe; +extern struct stm_gpio stm_gpiof; +extern struct stm_gpio stm_gpiog; +extern struct stm_gpio stm_gpioh; + +#define stm_gpioa (*((struct stm_gpio *) 0x40020000)) +#define stm_gpiob (*((struct stm_gpio *) 0x40020400)) +#define stm_gpioc (*((struct stm_gpio *) 0x40020800)) +#define stm_gpiod (*((struct stm_gpio *) 0x40020c00)) +#define stm_gpioe (*((struct stm_gpio *) 0x40021000)) +#define stm_gpiof (*((struct stm_gpio *) 0x40021400)) +#define stm_gpiog (*((struct stm_gpio *) 0x40021800)) +#define stm_gpioh (*((struct stm_gpio *) 0x40021c00)) + +struct stm_scb { + vuint32_t cpuid; + vuint32_t icsr; + vuint32_t vtor; + vuint32_t aircr; + + vuint32_t scr; + vuint32_t ccr; + vuint32_t shpr1; + vuint32_t shpr2; + + vuint32_t shpr3; + vuint32_t shcsr; + vuint32_t cfsr; + vuint32_t hfsr; + + vuint32_t dfsr; + vuint32_t mmcar; + vuint32_t bcar; + vuint32_t afsr; + + vuint32_t id_pfr0; + vuint32_t id_pfr1; + vuint32_t id_dfr0; + vuint32_t id_afr0; + + vuint32_t id_mmfr0; + vuint32_t id_mmfr1; + vuint32_t id_mmfr2; + vuint32_t id_mmfr3; + + vuint32_t id_isar0; + vuint32_t id_isar1; + vuint32_t id_isar2; + vuint32_t id_isar3; + + vuint32_t id_isar4; + vuint32_t pad_d74; + vuint32_t pad_d78; + vuint32_t pad_d7c; + + vuint32_t pad_d80; + vuint32_t pad_d84; + vuint32_t cpacr; + vuint32_t pad_d8c; + + vuint8_t pad_d90[0xf00 - 0xd90]; + + vuint32_t stir; +}; + +extern struct stm_scb stm_scb; + +#define stm_scb (*((struct stm_scb *) 0xe000ed00)) + +#define STM_SCB_CPACR_CP(n) ((n) <<1) +#define STM_SCB_CPACR_DENIED 0 +#define STM_SCB_CPACR_PRIVILEGED 1 +#define STM_SCB_CPACR_RESERVED 2 +#define STM_SCB_CPACR_FULL 3 +#define STM_SCB_CPACR_FP0 STM_SCB_CPACR_CP(10) +#define STM_SCB_CPACR_FP1 STM_SCB_CPACR_CP(11) + +/* The SYSTICK starts at 0xe000e010 */ + +struct stm_systick { + vuint32_t csr; + vuint32_t rvr; + vuint32_t cvr; + vuint32_t calib; +}; + +extern struct stm_systick stm_systick; + +#define stm_systick (*((struct stm_systick *) 0xe000e010)) + +#define STM_SYSTICK_CSR_ENABLE 0 +#define STM_SYSTICK_CSR_TICKINT 1 +#define STM_SYSTICK_CSR_CLKSOURCE 2 +#define STM_SYSTICK_CSR_CLKSOURCE_AHB_8 0 +#define STM_SYSTICK_CSR_CLKSOURCE_AHB 1 +#define STM_SYSTICK_CSR_COUNTFLAG 16 + +/* Errata 2.1.5 + + Delay after an RCC peripheral clock enabling + + Description + + A delay between an RCC peripheral clock enable and the effective + peripheral enabling should be taken into account in order to manage + the peripheral read/write to registers. + + This delay depends on the peripheral’s mapping: + + • If the peripheral is mapped on AHB: the delay should be equal to + 2 AHB cycles. + + • If the peripheral is mapped on APB: the delay should be equal to + 1 + (AHB/APB prescaler) cycles. + + Workarounds + + 1. Use the DSB instruction to stall the Cortex-M4 CPU pipeline + until the instruction is completed. + + 2. Insert “n” NOPs between the RCC enable bit write and the + peripheral register writes +*/ + +static inline void +stm32f4_set_rcc(uint32_t *rcc, uint32_t value) +{ + *rcc = value; + asm("dsb"); +} + +/* Errata 2.1.8 + + In some specific cases, DMA2 data corruption occurs when managing + AHB and APB2 peripherals in a concurrent way + + Description + + When the DMA2 is managing concurrent requests of AHB and APB2 + peripherals, the transfer on the AHB could be performed several + times. + + Impacted peripheral are: + + • Quad-SPI: indirect mode read and write transfers + + • FSMC: read and write operation with external device having FIFO + + • GPIO: DMA2 transfers to GPIO registers (in memory-to-peripheral + transfer mode).The transfers from GPIOs register are not + impacted. + + + The data corruption is due to multiple DMA2 accesses over AHB + peripheral port impacting peripherals embedding a FIFO. + + For transfer to the internal SRAM through the DMA2 AHB peripheral + port the accesses could be performed several times but without data + corruptions in cases of concurrent requests. + + Workaround + + • The DMA2 AHB memory port must be used when reading/writing + from/to Quad-SPI and FSMC instead of DMA2 AHB default peripheral + port. + + • The DMA2 AHB memory port must be used when writing to GPIOs + instead of DMA2 AHB default peripheral port. + + Refer to application note AN4031 section “Take benefits of DMA2 + controller and system architecture flexibility” for more details + about DMA controller feature. + +*/ + + + +#endif /* _STM32F4_H_ */ -- 2.30.2