I’m trying to get some Rust code running on a STM32F4 based board we are using at my university. As a first step I want to transform some low-level C code into the Rust equivalent, just to make sure I get the tooling correct. The only thing this code does is to initialize the GPIO D port, and then continuously writing the bits read on the pins 8-15 to pins 0-7.
Following piece of C works well when compiled with gcc:
#define GPIO_D 0x40020C00
#define GPIO_D_MODER GPIO_D
#define GPIO_D_IDR_H GPIO_D + 0x11
#define GPIO_D_ODR_L GPIO_D + 0x14
void startup(void) __attribute__((naked)) __attribute__((section(".start_section")));
void startup(void) {
asm volatile(" NOP\n"
" LDR SP,=0x2001C000\n"
" BL main\n"
".L1: B .L1\n"
);
}
int main(void) {
*((volatile unsigned int *) GPIO_D_MODER) = 0x00005555;
while(1) {
*((volatile unsigned char *) GPIO_D_ODR_L) = *((volatile unsigned char *) GPIO_D_IDR_H);
}
return 0;
}
This is the makefile used for the compiling, linking and conversion to appropriate loadfile (somewhat shortened, but should contain all relevant parts):
# Compiler
CC = $(TCPATH)arm-none-eabi-gcc
CFLAGS = -Wall -O0 -g \
-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard \
-I$(INCLIB)
# Linker
LD = $(T)arm-none-eabi-ld
LDFLAGS = -nostartfiles -Tmd407-ram.x
# Object copy
OC = $(TCPATH)arm-none-eabi-objcopy
OCFLAGS = -S -O srec
### Compile, link and create load file
$(OUT)/$(PROJ).s19 : $(OUT)/$(PROJ).elf
$(OC) $(OCFLAGS) $(OUT)/$(PROJ).elf $(OUT)/$(PROJ).s19
$(OUT)/$(PROJ).elf : $(OBJS)
$(LD) $(LDFLAGS) $(OBJS) -o $(OUT)/$(PROJ).elf
And this is the linker script used:
/*
Default linker script for MD407 (STM32F407)
All code and data goes to RAM
*/
/* Memory Spaces Definitions */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K
}
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.start_section) /* startup code */
*(.text) /* remaining code */
*(.text.*)
*(.data) /* initialised data */
*(.data.*)
*(.rodata) /* read-only data (constants) */
*(.rodata.*)
*(.bss) /* uninitialised data */
*(COMMON)
. = ALIGN(4);
} >RAM
}
Now, I’ve tried to translate the C code above quite literally into Rust. This is my attempt:
#![feature(asm, naked_functions, lang_items, used)]
#![no_std]
#![no_main]
use core::ptr;
#[naked]
#[used]
#[link_section = ".start_section"]
fn startup() {
unsafe {
asm!("NOP
LDR SP, =0X2001C000
BL main
.L1: B .L1"
: : : : "volatile"
);
}
}
#[inline(never)]
#[no_mangle]
pub extern fn main() -> ! {
const GPIO_D_MODER: u32 = 0x4002C000;
const GPIO_D_IDR_H: u32 = 0x4002C011;
const GPIO_D_ODR_L: u32 = 0x4002C014;
unsafe {
ptr::write_volatile(GPIO_D_MODER as *mut u32, 0x00005555);
}
loop {
unsafe {
let data: u8 = ptr::read_volatile(GPIO_D_IDR_H as *mut u8);
ptr::write_volatile(GPIO_D_ODR_L as *mut u8, data);
}
}
}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
… and this is my .cargo/config
[build]
target = "thumbv7em-none-eabi"
[target.thumbv7em-none-eabi]
rustflags = [
"-C", "link-arg=-nostartfiles",
"-C", "link-arg=-Tmd407-ram.x",
"--emit asm"
]
Compiling this with xargo build
works just fine, and a resulting 352 byte target/thumbv7em-none-eabi/debug/bargraph
is generated. The problem is the following:
$ arm-none-eabi-size target/thumbv7em-none-eabi/debug/bargraph
text data bss dec hex filename
0 0 0 0 0 target/thumbv7em-none-eabi/debug/bargraph
I guess this has something to do with the linking step, but I can’t figure out how to proceed. Any help is most appreciated!