From 3f86b308d69b60487ea13645dc8823edab57a0b9 Mon Sep 17 00:00:00 2001 From: Kelvin Ly Date: Thu, 11 Feb 2021 08:50:43 -0500 Subject: [PATCH] Start work on ztheta firmware --- .gitmodules | 3 + controller-board/fw/libopencm3 | 1 + controller-board/fw/rules.mk | 177 ++++++++++++++++++ .../fw/ztheta-controller/.gitignore | 2 + .../fw/ztheta-controller/Makefile | 18 ++ .../generated.stm32l011f4p6.ld | 69 +++++++ controller-board/fw/ztheta-controller/main.c | 3 + .../ztheta-controller/ztheta-controller.elf | Bin 0 -> 100072 bytes 8 files changed, 273 insertions(+) create mode 100644 .gitmodules create mode 160000 controller-board/fw/libopencm3 create mode 100644 controller-board/fw/rules.mk create mode 100644 controller-board/fw/ztheta-controller/.gitignore create mode 100644 controller-board/fw/ztheta-controller/Makefile create mode 100644 controller-board/fw/ztheta-controller/generated.stm32l011f4p6.ld create mode 100644 controller-board/fw/ztheta-controller/main.c create mode 100755 controller-board/fw/ztheta-controller/ztheta-controller.elf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2dd3ffd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "controller-board/fw/libopencm3"] + path = controller-board/fw/libopencm3 + url = https://github.com/libopencm3/libopencm3 diff --git a/controller-board/fw/libopencm3 b/controller-board/fw/libopencm3 new file mode 160000 index 0000000..aeb3cee --- /dev/null +++ b/controller-board/fw/libopencm3 @@ -0,0 +1 @@ +Subproject commit aeb3cee0235dd3e8784c003ff1af82ce44b7b38c diff --git a/controller-board/fw/rules.mk b/controller-board/fw/rules.mk new file mode 100644 index 0000000..e417d2f --- /dev/null +++ b/controller-board/fw/rules.mk @@ -0,0 +1,177 @@ +# This version of rules.mk expects the following to be defined before +# inclusion.. +### REQUIRED ### +# OPENCM3_DIR - duh +# PROJECT - will be the basename of the output elf, eg usb-gadget0-stm32f4disco +# CFILES - basenames only, eg main.c blah.c +# CXXFILES - same for C++ files. Must have cxx suffix! +# DEVICE - the full device name, eg stm32f405ret6 +# _or_ +# LDSCRIPT - full path, eg ../../examples/stm32/f4/stm32f4-discovery/stm32f4-discovery.ld +# OPENCM3_LIB - the basename, eg: opencm3_stm32f4 +# OPENCM3_DEFS - the target define eg: -DSTM32F4 +# ARCH_FLAGS - eg, -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 +# (ie, the full set of cpu arch flags, _none_ are defined in this file) +# +### OPTIONAL ### +# INCLUDES - fully formed -I paths, if you want extra, eg -I../shared +# BUILD_DIR - defaults to bin, should set this if you are building multiarch +# OPT - full -O flag, defaults to -Os +# CSTD - defaults -std=c99 +# CXXSTD - no default. +# OOCD_INTERFACE - eg stlink-v2 +# OOCD_TARGET - eg stm32f4x +# both only used if you use the "make flash" target. +# OOCD_FILE - eg my.openocd.cfg +# This overrides interface/target above, and is used as just -f FILE +### TODO/FIXME/notes ### +# No support for stylecheck. +# No support for BMP/texane/random flash methods, no plans either +# No support for magically finding the library. +# C++ hasn't been actually tested with this..... sorry bout that. ;) +# Second expansion/secondary not set, add this if you need them. + +BUILD_DIR ?= bin +OPT ?= -Os +CSTD ?= -std=c99 + +# Be silent per default, but 'make V=1' will show all compiler calls. +# If you're insane, V=99 will print out all sorts of things. +V?=0 +ifeq ($(V),0) +Q := @ +NULL := 2>/dev/null +endif + +# Tool paths. +PREFIX ?= arm-none-eabi- +CC = $(PREFIX)gcc +CXX = $(PREFIX)g++ +LD = $(PREFIX)gcc +OBJCOPY = $(PREFIX)objcopy +OBJDUMP = $(PREFIX)objdump +OOCD ?= openocd + +OPENCM3_INC = $(OPENCM3_DIR)/include + +# Inclusion of library header files +INCLUDES += $(patsubst %,-I%, . $(OPENCM3_INC) ) + +OBJS = $(CFILES:%.c=$(BUILD_DIR)/%.o) +OBJS += $(CXXFILES:%.cxx=$(BUILD_DIR)/%.o) +OBJS += $(AFILES:%.S=$(BUILD_DIR)/%.o) +GENERATED_BINS = $(PROJECT).elf $(PROJECT).bin $(PROJECT).map $(PROJECT).list $(PROJECT).lss + +TGT_CPPFLAGS += -MD +TGT_CPPFLAGS += -Wall -Wundef $(INCLUDES) +TGT_CPPFLAGS += $(INCLUDES) $(OPENCM3_DEFS) + +TGT_CFLAGS += $(OPT) $(CSTD) -ggdb3 +TGT_CFLAGS += $(ARCH_FLAGS) +TGT_CFLAGS += -fno-common +TGT_CFLAGS += -ffunction-sections -fdata-sections +TGT_CFLAGS += -Wextra -Wshadow -Wno-unused-variable -Wimplicit-function-declaration +TGT_CFLAGS += -Wredundant-decls -Wstrict-prototypes -Wmissing-prototypes + +TGT_CXXFLAGS += $(OPT) $(CXXSTD) -ggdb3 +TGT_CXXFLAGS += $(ARCH_FLAGS) +TGT_CXXFLAGS += -fno-common +TGT_CXXFLAGS += -ffunction-sections -fdata-sections +TGT_CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ + +TGT_ASFLAGS += $(OPT) $(ARCH_FLAGS) -ggdb3 + +TGT_LDFLAGS += -T$(LDSCRIPT) -L$(OPENCM3_DIR)/lib -nostartfiles +TGT_LDFLAGS += $(ARCH_FLAGS) +TGT_LDFLAGS += -specs=nano.specs +TGT_LDFLAGS += -Wl,--gc-sections +# OPTIONAL +#TGT_LDFLAGS += -Wl,-Map=$(PROJECT).map +ifeq ($(V),99) +TGT_LDFLAGS += -Wl,--print-gc-sections +endif + +# Linker script generator fills this in for us. +ifeq (,$(DEVICE)) +LDLIBS += -l$(OPENCM3_LIB) +endif +# nosys is only in newer gcc-arm-embedded... +#LDLIBS += -specs=nosys.specs +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +# Burn in legacy hell fortran modula pascal yacc idontevenwat +.SUFFIXES: +.SUFFIXES: .c .S .h .o .cxx .elf .bin .list .lss + +# Bad make, never *ever* try to get a file out of source control by yourself. +%: %,v +%: RCS/%,v +%: RCS/% +%: s.% +%: SCCS/s.% + +all: $(PROJECT).elf $(PROJECT).bin +flash: $(PROJECT).flash + +# error if not using linker script generator +ifeq (,$(DEVICE)) +$(LDSCRIPT): +ifeq (,$(wildcard $(LDSCRIPT))) + $(error Unable to find specified linker script: $(LDSCRIPT)) +endif +else +# if linker script generator was used, make sure it's cleaned. +GENERATED_BINS += $(LDSCRIPT) +endif + +# Need a special rule to have a bin dir +$(BUILD_DIR)/%.o: %.c + @printf " CC\t$<\n" + @mkdir -p $(dir $@) + $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(BUILD_DIR)/%.o: %.cxx + @printf " CXX\t$<\n" + @mkdir -p $(dir $@) + $(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(BUILD_DIR)/%.o: %.S + @printf " AS\t$<\n" + @mkdir -p $(dir $@) + $(Q)$(CC) $(TGT_ASFLAGS) $(ASFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(PROJECT).elf: $(OBJS) $(LDSCRIPT) $(LIBDEPS) + @printf " LD\t$@\n" + $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ + +%.bin: %.elf + @printf " OBJCOPY\t$@\n" + $(Q)$(OBJCOPY) -O binary $< $@ + +%.lss: %.elf + $(OBJDUMP) -h -S $< > $@ + +%.list: %.elf + $(OBJDUMP) -S $< > $@ + +%.flash: %.elf + @printf " FLASH\t$<\n" +ifeq (,$(OOCD_FILE)) + $(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \ + $(OOCD) -f interface/$(OOCD_INTERFACE).cfg \ + -f target/$(OOCD_TARGET).cfg \ + -c "program $(realpath $(*).elf) verify reset exit" \ + $(NULL) +else + $(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \ + $(OOCD) -f $(OOCD_FILE) \ + -c "program $(realpath $(*).elf) verify reset exit" \ + $(NULL) +endif + +clean: + rm -rf $(BUILD_DIR) $(GENERATED_BINS) + +.PHONY: all clean flash +-include $(OBJS:.o=.d) + diff --git a/controller-board/fw/ztheta-controller/.gitignore b/controller-board/fw/ztheta-controller/.gitignore new file mode 100644 index 0000000..ac4d450 --- /dev/null +++ b/controller-board/fw/ztheta-controller/.gitignore @@ -0,0 +1,2 @@ +bin/ +*.bin diff --git a/controller-board/fw/ztheta-controller/Makefile b/controller-board/fw/ztheta-controller/Makefile new file mode 100644 index 0000000..5c90a39 --- /dev/null +++ b/controller-board/fw/ztheta-controller/Makefile @@ -0,0 +1,18 @@ +PROJECT = ztheta-controller +BUILD_DIR = bin + +#SHARED_DIR = +CFILES = main.c + +# TODO - you will need to edit these two lines! +DEVICE=stm32l011f4p6 +OOCD_FILE = board/stm32f4discovery.cfg + +# You shouldn't have to edit anything below here. +VPATH += $(SHARED_DIR) +INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR)) +OPENCM3_DIR=../libopencm3 + +include $(OPENCM3_DIR)/mk/genlink-config.mk +include ../rules.mk +include $(OPENCM3_DIR)/mk/genlink-rules.mk diff --git a/controller-board/fw/ztheta-controller/generated.stm32l011f4p6.ld b/controller-board/fw/ztheta-controller/generated.stm32l011f4p6.ld new file mode 100644 index 0000000..de52abf --- /dev/null +++ b/controller-board/fw/ztheta-controller/generated.stm32l011f4p6.ld @@ -0,0 +1,69 @@ +EXTERN(vector_table) +ENTRY(reset_handler) +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 2K + rom (rx) : ORIGIN = 0x08000000, LENGTH = 16K +} +SECTIONS +{ + .text : { + *(.vectors) + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + } >rom + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + . = ALIGN(4); + _etext = .; + .noinit (NOLOAD) : { + *(.noinit*) + } >ram + . = ALIGN(4); + .data : { + _data = .; + *(.data*) + *(.ramtext*) + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + .bss : { + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + /DISCARD/ : { *(.eh_frame) } + . = ALIGN(4); + end = .; +} +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); diff --git a/controller-board/fw/ztheta-controller/main.c b/controller-board/fw/ztheta-controller/main.c new file mode 100644 index 0000000..d633183 --- /dev/null +++ b/controller-board/fw/ztheta-controller/main.c @@ -0,0 +1,3 @@ +int main(void) { + // TODO +} diff --git a/controller-board/fw/ztheta-controller/ztheta-controller.elf b/controller-board/fw/ztheta-controller/ztheta-controller.elf new file mode 100755 index 0000000000000000000000000000000000000000..e1b79f60567a4cd263b2b58c11b540f823219b19 GIT binary patch literal 100072 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 VG)=FsEBCpV`rbby&wNa|{{uWKNErYC literal 0 HcmV?d00001