├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json └── settings.json ├── Makefile ├── README.md ├── fsbl ├── Makefile ├── fix_header.py ├── include │ ├── board_dk2.hpp │ ├── drivers │ │ ├── clk.hpp │ │ ├── ddr.hpp │ │ ├── gpio.hpp │ │ ├── i2c.hpp │ │ ├── mmio.hpp │ │ └── rcc.hpp │ ├── stm32mp1.hpp │ ├── types.hpp │ ├── units.hpp │ └── utils.hpp ├── linker.ld └── source │ ├── crt0.s │ ├── drivers │ ├── clk.cpp │ ├── ddr.cpp │ ├── gpio.cpp │ └── i2c.cpp │ ├── main.cpp │ └── utils.cpp ├── genimage.cfg ├── genimage.sh └── start_openocd.sh /.gitignore: -------------------------------------------------------------------------------- 1 | fsbl/build/* 2 | fsbl/fsbl.stm32 3 | 4 | tmp/ 5 | genimage.tmp/ 6 | 7 | sdcard.img -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "STM32MP1", 5 | "includePath": [ 6 | "${workspaceFolder}/fsbl/include", 7 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/STM32MP1xx_HAL_Driver/Inc", 8 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/CMSIS", 9 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/CMSIS/Include", 10 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/CMSIS/Core/Include", 11 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/CMSIS/Core_A/Include", 12 | "${workspaceFolder}/fsbl/stm32mp1/Drivers/CMSIS/Device/ST/STM32MP1xx/Include" 13 | ], 14 | "defines": [ "CORE_CA7", "STM32MP157Cxx", "USE_FULL_LL_DRIVER" ], 15 | "compilerPath": "${env:HOME}/buildroot/output/host/bin/arm-linux-g++", 16 | "cStandard": "c11", 17 | "cppStandard": "gnu++20", 18 | "intelliSenseMode": "gcc-arm" 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug", 9 | "type": "gdb", 10 | "request": "launch", 11 | "cwd": "${workspaceRoot}", 12 | "target": "${workspaceRoot}/fsbl/build/fsbl.elf", 13 | "gdbpath" : "/bin/arm-none-eabi-gdb", 14 | "autorun": [ 15 | "target remote tcp:localhost:3333", 16 | "load ./fsbl/build/fsbl.elf", 17 | "file ./fsbl/build/fsbl.elf", 18 | "b _start", 19 | "j _start" 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "type_traits": "cpp", 4 | "concepts": "cpp", 5 | "array": "cpp", 6 | "atomic": "cpp", 7 | "bit": "cpp", 8 | "*.tcc": "cpp", 9 | "cctype": "cpp", 10 | "chrono": "cpp", 11 | "clocale": "cpp", 12 | "cmath": "cpp", 13 | "compare": "cpp", 14 | "condition_variable": "cpp", 15 | "cstdarg": "cpp", 16 | "cstddef": "cpp", 17 | "cstdint": "cpp", 18 | "cstdio": "cpp", 19 | "cstdlib": "cpp", 20 | "cstring": "cpp", 21 | "ctime": "cpp", 22 | "cwchar": "cpp", 23 | "cwctype": "cpp", 24 | "deque": "cpp", 25 | "list": "cpp", 26 | "map": "cpp", 27 | "set": "cpp", 28 | "unordered_map": "cpp", 29 | "unordered_set": "cpp", 30 | "vector": "cpp", 31 | "exception": "cpp", 32 | "algorithm": "cpp", 33 | "functional": "cpp", 34 | "iterator": "cpp", 35 | "memory": "cpp", 36 | "memory_resource": "cpp", 37 | "numeric": "cpp", 38 | "optional": "cpp", 39 | "random": "cpp", 40 | "ratio": "cpp", 41 | "string": "cpp", 42 | "string_view": "cpp", 43 | "system_error": "cpp", 44 | "tuple": "cpp", 45 | "utility": "cpp", 46 | "fstream": "cpp", 47 | "future": "cpp", 48 | "initializer_list": "cpp", 49 | "iomanip": "cpp", 50 | "iosfwd": "cpp", 51 | "iostream": "cpp", 52 | "istream": "cpp", 53 | "limits": "cpp", 54 | "mutex": "cpp", 55 | "new": "cpp", 56 | "ostream": "cpp", 57 | "ranges": "cpp", 58 | "sstream": "cpp", 59 | "stdexcept": "cpp", 60 | "stop_token": "cpp", 61 | "streambuf": "cpp", 62 | "thread": "cpp", 63 | "cinttypes": "cpp", 64 | "typeinfo": "cpp", 65 | "valarray": "cpp", 66 | "variant": "cpp", 67 | "cstdbool": "cpp" 68 | } 69 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_PARTS := fsbl 2 | 3 | .PHONY: all clean $(PROJECT_PARTS) 4 | 5 | all: $(PROJECT_PARTS) 6 | ./genimage.sh -c genimage.cfg 7 | 8 | $(PROJECT_PARTS): 9 | $(MAKE) -C $@ 10 | 11 | clean: 12 | $(MAKE) -C fsbl clean 13 | rm -rf sdcard.img 14 | rm -rf genimage.tmp 15 | rm -rf tmp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STM32MP1OS 2 | 3 | A operating system to be running on the STM32MP157C-DK2 development board without the use of any of ST Microelectronic's proprietary binaries, code or tools. 4 | 5 | ## Overview 6 | 7 | - /fsbl : The first stage bootlaoder, loaded by the bootROM 8 | 9 | ## Building an image 10 | 11 | ARM32 gcc (e.g `arm-none-eabi-gcc` or `arm-linux-gcc`), a standard library such as `arm-none-eabi-newlib` and `genimage` is required to build a working image. They can be found either within buildroot or on the AUR. 12 | 13 | The build process is as easy as typing `make` in the command line. 14 | 15 | ## Debugging 16 | 17 | For debugging a very up-to-date version of `openocd` is required to support the stm32mp1. Easiest way to get this is through the `openocd-git` AUR package. Additionally ARM32 gdb (e.g `arm-none-eabi-gdb` or `arm-linux-gdb`) is needed. The project includes a VSCode launch script to make use of the integrated debugger tools. 18 | 19 | To start debugging, start the openocd server by running 20 | 21 | ```sh 22 | $ ./start_openocd.sh 23 | Open On-Chip Debugger 0.10.0+dev-01372-gfa9a4d4db-dirty (2020-08-08-12:00) 24 | Licensed under GNU GPL v2 25 | ... 26 | ``` 27 | -------------------------------------------------------------------------------- /fsbl/Makefile: -------------------------------------------------------------------------------- 1 | TOPDIR ?= $(CURDIR) 2 | 3 | CROSS_COMPILER_PATH := 4 | PREFIX := arm-none-eabi- 5 | 6 | ARM_GCC := $(CROSS_COMPILER_PATH)$(PREFIX)gcc 7 | ARM_G++ := $(CROSS_COMPILER_PATH)$(PREFIX)g++ 8 | ARM_OBJCOPY := $(CROSS_COMPILER_PATH)$(PREFIX)objcopy 9 | ARM_LD := $(CROSS_COMPILER_PATH)$(PREFIX)ld 10 | 11 | 12 | TOPDIR ?= $(CURDIR) 13 | 14 | BUILD := build 15 | SOURCES_TOP := source 16 | SOURCES += $(foreach dir,$(SOURCES_TOP),$(shell find $(dir) -type d 2>/dev/null)) 17 | INCLUDES := include 18 | 19 | ARCH := -fPIE 20 | 21 | null := 22 | SPACE := $(null) $(null) 23 | 24 | DEFINES += -DCORE_CA7 -DSTM32MP157Cxx 25 | 26 | COMMONFLAGS := -g -O3 -ffunction-sections -fPIE \ 27 | $(ARCH) $(DEFINES) \ 28 | $(INCLUDE) 29 | 30 | CFLAGS := $(COMMONFLAGS) 31 | 32 | CPPFLAGS := $(COMMONFLAGS) -std=gnu++2a -fno-exceptions 33 | 34 | ASFLAGS := -g $(ARCH) 35 | LDFLAGS := -g -nostartfiles -T $(TOPDIR)/linker.ld 36 | 37 | 38 | ifneq ($(BUILD),$(notdir $(CURDIR))) 39 | 40 | export TOPDIR := $(CURDIR) 41 | 42 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 43 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 44 | 45 | export DEPSDIR := $(CURDIR)/$(BUILD) 46 | 47 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 48 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 49 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 50 | 51 | 52 | export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 53 | 54 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 55 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 56 | -I$(CURDIR)/$(BUILD) 57 | 58 | 59 | .PHONY: $(BUILD) clean all 60 | 61 | all: $(BUILD) 62 | 63 | $(BUILD): 64 | @[ -d $@ ] || mkdir -p $@ $(BUILD) 65 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 66 | 67 | clean: 68 | rm -fr $(BUILD) $(TOPDIR)/fsbl.stm32 69 | 70 | else 71 | 72 | .PHONY: all 73 | 74 | DEPENDS := $(OFILES:.o=.d) 75 | 76 | all: $(TOPDIR)/fsbl.stm32 77 | 78 | $(TOPDIR)/fsbl.stm32: fsbl.bin 79 | python3 $(TOPDIR)/fix_header.py $< $@ 80 | 81 | fsbl.bin: fsbl.elf 82 | $(ARM_OBJCOPY) -S -O binary $< $@ 83 | 84 | fsbl.elf: $(OFILES) 85 | 86 | %.o: %.cpp 87 | @mkdir -p $(@D) 88 | $(ARM_G++) -MMD -MP -MF $*.d $(CPPFLAGS) -c $< -o $@ 89 | 90 | %.o: %.c 91 | @mkdir -p $(@D) 92 | $(ARM_GCC) -MMD -MP -MF $*.d $(CFLAGS) -c $< -o $@ 93 | 94 | %.o: %.s 95 | @mkdir -p $(@D) 96 | $(ARM_GCC) -MMD -MP -MF $*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ 97 | 98 | %.elf: 99 | $(ARM_LD) $(LDFLAGS) $(OFILES) -o $@ 100 | 101 | -include $(DEPENDS) 102 | 103 | endif -------------------------------------------------------------------------------- /fsbl/fix_header.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import sys 3 | 4 | with open(sys.argv[1], "rb") as fsbl_file: 5 | fsbl = fsbl_file.read(); 6 | 7 | if fsbl[:4] == b"STM2": 8 | exit("Header already fixed") 9 | 10 | payload = bytearray(fsbl[0x100:]) 11 | 12 | header = struct.pack(" 5 | 6 | namespace mp1::mmio { 7 | 8 | constexpr static inline u32 NoChange = 0x00; 9 | 10 | constexpr u32 bit(u8 bit) { 11 | return (1U << static_cast(bit)); 12 | } 13 | 14 | constexpr addr_t toAddress(auto base, auto offset) { 15 | return static_cast(base) + static_cast(offset); 16 | } 17 | 18 | template 19 | void write(addr_t addr, T value) { 20 | *reinterpret_cast(addr) = value; 21 | } 22 | 23 | template 24 | T read(addr_t addr) { 25 | return *reinterpret_cast(addr); 26 | } 27 | 28 | template 29 | void modify(addr_t addr, T set, T clear) { 30 | *reinterpret_cast(addr) = ((read(addr) & ~clear) | set); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /fsbl/include/drivers/rcc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stm32mp1.hpp" 4 | 5 | namespace mp1::rcc { 6 | 7 | 8 | } -------------------------------------------------------------------------------- /fsbl/include/stm32mp1.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types.hpp" 8 | #include "units.hpp" 9 | 10 | #define PACKED __attribute__((packed)) 11 | #define FORCE_INLINE inline __attribute__((always_inline)) 12 | 13 | #define PADDING(addr, next) std::uint8_t padding_x##addr[next - addr] 14 | 15 | namespace mp1 { 16 | 17 | } -------------------------------------------------------------------------------- /fsbl/include/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mp1 { 4 | 5 | using u8 = std::uint8_t; 6 | using u16 = std::uint16_t; 7 | using u32 = std::uint32_t; 8 | using u64 = std::uint64_t; 9 | 10 | using s8 = std::int8_t; 11 | using s16 = std::int16_t; 12 | using s32 = std::int32_t; 13 | using s64 = std::int64_t; 14 | 15 | using vu8 = volatile std::uint8_t; 16 | using vu16 = volatile std::uint16_t; 17 | using vu32 = volatile std::uint32_t; 18 | using vu64 = volatile std::uint64_t; 19 | 20 | using vs8 = volatile std::uint8_t; 21 | using vs16 = volatile std::uint16_t; 22 | using vs32 = volatile std::uint32_t; 23 | using vs64 = volatile std::uint64_t; 24 | 25 | 26 | using reg_t = mp1::vu32; 27 | using addr_t = mp1::u32; 28 | using offset_t = mp1::s32; 29 | 30 | } -------------------------------------------------------------------------------- /fsbl/include/units.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.hpp" 4 | 5 | namespace mp1 { 6 | 7 | /* Frequency [Hz] */ 8 | 9 | using freq_t = u64; 10 | 11 | constexpr freq_t operator ""_Hz(unsigned long long value) { 12 | return value; 13 | } 14 | 15 | constexpr freq_t operator ""_kHz(unsigned long long value) { 16 | return value * 1000ULL; 17 | } 18 | 19 | constexpr freq_t operator ""_kHz(long double value) { 20 | return static_cast(value * 1000ULL); 21 | } 22 | 23 | constexpr freq_t operator ""_MHz(unsigned long long value) { 24 | return value * 1000'0000ULL; 25 | } 26 | 27 | constexpr freq_t operator ""_MHz(long double value) { 28 | return static_cast(value * 1000'0000ULL); 29 | } 30 | 31 | 32 | 33 | 34 | /* Voltage [V] */ 35 | 36 | using volt_t = u64; 37 | 38 | constexpr volt_t operator ""_mV(unsigned long long value) { 39 | return value; 40 | } 41 | 42 | constexpr volt_t operator ""_V(unsigned long long value) { 43 | return value * 1000ULL; 44 | } 45 | 46 | constexpr volt_t operator ""_V(long double value) { 47 | return static_cast(value * 1000ULL); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /fsbl/include/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stm32mp1.hpp" 4 | #include 5 | #include 6 | 7 | namespace mp1::utl { 8 | 9 | template 10 | consteval T encodeMagic(const char* magic) { 11 | /*if (!(std::strlen(magic) <= 8 && std::strlen(magic) <= sizeof(T))) 12 | throw "Magic too long!";*/ 13 | 14 | T encoded = 0; 15 | 16 | for (offset_t offset = 0; offset < std::strlen(magic); offset++) 17 | encoded |= (*(magic + offset)) << (offset * 8); 18 | 19 | return encoded; 20 | } 21 | 22 | [[noreturn]] void panic(); 23 | 24 | } -------------------------------------------------------------------------------- /fsbl/linker.ld: -------------------------------------------------------------------------------- 1 | /* SYSRAM starts at 0x2FFC'0000 but first 0x2400 bytes are reserved for 2 | the BootROM data/bss segments. FSBL will be loaded at 0x2FFC2400. 3 | */ 4 | MEMORY { 5 | SYSRAM (rwx) : ORIGIN = 0x2FFC2400, LENGTH = 256K 6 | } 7 | 8 | ENTRY(_start); 9 | 10 | SECTIONS { 11 | /* 0x100 bytes at the start reserved for header */ 12 | .header : { 13 | FILL(0xFF); 14 | BYTE(0xFF); 15 | . = 0x100; 16 | } >SYSRAM 17 | 18 | .text : { 19 | *(.crt0) /* Make sure entry point is actually right after the header */ 20 | *(.text) 21 | *(.text*) 22 | 23 | KEEP(*(.init)) 24 | KEEP(*(.fini)) 25 | } >SYSRAM 26 | 27 | .rodata : { 28 | . = ALIGN(4); 29 | *(.rodata) 30 | *(.rodata*) 31 | . = ALIGN(4); 32 | } >SYSRAM 33 | 34 | .preinit_array : { 35 | PROVIDE_HIDDEN (__preinit_array_start = .); 36 | KEEP (*(.preinit_array*)) 37 | PROVIDE_HIDDEN (__preinit_array_end = .); 38 | } >SYSRAM 39 | 40 | .init_array : { 41 | PROVIDE_HIDDEN (__init_array_start = .); 42 | KEEP (*(SORT(.init_array.*))) 43 | KEEP (*(.init_array*)) 44 | PROVIDE_HIDDEN (__init_array_end = .); 45 | } >SYSRAM 46 | 47 | .fini_array : { 48 | PROVIDE_HIDDEN (__fini_array_start = .); 49 | KEEP (*(SORT(.fini_array.*))) 50 | KEEP (*(.fini_array*)) 51 | PROVIDE_HIDDEN (__fini_array_end = .); 52 | } >SYSRAM 53 | 54 | .data : { 55 | . = ALIGN(4); 56 | _data_begin_ = .; 57 | *(.data) 58 | *(.data*) 59 | 60 | . = ALIGN(4); 61 | _data_end_ = .; 62 | } >SYSRAM 63 | 64 | .bss : { 65 | . = ALIGN(4); 66 | _bss_start_ = .; 67 | *(.bss) 68 | *(.bss*) 69 | *(COMMON) 70 | . = ALIGN(4); 71 | _bss_end_ = .; 72 | } >SYSRAM 73 | 74 | 75 | } -------------------------------------------------------------------------------- /fsbl/source/crt0.s: -------------------------------------------------------------------------------- 1 | .syntax unified 2 | .cpu cortex-a7 3 | .fpu softvfp 4 | .thumb 5 | 6 | .global _start 7 | .section .crt0 8 | _start: 9 | // Clear bss segment 10 | /*movs R0, #0x00 11 | ldr R1, =_bss_start_ 12 | ldr R2, =_bss_end_ 13 | 14 | BssFillLoop: 15 | str R0, [R1, #4]! 16 | cmp R1, R2 17 | bne BssFillLoop*/ 18 | 19 | // Setup Stack pointer 20 | ldr R0, =StackPointerStart 21 | ldr R0, [R0] 22 | mov SP, R0 23 | 24 | // Branch to main 25 | ldr R0, =main 26 | blx R0 27 | bx lr 28 | 29 | .section .rodata 30 | 31 | StackPointerStart: 32 | .word 0x30000000 // End of SYSRAM 33 | -------------------------------------------------------------------------------- /fsbl/source/drivers/clk.cpp: -------------------------------------------------------------------------------- 1 | #include "drivers/clk.hpp" 2 | 3 | #include "drivers/mmio.hpp" 4 | #include "utils.hpp" 5 | 6 | namespace mp1::clk { 7 | 8 | void enableOscillator(Oscillator oscillator, bool force) { 9 | if (!force) { 10 | switch (oscillator) { 11 | case Oscillator::HSI: 12 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(0), mmio::NoChange); 13 | break; 14 | case Oscillator::CSI: 15 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(4), mmio::NoChange); 16 | break; 17 | case Oscillator::HSE: 18 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(8), mmio::NoChange); 19 | break; 20 | default: utl::panic(); 21 | } 22 | } else { 23 | switch (oscillator) { 24 | case Oscillator::HSI: 25 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(1), mmio::NoChange); 26 | break; 27 | case Oscillator::CSI: 28 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(5), mmio::NoChange); 29 | break; 30 | case Oscillator::HSE: 31 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENSETR), mmio::bit(9), mmio::NoChange); 32 | break; 33 | default: utl::panic(); 34 | } 35 | } 36 | } 37 | 38 | void disableOscillator(Oscillator oscillator) { 39 | switch (oscillator) { 40 | case Oscillator::HSI: 41 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENCLRR), mmio::bit(0), mmio::NoChange); 42 | break; 43 | case Oscillator::CSI: 44 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENCLRR), mmio::bit(4), mmio::NoChange); 45 | break; 46 | case Oscillator::HSE: 47 | mmio::modify(mmio::toAddress(Bank::RCC, Register::OCENCLRR), mmio::bit(8), mmio::NoChange); 48 | break; 49 | default: utl::panic(); 50 | } 51 | } 52 | 53 | void setPLLSource(PLL pll, Oscillator source) { 54 | switch (pll) { 55 | case PLL::PLL1: 56 | case PLL::PLL2: 57 | switch (source) { 58 | case Oscillator::HSI: 59 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK12SELR), mmio::NoChange, mmio::bit(0) | mmio::bit(1)); 60 | break; 61 | case Oscillator::HSE: 62 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK12SELR), mmio::bit(0), mmio::bit(0) | mmio::bit(1)); 63 | break; 64 | case Oscillator::None: 65 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK12SELR), mmio::bit(1), mmio::bit(0) | mmio::bit(1)); 66 | default: utl::panic(); 67 | } 68 | break; 69 | case PLL::PLL3: 70 | switch (source) { 71 | case Oscillator::HSI: 72 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK3SELR), u32(0x00), mmio::bit(0) | mmio::bit(1)); 73 | break; 74 | case Oscillator::HSE: 75 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK3SELR), u32(0x01), mmio::bit(0) | mmio::bit(1)); 76 | break; 77 | case Oscillator::CSI: 78 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK3SELR), u32(0x02), mmio::bit(0) | mmio::bit(1)); 79 | break; 80 | case Oscillator::None: 81 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK3SELR), u32(0x03), mmio::bit(0) | mmio::bit(1)); 82 | break; 83 | default: utl::panic(); 84 | } 85 | break; 86 | case PLL::PLL4: 87 | switch (source) { 88 | case Oscillator::HSI: 89 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK4SELR), u32(0x00), mmio::bit(0) | mmio::bit(1)); 90 | break; 91 | case Oscillator::HSE: 92 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK4SELR), u32(0x01), mmio::bit(0) | mmio::bit(1)); 93 | break; 94 | case Oscillator::CSI: 95 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK4SELR), u32(0x02), mmio::bit(0) | mmio::bit(1)); 96 | break; 97 | /* 98 | case Oscillator::I2C_CKIN: 99 | mmio::modify(mmio::toAddress(Bank::RCC, Register::RCK4SELR), 0x03U, mmio::bit(0) | mmio::bit(1)); 100 | break; 101 | */ 102 | default: utl::panic(); 103 | } 104 | break; 105 | 106 | } 107 | } 108 | 109 | void enablePeripheralClock(AHB4Peripheral peripheral) { 110 | addr_t registerAddress = mmio::toAddress(Bank::RCC, Register::MP_AHB4ENSETR); 111 | 112 | mmio::modify(registerAddress, mmio::bit(static_cast(peripheral)), mmio::NoChange); 113 | } 114 | 115 | void enablePeripheralClock(APB1Peripheral peripheral) { 116 | addr_t registerAddress = mmio::toAddress(Bank::RCC, Register::MP_APB1ENSETR); 117 | 118 | mmio::modify(registerAddress, mmio::bit(static_cast(peripheral)), mmio::NoChange); 119 | } 120 | 121 | void enablePeripheralClock(APB5Peripheral peripheral) { 122 | addr_t registerAddress = mmio::toAddress(Bank::RCC, Register::MP_APB5ENSETR); 123 | 124 | mmio::modify(registerAddress, mmio::bit(static_cast(peripheral)), mmio::NoChange); 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /fsbl/source/drivers/ddr.cpp: -------------------------------------------------------------------------------- 1 | #include "drivers/ddr.hpp" 2 | 3 | #include "drivers/mmio.hpp" 4 | 5 | namespace mp1::ddr { 6 | 7 | Type getConnectedType() { 8 | reg_t mstr = mmio::read(mmio::toAddress(Bank::DDRCTRL, Register::MSTR)); 9 | 10 | if (mstr & mmio::bit(0) != 0) 11 | return Type::DDR3; 12 | else if (mstr & mmio::bit(2) != 0) 13 | return Type::LPDDR2; 14 | else if (mstr & mmio::bit(3) != 0) 15 | return Type::LPDDR3; 16 | else 17 | return Type::Invalid; 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /fsbl/source/drivers/gpio.cpp: -------------------------------------------------------------------------------- 1 | #include "drivers/gpio.hpp" 2 | #include "drivers/mmio.hpp" 3 | 4 | namespace mp1::gpio { 5 | 6 | void configure(Bank bank, u8 pin, Mode mode, Type type, Speed speed, PullResistor pullResistor) noexcept { 7 | mmio::modify(mmio::toAddress(bank, Register::MODER), static_cast(mode) << (pin * 2), 0b11 << (pin * 2)); 8 | mmio::modify(mmio::toAddress(bank, Register::OTYPER), static_cast(type) << pin, 0b1 << pin); 9 | mmio::modify(mmio::toAddress(bank, Register::OSPEEDR), static_cast(speed) << (pin * 2), 0b11 << (pin * 2)); 10 | mmio::modify(mmio::toAddress(bank, Register::PUPDR), static_cast(pullResistor) << (pin * 2), 0b11 << (pin * 2)); 11 | } 12 | 13 | void set(Bank bank, u8 pin, Status status) noexcept { 14 | if (status == Status::Enabled) 15 | mmio::write(mmio::toAddress(bank, Register::BSRR), 0b1 << pin); 16 | else 17 | mmio::write(mmio::toAddress(bank, Register::BSRR), 0b1 << (pin + 16)); 18 | } 19 | 20 | Status get(Bank bank, u8 pin) noexcept { 21 | return mmio::read(mmio::toAddress(bank, Register::IDR)) & (0b1 << pin) > 0 ? Status::Enabled : Status::Disabled; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /fsbl/source/drivers/i2c.cpp: -------------------------------------------------------------------------------- 1 | #include "drivers/i2c.hpp" 2 | 3 | #include "drivers/mmio.hpp" 4 | #include "utils.hpp" 5 | 6 | namespace mp1::i2c { 7 | 8 | void enableInterface(Bank bank) { 9 | // Enable Analog noise filter 10 | mmio::modify(mmio::toAddress(bank, Register::CR1), mmio::bit(12), mmio::NoChange); 11 | 12 | // Configure timing register with following settings: Standard mode, 100kHz, Analog filter 13 | mmio::write(mmio::toAddress(bank, Register::TIMINGR), 0x1070'7DBC); 14 | 15 | // Set PE (Peripheral Enable) bit in register CR1 16 | mmio::modify(mmio::toAddress(bank, Register::CR1), mmio::bit(0), mmio::NoChange); 17 | } 18 | 19 | void disableInterface(Bank bank) { 20 | // Clear PE (Peripheral Enable) bit in register CR1 21 | mmio::modify(mmio::toAddress(bank, Register::CR1), mmio::NoChange, mmio::bit(0)); 22 | } 23 | 24 | void transmit(Bank bank, size_t numBytes, u8 *data) { 25 | if (numBytes > 0xFF) 26 | utl::panic(); 27 | 28 | // Set NBYTES value 29 | mmio::modify(mmio::toAddress(bank, Register::CR2), (numBytes & 0xFF) << 16, 0xFF << 16); 30 | 31 | // Enable auto end mode 32 | mmio::modify(mmio::toAddress(bank, Register::CR2), mmio::bit(25), mmio::NoChange); 33 | 34 | // Transmit N bytes 35 | for (u16 i = 0; i < numBytes; i++) { 36 | // Wait till transmission is complete 37 | do { 38 | // Bail out if a NACK was received 39 | if (mmio::read(mmio::toAddress(bank, Register::ISR)) & mmio::bit(4) != 0) 40 | return; 41 | } 42 | while (mmio::read(mmio::toAddress(bank, Register::ISR)) & mmio::bit(1) == 0); 43 | 44 | // Write next byte to transmit into TXDR 45 | mmio::write(mmio::toAddress(bank, Register::TXDR), data[i]); 46 | 47 | } 48 | 49 | } 50 | 51 | void receive(Bank bank, size_t numBytes, u8 *data) { 52 | if (numBytes > 0xFF) 53 | utl::panic(); 54 | 55 | // Set NBYTES value 56 | mmio::modify(mmio::toAddress(bank, Register::CR2), (numBytes & 0xFF) << 16, 0xFF << 16); 57 | 58 | // Enable auto end mode 59 | mmio::modify(mmio::toAddress(bank, Register::CR2), mmio::bit(25), mmio::NoChange); 60 | 61 | // Receive N bytes 62 | for (u16 i = 0; i < numBytes; i++) { 63 | // Wait till there's data in the receive register 64 | while (mmio::read(mmio::toAddress(bank, Register::ISR)) & mmio::bit(2) == 0); 65 | 66 | // Write next byte to transmit into TXDR 67 | data[i] = static_cast(mmio::read(mmio::toAddress(bank, Register::RXDR)) & 0xFF); 68 | } 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /fsbl/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stm32mp1.hpp" 2 | #include "board_dk2.hpp" 3 | 4 | #include "drivers/mmio.hpp" 5 | #include "drivers/clk.hpp" 6 | #include "drivers/gpio.hpp" 7 | #include "drivers/i2c.hpp" 8 | #include "utils.hpp" 9 | 10 | int main() { 11 | 12 | // Init clocks 13 | { 14 | using namespace mp1::clk; 15 | 16 | mp1::clk::enableOscillator(Oscillator::HSE); 17 | mp1::clk::setPLLSource(PLL::PLL1 /* | PLL::PLL2 */, Oscillator::HSE); 18 | 19 | 20 | mp1::clk::enablePeripheralClock(AHB4Peripheral::GPIOD); 21 | mp1::clk::enablePeripheralClock(AHB4Peripheral::GPIOH); 22 | 23 | mp1::clk::enablePeripheralClock(APB5Peripheral::I2C4); 24 | 25 | // Make I2C4 and I2C6 use HSI as clock source 26 | mp1::mmio::write(mp1::mmio::toAddress(mp1::clk::Bank::RCC, mp1::clk::Register::I2C46CKSELR), 0x02); 27 | } 28 | 29 | // Init GPIOs 30 | { 31 | using namespace mp1::gpio; 32 | 33 | mp1::gpio::configure(mp1::dk2::BlueLedPort, mp1::dk2::BlueLedPin, Mode::Output, Type::PushPull); 34 | mp1::gpio::set(mp1::dk2::BlueLedPort, mp1::dk2::BlueLedPin, Status::Enabled); 35 | } 36 | 37 | // Init I2C 38 | { 39 | using namespace mp1::i2c; 40 | 41 | mp1::i2c::enableInterface(Bank::I2C4); 42 | 43 | mp1::u8 send[] = { 0x33, 0x06 }; // STPMIC1 address 0x33, register 0x06 44 | mp1::u8 recv = 0; 45 | mp1::i2c::transmit(Bank::I2C4, 2, send); 46 | mp1::i2c::receive(Bank::I2C4, 1, &recv); 47 | 48 | if (recv == 0) 49 | mp1::utl::panic(); 50 | } 51 | 52 | while(true); 53 | 54 | return 0; 55 | } -------------------------------------------------------------------------------- /fsbl/source/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.hpp" 2 | 3 | #include "board_dk2.hpp" 4 | 5 | #include "drivers/clk.hpp" 6 | #include "drivers/gpio.hpp" 7 | 8 | namespace mp1::utl { 9 | 10 | [[noreturn]] void panic() { 11 | 12 | // Enable clock for GPIOD 13 | { 14 | using namespace mp1::clk; 15 | mp1::clk::enablePeripheralClock(dk2::YellowLedPortClock); 16 | } 17 | 18 | // Initiaize and enable orange panic LED 19 | { 20 | using namespace mp1::gpio; 21 | 22 | mp1::gpio::configure(dk2::YellowLedPort, dk2::YellowLedPin, Mode::Output, Type::PushPull); 23 | mp1::gpio::set(dk2::YellowLedPort, dk2::YellowLedPin, Status::Enabled); 24 | } 25 | 26 | 27 | while (true); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /genimage.cfg: -------------------------------------------------------------------------------- 1 | image sdcard.img { 2 | hdimage { 3 | gpt = "true" 4 | } 5 | 6 | partition fsbl1 { 7 | image = "fsbl/fsbl.stm32" 8 | } 9 | 10 | partition fsbl2 { 11 | image = "fsbl/fsbl.stm32" 12 | } 13 | } -------------------------------------------------------------------------------- /genimage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | die() { 4 | cat <&2 5 | Error: $@ 6 | Usage: ${0} -c GENIMAGE_CONFIG_FILE 7 | EOF 8 | exit 1 9 | } 10 | 11 | # Parse arguments and put into argument list of the script 12 | opts="$(getopt -n "${0##*/}" -o c: -- "$@")" || exit $? 13 | eval set -- "$opts" 14 | 15 | GENIMAGE_TMP="./genimage.tmp" 16 | 17 | while true ; do 18 | case "$1" in 19 | -c) 20 | GENIMAGE_CFG="${2}"; 21 | shift 2 ;; 22 | --) # Discard all non-option parameters 23 | shift 1; 24 | break ;; 25 | *) 26 | die "unknown option '${1}'" ;; 27 | esac 28 | done 29 | 30 | [ -n "${GENIMAGE_CFG}" ] || die "Missing argument" 31 | 32 | # Pass an empty rootpath. genimage makes a full copy of the given rootpath to 33 | # ${GENIMAGE_TMP}/root so passing TARGET_DIR would be a waste of time and disk 34 | # space. We don't rely on genimage to build the rootfs image, just to insert a 35 | # pre-built one in the disk image. 36 | 37 | trap 'rm -rf "${ROOTPATH_TMP}"' EXIT 38 | ROOTPATH_TMP="$(mktemp -d)" 39 | 40 | rm -rf "${GENIMAGE_TMP}" 41 | 42 | genimage \ 43 | --rootpath "${ROOTPATH_TMP}" \ 44 | --tmppath "${GENIMAGE_TMP}" \ 45 | --inputpath "${BINARIES_DIR}" \ 46 | --outputpath "${BINARIES_DIR}" \ 47 | --config "${GENIMAGE_CFG}" -------------------------------------------------------------------------------- /start_openocd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OPENOCD_SCRIPTS="/usr/share/openocd/scripts" 4 | 5 | openocd -f $OPENOCD_SCRIPTS/board/stm32mp15x_dk2.cfg -c "gdb_flash_program enable" --------------------------------------------------------------------------------