diff --git a/.github/workflows/rust_all.yml b/.github/workflows/rust_all.yml index a9cdd3e..9cd7f87 100644 --- a/.github/workflows/rust_all.yml +++ b/.github/workflows/rust_all.yml @@ -28,8 +28,9 @@ jobs: run: | rustup component add llvm-tools-preview rustup target add thumbv7em-none-eabihf - cargo install cbindgen cargo install cargo-binutils + cargo install cargo-make + cargo install cbindgen - name: Environment info run: | @@ -47,3 +48,10 @@ jobs: cargo size cargo build --release cargo size --release + + - name: Minimal buildsystem + working-directory: ${{github.workspace}}/minimal_buildsystem + run: | + cargo make ci_debug + cargo make ci_release + cargo doc diff --git a/README.md b/README.md index 2faec1b..5968109 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ # lowlevel_rust + Rust on microcontrollers + +# Projects + +- Minimal blinky + - Barebones blinky example i.e linker script to main +- Minimal buildsystem + - Initial [cargo-make](https://github.com/sagiegurari/cargo-make) framework to have configurable build options i.e extending `cargo` + +# Roadmap + +## RTOS + +## Debugging + +## Tooling diff --git a/minimal_buildsystem/.cargo/config.toml b/minimal_buildsystem/.cargo/config.toml new file mode 100644 index 0000000..6e536a7 --- /dev/null +++ b/minimal_buildsystem/.cargo/config.toml @@ -0,0 +1,43 @@ +[target.thumbv7em-none-eabihf] +# uncomment this to make `cargo run` execute programs on QEMU +# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" +rustflags = [ + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + # "-C", "link-arg=--nmagic", + + # LLD (shipped with the Rust toolchain) is used as the default linker + # "-C", "link-arg=-Tgcc_arm.ld", + + # Generate a .map file + # "-C", "link-args=-Map=minimal_buildsystem.map", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + "-C", "linker=arm-none-eabi-gcc", + "-C", "link-arg=-Wl,-Tgcc_arm.ld", + "-C", "link-arg=-Wl,-Map,minimal_buildsystem.map", + "-C", "link-arg=-nostartfiles", +] + +[build] +# Pick ONE of these compilation targets +# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ +# target = "thumbv7m-none-eabi" # Cortex-M3 +# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) +# target = "thumbv8m.base-none-eabi" # Cortex-M23 +# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU) +# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU) diff --git a/minimal_buildsystem/.gitignore b/minimal_buildsystem/.gitignore new file mode 100644 index 0000000..4145ba0 --- /dev/null +++ b/minimal_buildsystem/.gitignore @@ -0,0 +1,6 @@ +# Files +*.map +.vscode/.cortex-debug.* + +# Folders +target diff --git a/minimal_buildsystem/.vscode/launch.json b/minimal_buildsystem/.vscode/launch.json new file mode 100644 index 0000000..61fa77e --- /dev/null +++ b/minimal_buildsystem/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + "configurations": [ + { + "cwd": "${workspaceFolder}", + "executable": "target/thumbv7em-none-eabihf/debug/minimal_buildsystem", + "configFiles": [ + "stm32l4discovery.cfg" + ], + "postLaunchCommands": [ + "load", + "monitor arm semihosting enable", + ], + "name": "Rust Debug", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd" + }, + { + "cwd": "${workspaceFolder}", + "executable": "target/thumbv7em-none-eabihf/release/minimal_buildsystem", + "configFiles": [ + "stm32l4discovery.cfg" + ], + "postLaunchCommands": [ + "load", + "monitor arm semihosting enable", + ], + "name": "Rust Release", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd" + } + ] +} diff --git a/minimal_buildsystem/Cargo.toml b/minimal_buildsystem/Cargo.toml new file mode 100644 index 0000000..2f6c554 --- /dev/null +++ b/minimal_buildsystem/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "minimal_buildsystem" +version = "0.1.0" +authors = ["Niket Naidu "] +edition = "2021" +readme = "README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/minimal_buildsystem/Makefile.toml b/minimal_buildsystem/Makefile.toml new file mode 100644 index 0000000..55a3411 --- /dev/null +++ b/minimal_buildsystem/Makefile.toml @@ -0,0 +1,83 @@ +# Duckscript is used here to convert \ to / for binary output path +[tasks.build_debug] +script_runner = "@duckscript" +script = ''' +output = set ${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}/debug/${CARGO_MAKE_CRATE_NAME} +output = replace ${output} \\ / +set_env OUTPUT ${output} +exec cargo build +''' + +# Duckscript is used here to convert \ to / for binary output path +[tasks.build_release] +script_runner = "@duckscript" +script = ''' +output = set ${CARGO_MAKE_CRATE_CUSTOM_TRIPLE_TARGET_DIRECTORY}/release/${CARGO_MAKE_CRATE_NAME} +output = replace ${output} \\ / +set_env OUTPUT ${output} +exec cargo build --release +''' + +[tasks.test] +command = "cargo" +args = ["test", "--target", "${CARGO_MAKE_RUST_TARGET_TRIPLE}"] + +[tasks.flash_debug] +script_runner = "@shell" +script = ''' +openocd -f board/stm32l4discovery.cfg -c "program ${OUTPUT} verify reset exit" +''' +dependencies = ["build_debug"] + +[tasks.ci_debug] +dependencies = [ + "build_debug", + "test", + "objcopy_to_binary", + "objcopy_to_hex", + "objdump", + "size", +] + +[tasks.ci_release] +dependencies = [ + "build_release", + "test", + "objcopy_to_binary", + "objcopy_to_hex", + "objdump", + "size", +] + +# Private Tasks + +# Requires +# arm-none-eabi-size executable (ARM GCC toolchain) +# OUTPUT env variable (Set by build_*) +[tasks.size] +private = true +command = "arm-none-eabi-size" +args = ["${OUTPUT}"] + +# arm-none-eabi-objcopy executable (ARM GCC toolchain) +# OUTPUT env variable (Set by build_*) +[tasks.objcopy_to_binary] +private = true +command = "arm-none-eabi-objcopy" +args = ["-O", "binary", "${OUTPUT}", "${OUTPUT}.bin"] + +# arm-none-eabi-objcopy executable (ARM GCC toolchain) +# OUTPUT env variable (Set by build_*) +[tasks.objcopy_to_hex] +private = true +command = "arm-none-eabi-objcopy" +args = ["-O", "ihex", "${OUTPUT}", "${OUTPUT}.hex"] + +# arm-none-eabi-objdump executable (ARM GCC toolchain) +# OUTPUT env variable (Set by build_*) +[tasks.objdump] +private = true +script_runner = "@shell" +script = ''' +arm-none-eabi-objdump --source --all-headers --demangle --line-numbers --wide ${OUTPUT} > ${OUTPUT}.lst +''' diff --git a/minimal_buildsystem/README.md b/minimal_buildsystem/README.md new file mode 100644 index 0000000..9b14339 --- /dev/null +++ b/minimal_buildsystem/README.md @@ -0,0 +1,78 @@ +- [Minimal Buildsystem](#minimal-buildsystem) + - [Links](#links) + - [Pre-requisites](#pre-requisites) + - [Build system for Rust](#build-system-for-rust) + - [\[build\_debug | build\_release\]](#build_debug--build_release) + - [test](#test) + - [flash\_debug](#flash_debug) + - [\[ci\_debug | ci\_release\]](#ci_debug--ci_release) + - [doc](#doc) + +# Minimal Buildsystem + +This code has been tested on + +- B-L475-IOT01A board (STM32L475VGT6 ARM Cortex M4 CPU with FPU) + +## Links + +- [Cargo binutils](https://github.com/rust-embedded/cargo-binutils) +- [Embedded Rust book](https://doc.rust-lang.org/stable/embedded-book/) +- [Lowlevel Embedded Rust book](https://docs.rust-embedded.org/embedonomicon/) + +## Pre-requisites + +- Pre-requisites from `minimal_blinky` +- cargo install cargo-make + +## Build system for Rust + +Cargo make is used to build, run and deploy various aspects of this project. +This is because we need configurations for + +- Building microcontroller (on-target) code for different supported architectures and toolchains. + - Pre-processing (.c to .rs conversion, code generation) + - Building (convert to .elf) + - Post-processing (.elf size, .bin and .hex generation, flashing after build, CI run) +- Unit-testing functionality (off-target) using host toolchain +- Documentation generation + +Commands can be run using + +```bash +cargo make [command] +``` + +### [build_debug | build_release] + +Makes a debug or release build of the project using the microcontroller target + +See `.cargo/config.toml`, **build.target** field + +### test + +Make a build of the project using the default system host toolchain and target + +Run `rustup default` to see your system host toolchain +Run `rustup target list` to see the system host target installed for your toolchain + +### flash_debug + +Uses openocd to flash your generated `/debug/*.elf` file to the STM32 microcontroller + +### [ci_debug | ci_release] + +Single command that does the following in order + +- Build on-target code [debug | release] +- Size of on-target code +- Executes unit-tests and mocks using an off-target build +- Convert `*.elf` to `*.bin` +- Convert `*.elf` to `*.hex` +- Dump `*.elf` symbols to `*.lst` + +### doc + +This is not added to `cargo make` + +Invoke cargo doc from the root diff --git a/minimal_buildsystem/build.rs b/minimal_buildsystem/build.rs new file mode 100644 index 0000000..da8a068 --- /dev/null +++ b/minimal_buildsystem/build.rs @@ -0,0 +1,26 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::path::PathBuf; + +fn reference() { + let out: &PathBuf = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + println!("{}", out.display()); +} + +fn linker_script() { + println!("cargo:rerun-if-changed=gcc_arm.ld"); +} + +fn main() { + reference(); + linker_script(); +} diff --git a/minimal_buildsystem/gcc_arm.ld b/minimal_buildsystem/gcc_arm.ld new file mode 100644 index 0000000..8bbd1cb --- /dev/null +++ b/minimal_buildsystem/gcc_arm.ld @@ -0,0 +1,339 @@ +/****************************************************************************** + * @file gcc_arm.ld + * @brief GNU Linker Script for Cortex-M based device + * @version V2.0.0 + * @date 21. May 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + *-------- <<< Use Configuration Wizard in Context Menu >>> ------------------- + */ + +/*---------------------- Flash Configuration ---------------------------------- + Flash Configuration + Flash Base Address <0x0-0xFFFFFFFF:8> + Flash Size (in Bytes) <0x0-0xFFFFFFFF:8> + + -----------------------------------------------------------------------------*/ +__ROM_BASE = 0x08000000; +__ROM_SIZE = 0x00100000; + +/*--------------------- Embedded RAM Configuration ---------------------------- + RAM Configuration + RAM Base Address <0x0-0xFFFFFFFF:8> + RAM Size (in Bytes) <0x0-0xFFFFFFFF:8> + + -----------------------------------------------------------------------------*/ +__RAM_BASE = 0x20000000; +__RAM_SIZE = 0x00018000; + +/*--------------------- Stack / Heap Configuration ---------------------------- + Stack / Heap Configuration + Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> + Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> + + -----------------------------------------------------------------------------*/ +/** +See Diagram below + +FreeRTOS Heap 4 allocated on BSS +FreeRTOS allocation on BSS: 40K +STACK SIZE to 20K +HEAP SIZE to 10K +Remaining unallocated memory = 96 - 40 - 20 - 10 = 26K +DATA and other BSS can consume 26K +**/ +__STACK_SIZE = 20K; +__HEAP_SIZE = 10K; + +/************************************************* + * + 64K RAM +-----Stack Top +-----+ 32K RAM * + | || | | * + | \/ | | * + | | | | * + | | | | * + | /\ heap | /\ * + | || overflow | || * + .-----. +-----Heap Start * + |bbbbb| 0x2000000 * + |bbbbb| *bss * + .-----. * + |ddddd| * + |ddddd| *data * + .-----. * + 0x1000000 * + * +************************************************** +*/ + +/* + *-------------------- <<< end of configuration section >>> ------------------- + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = __ROM_BASE, LENGTH = __ROM_SIZE + RAM (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + LONG(ORIGIN(RAM) + LENGTH(RAM)); + KEEP(*(.vector_table.reset_vector)) + KEEP(*(.vector_table.exceptions)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + /* + * SG veneers: + * All SG veneers are placed in the special output section .gnu.sgstubs. Its start address + * must be set, either with the command line option �--section-start� or in a linker script, + * to indicate where to place these veneers in memory. + */ +/* + .gnu.sgstubs : + { + . = ALIGN(32); + } > FLASH +*/ + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG ((__data_end__ - __data_start__) / 4) + /* Add each additional data section here */ +/* + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) +*/ + __copy_table_end__ = .; + } > FLASH + + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG ((__bss_end__ - __bss_start__) / 4) + /* Add each additional bss section here */ +/* + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) +*/ + __zero_table_end__ = .; + } > FLASH + + /** + * Location counter can end up 2byte aligned with narrow Thumb code but + * __etext is assumed by startup code to be the LMA of a section in RAM + * which must be 4byte aligned + */ + __etext = ALIGN (4); + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data) + *(.data.*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + /* + * Secondary data section, optional + * + * Remember to add each additional data section + * to the .copy.table above to asure proper + * initialization during startup. + */ +/* + __etext2 = ALIGN (4); + + .data2 : AT (__etext2) + { + . = ALIGN(4); + __data2_start__ = .; + *(.data2) + *(.data2.*) + . = ALIGN(4); + __data2_end__ = .; + + } > RAM2 +*/ + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM AT > RAM + + /* + * Secondary bss section, optional + * + * Remember to add each additional bss section + * to the .zero.table above to asure proper + * initialization during startup. + */ +/* + .bss2 : + { + . = ALIGN(4); + __bss2_start__ = .; + *(.bss2) + *(.bss2.*) + . = ALIGN(4); + __bss2_end__ = .; + } > RAM2 AT > RAM2 +*/ + + .heap (COPY) : + { + . = ALIGN(8); + __end__ = .; + PROVIDE(end = .); + . = . + __HEAP_SIZE; + . = ALIGN(8); + __HeapLimit = .; + } > RAM + + .stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE) (COPY) : + { + . = ALIGN(8); + __StackLimit = .; + . = . + __STACK_SIZE; + . = ALIGN(8); + __StackTop = .; + } > RAM + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} + +PROVIDE(NMI = DefaultExceptionHandler); +PROVIDE(HardFault = DefaultExceptionHandler); +PROVIDE(MemManage = DefaultExceptionHandler); +PROVIDE(BusFault = DefaultExceptionHandler); +PROVIDE(UsageFault = DefaultExceptionHandler); +PROVIDE(SVCall = DefaultExceptionHandler); +PROVIDE(PendSV = DefaultExceptionHandler); +PROVIDE(SysTick = DefaultExceptionHandler); diff --git a/minimal_buildsystem/src/blink.rs b/minimal_buildsystem/src/blink.rs new file mode 100644 index 0000000..7bac3ca --- /dev/null +++ b/minimal_buildsystem/src/blink.rs @@ -0,0 +1,149 @@ +#![cfg(not(test))] + +use core::arch::asm; + +#[allow(non_snake_case)] +#[repr(C)] +struct RCC_TypeDef { + pub CR: u32, + pub ICSCR: u32, + pub CFGR: u32, + pub PLLCFGR: u32, + pub PLLSAI1CFGR: u32, + pub PLLSAI2CFGR: u32, + pub CIER: u32, + pub CIFR: u32, + pub CICR: u32, + pub RESERVED0: u32, + pub AHB1RSTR: u32, + pub AHB2RSTR: u32, + pub AHB3RSTR: u32, + pub RESERVED1: u32, + pub APB1RSTR1: u32, + pub APB1RSTR2: u32, + pub APB2RSTR: u32, + pub RESERVED2: u32, + pub AHB1ENR: u32, + pub AHB2ENR: u32, + pub AHB3ENR: u32, + pub RESERVED3: u32, + pub APB1ENR1: u32, + pub APB1ENR2: u32, + pub APB2ENR: u32, + pub RESERVED4: u32, + pub AHB1SMENR: u32, + pub AHB2SMENR: u32, + pub AHB3SMENR: u32, + pub RESERVED5: u32, + pub APB1SMENR1: u32, + pub APB1SMENR2: u32, + pub APB2SMENR: u32, + pub RESERVED6: u32, + pub CCIPR: u32, + pub RESERVED7: u32, + pub BDCR: u32, + pub CSR: u32, +} + +#[allow(non_snake_case)] +#[repr(C)] +struct GPIO_TypeDef { + pub MODER: u32, + pub OTYPER: u32, + pub OSPEEDR: u32, + pub PUPDR: u32, + pub IDR: u32, + pub ODR: u32, + pub BSRR: u32, + pub LCKR: u32, + pub AFR0: u32, + pub AFR1: u32, + pub BRR: u32, + pub ASCR: u32, +} + +pub fn _spin_delay(delay: u32) { + let mut mdelay = delay; + while mdelay != 0 { + unsafe { + asm!("nop"); + } + mdelay -= 1; + } +} + +const PERIPH_BASE: u32 = 0x4000_0000; + +const AHB1PERIPH_BASE: u32 = PERIPH_BASE + 0x0002_0000; +const RCC_BASE: u32 = AHB1PERIPH_BASE + 0x1000; + +const AHB2PERIPH_BASE: u32 = PERIPH_BASE + 0x0800_0000; +const GPIOA_BASE: u32 = AHB2PERIPH_BASE + 0x0000; + +#[allow(non_snake_case)] +pub fn blink_init() { + // RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + // GPIOA->BRR |= (1 << 5); // Reset the pin here + + // Set the mode + // GPIOA->MODER &= ~(3 << 10); + // GPIOA->MODER |= (1 << 10); // 01 00 00 00 00 00 + + // Check these registers + // GPIOA->OTYPER &= ~(1 << 5); // set to 0 + // GPIOA->OSPEEDR &= ~(3 << 10); + // GPIOA->PUPDR &= ~(3 << 10); + let safe_rcc = RCC_BASE as *mut RCC_TypeDef; + let rcc = unsafe { &mut *(safe_rcc) }; + + let safe_gpioa = GPIOA_BASE as *mut GPIO_TypeDef; + let gpioa = unsafe { &mut *(safe_gpioa) }; + + // activate GPIOA + let mut rcc_ahb2enr = unsafe { core::ptr::read_volatile(&mut rcc.AHB2ENR) }; + rcc_ahb2enr |= 1 << 0; + unsafe { core::ptr::write_volatile(&mut rcc.AHB2ENR, rcc_ahb2enr) }; + + // MODER + let mut gpioa_moder = unsafe { core::ptr::read_volatile(&mut gpioa.MODER) }; + gpioa_moder &= !(3 << 10); + gpioa_moder |= 1 << 10; + unsafe { core::ptr::write_volatile(&mut gpioa.MODER, gpioa_moder) }; + + // OTYPER + let mut gpioa_otyper = unsafe { core::ptr::read_volatile(&mut gpioa.OTYPER) }; + gpioa_otyper &= !(1 << 5); + unsafe { core::ptr::write_volatile(&mut gpioa.OTYPER, gpioa_otyper) }; + + // OSPEEDR + let mut gpioa_ospeedr = unsafe { core::ptr::read_volatile(&mut gpioa.OSPEEDR) }; + gpioa_ospeedr &= !(3 << 10); + unsafe { core::ptr::write_volatile(&mut gpioa.OSPEEDR, gpioa_ospeedr) }; + + // PUPDR + let mut gpioa_pupdr = unsafe { core::ptr::read_volatile(&mut gpioa.PUPDR) }; + gpioa_pupdr &= !(3 << 10); + unsafe { core::ptr::write_volatile(&mut gpioa.PUPDR, gpioa_pupdr) }; +} + +pub fn blink_set() { + // Set the pin here + // GPIOA->BSRR |= (1 << 5); + let safe_gpioa = GPIOA_BASE as *mut GPIO_TypeDef; + let gpioa = unsafe { &mut *(safe_gpioa) }; + + let mut gpioa_bsrr = unsafe { core::ptr::read_volatile(&mut gpioa.BSRR) }; + gpioa_bsrr |= 1 << 5; + unsafe { core::ptr::write_volatile(&mut gpioa.BSRR, gpioa_bsrr) }; +} + +pub fn blink_reset() { + // _spin_delay(1000 * 1000); + // GPIOA->BRR = (1 << 5); // Reset + let safe_gpioa = GPIOA_BASE as *mut GPIO_TypeDef; + let gpioa = unsafe { &mut *(safe_gpioa) }; + + let mut gpioa_brr = unsafe { core::ptr::read_volatile(&mut gpioa.BRR) }; + gpioa_brr |= 1 << 5; + unsafe { core::ptr::write_volatile(&mut gpioa.BRR, gpioa_brr) }; +} diff --git a/minimal_buildsystem/src/entry_point.rs b/minimal_buildsystem/src/entry_point.rs new file mode 100644 index 0000000..ac4b3fe --- /dev/null +++ b/minimal_buildsystem/src/entry_point.rs @@ -0,0 +1,84 @@ +#![cfg(not(test))] + +#[link_section = ".vector_table.reset_vector"] +#[no_mangle] +pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; + +// NOTE, All the externed modules come here +#[no_mangle] +pub unsafe extern "C" fn Reset() -> ! { + extern "C" { + // .data section + static mut __data_end__: u8; + static mut __data_start__: u8; + static mut __etext: u8; + + // .bss section + static mut __bss_start__: u8; + static mut __bss_end__: u8; + } + + // data + let vma_data_end = &__data_end__ as *const u8; + let vma_data_start = &__data_start__ as *const u8; + let lma_data_start = &__etext as *const u8; + let count: usize = vma_data_end as usize - vma_data_start as usize; + // core::ptr::copy_nonoverlapping(lma_data_start, &mut __data_start__ as *mut u8, count); + core::ptr::copy_nonoverlapping(lma_data_start, vma_data_start as *mut u8, count); + + // end + let bss_end = &__bss_end__ as *const u8; + let bss_start = &__bss_start__ as *const u8; + let count = bss_end as usize - bss_start as usize; + // core::ptr::write_bytes(&mut __bss_start__ as *mut u8, 0, count); + core::ptr::write_bytes(bss_start as *mut u8, 0, count); + + extern "Rust" { + fn main() -> !; + } + main(); +} + +pub union Vector { + reserved: u32, + handler: unsafe extern "C" fn(), +} + +extern "C" { + fn NMI(); + fn HardFault(); + fn MemManage(); + fn BusFault(); + fn UsageFault(); + fn SVCall(); + fn PendSV(); + fn SysTick(); +} + +#[link_section = ".vector_table.exceptions"] +#[no_mangle] +pub static EXCEPTIONS: [Vector; 14] = [ + Vector { handler: NMI }, + Vector { handler: HardFault }, + Vector { handler: MemManage }, + Vector { handler: BusFault }, + Vector { + handler: UsageFault, + }, + Vector { reserved: 0 }, + Vector { reserved: 0 }, + Vector { reserved: 0 }, + Vector { reserved: 0 }, + Vector { handler: SVCall }, + Vector { reserved: 0 }, + Vector { reserved: 0 }, + Vector { handler: PendSV }, + Vector { handler: SysTick }, +]; + +// TODO, Add peripheral interrupts here + +#[no_mangle] +pub extern "C" fn DefaultExceptionHandler() { + loop {} +} diff --git a/minimal_buildsystem/src/main.rs b/minimal_buildsystem/src/main.rs new file mode 100644 index 0000000..315762f --- /dev/null +++ b/minimal_buildsystem/src/main.rs @@ -0,0 +1,28 @@ +#![cfg_attr(not(test), no_std)] +#![cfg_attr(not(test), no_main)] + +mod entry_point; +mod rust_entry_point; + +mod blink; + +#[cfg(not(test))] +#[no_mangle] +fn main() -> ! { + blink::blink_init(); + blink::blink_set(); + loop { + blink::_spin_delay(100_000); + blink::blink_reset(); + blink::_spin_delay(100_000); + blink::blink_set(); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn unit_tests_work() { + assert_eq!(1, 1); + } +} diff --git a/minimal_buildsystem/src/rust_entry_point.rs b/minimal_buildsystem/src/rust_entry_point.rs new file mode 100644 index 0000000..eaa1ce1 --- /dev/null +++ b/minimal_buildsystem/src/rust_entry_point.rs @@ -0,0 +1,8 @@ +#![cfg(not(test))] + +use core::panic::PanicInfo; + +#[panic_handler] +pub fn panic(_panic: &PanicInfo<'_>) -> ! { + loop {} +} diff --git a/minimal_buildsystem/stm32l4discovery.cfg b/minimal_buildsystem/stm32l4discovery.cfg new file mode 100644 index 0000000..8b79841 --- /dev/null +++ b/minimal_buildsystem/stm32l4discovery.cfg @@ -0,0 +1,13 @@ +# Explicitly for the STM32L476 discovery board: +# http://www.st.com/web/en/catalog/tools/PF261635 +# but perfectly functional for any other STM32L4 board connected via +# an stlink-v2-1 interface. +# This is for STM32L4 boards that are connected via stlink-v2-1. + +source [find interface/stlink.cfg] + +transport select hla_swd + +source [find target/stm32l4x.cfg] + +reset_config srst_only