2017-08-06 09:52:47 +02:00
# encoding: utf-8
# ruby: 2.4.2
= begin
2020-11-24 15:51:03 +01:00
Rakefile to manage compiling CuVoodoo STM32F4 firmware .
the firmware is for development boards based around a STM32F4xx micro - controller .
2017-08-06 09:52:47 +02:00
the firmware uses the libopencm3 library providing support for this micro - controller .
= end
require 'rake'
require 'rake/clean'
# the firmwares to compile
BOOTLOADER = " bootloader "
APPLICATION = " application "
FIRMWARES = [ BOOTLOADER , APPLICATION ]
# which development board is used
2020-11-24 15:51:03 +01:00
# supported are: WeAct MiniF4 with STM32F401
BOARD = ENV [ " BOARD " ] || " MINIF401 "
2020-11-27 15:29:57 +01:00
# get MCU from board
DEVICE = case BOARD
when " MINIF401 "
2021-05-14 14:30:15 +02:00
# the genuine MINIF401 with stm32f401cc is discontinued and has been replaced with stm32f401ce (just more RAM/flash), but knockoffs with stm32f401cc are still common
2020-11-27 15:29:57 +01:00
" stm32f401cc "
else
raise " unknown MCU for board #{ BOARD } "
end
2017-08-06 09:52:47 +02:00
# libopencm3 definitions
LIBOPENCM3_DIR = " libopencm3 "
LIBOPENCM3_INC = LIBOPENCM3_DIR + " /include "
2020-11-27 15:29:57 +01:00
LIBOPENCM3_LIBS = LIBOPENCM3_DIR + " /lib "
2020-12-10 23:51:03 +01:00
# get libopencm3
unless File . file? ( " ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py " ) then
sh " git submodule init "
sh " git submodule update "
end
2020-11-27 15:29:57 +01:00
LIBOPENCM3_LIB = " opencm3_ " + ` ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py #{ LIBOPENCM3_DIR } /ld/devices.data #{ DEVICE } FAMILY `
2017-08-06 09:52:47 +02:00
# source code used by the firmware
SRC_DIRS = [ " . " , " lib " ]
# cross-compiler environment
PREFIX = ENV [ " PREFIX " ] || " arm-none-eabi "
2017-10-09 17:59:12 +02:00
CC = PREFIX + " -gcc "
2018-06-08 13:40:38 +02:00
# LD would be the proper linker, but gcc comes with essential library path finder
LD = PREFIX + " -gcc "
2017-08-06 09:52:47 +02:00
AR = PREFIX + " -ar "
AS = PREFIX + " -as "
OBJCOPY = PREFIX + " -objcopy "
OBJDUMP = PREFIX + " -objdump "
GDB = PREFIX + " -gdb "
# compiler flags
cflags = [ ENV [ " CFLAGS " ] ]
# optimize for size
cflags << " -Os "
# add debug symbols (remove for smaller release)
2020-11-28 15:19:13 +01:00
cflags << " -ggdb3 "
2017-08-06 09:52:47 +02:00
# use C99 (supported by most an sufficient)
cflags << " -std=c99 "
# have strict warning (for better code)
cflags << " -Wpedantic -Wall -Werror -Wundef -Wextra -Wshadow -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -Wstrict-overflow=5 "
# add options for better code optimization
cflags << " -fno-common -ffunction-sections -fdata-sections "
# use variable size enum (clang doesn't but opencm3, and gcc do)
cflags << " -fshort-enums "
# don't use system main definition (the starting point)
cflags << " -ffreestanding "
# include own libraries
cflags += SRC_DIRS . collect { | srd_dir | " -I #{ srd_dir } " }
# include libopencm3 library
cflags << " -I #{ LIBOPENCM3_INC } "
# add defines for micro-controller and board
2020-11-27 15:29:57 +01:00
cflags << " -D #{ BOARD } "
cflags << ` ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py #{ LIBOPENCM3_DIR } /ld/devices.data #{ DEVICE } CPPFLAGS `
2017-08-06 09:52:47 +02:00
# render cflags
cflags = cflags . compact * ' '
# linker flags
2018-06-08 13:40:38 +02:00
ldflags = [ ]
2017-08-06 09:52:47 +02:00
# build static binary (no shared libraries on the micro-controller)
ldflags << " -static "
# don't include the system start files
ldflags << " -nostartfiles "
2018-06-08 13:40:38 +02:00
# linker specific flags
ldflags_linker = [ ENV [ " LDFLAGS " ] ]
2017-08-06 09:52:47 +02:00
# only keep used sections
2018-06-08 13:40:38 +02:00
ldflags_linker = [ " --gc-sections " ]
2019-11-19 10:23:48 +01:00
# show memory usage
ldflags_linker << " --print-memory-usage "
2018-06-08 13:40:38 +02:00
# add libopencm3 libraries
2020-11-27 15:29:57 +01:00
library_paths = [ LIBOPENCM3_LIBS ]
2018-06-08 13:40:38 +02:00
# project libraries
2020-11-27 15:29:57 +01:00
ldlibs = [ LIBOPENCM3_LIB ]
2018-06-08 13:40:38 +02:00
# general libraries (gcc provides the ARM ABI)
ldlibs_linker = [ " m " , " c " , " nosys " , " gcc " ]
2017-08-06 09:52:47 +02:00
2020-11-24 15:51:03 +01:00
# target micro-controller information (ARM Cortex-M4 supports thumb and thumb2, but does not include a floating point unit)
2020-11-27 15:29:57 +01:00
archflags = " -mthumb "
archflags += " -mcpu= " + ` ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py #{ LIBOPENCM3_DIR } /ld/devices.data #{ DEVICE } CPU `
archflags += " -mfloat-abi= " + ` ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py #{ LIBOPENCM3_DIR } /ld/devices.data #{ DEVICE } FPU ` . split ( " - " ) [ 0 ]
archflags += " -mfpu= " + ` ./ #{ LIBOPENCM3_DIR } /scripts/genlink.py #{ LIBOPENCM3_DIR } /ld/devices.data #{ DEVICE } FPU ` . split ( " - " ) [ 1 .. - 1 ] * " - "
2017-08-06 09:52:47 +02:00
desc " compile firmwares "
task :default = > FIRMWARES
2018-02-18 15:20:01 +01:00
task :compile = > FIRMWARES
2017-08-06 09:52:47 +02:00
FIRMWARES . each do | firmware |
desc " compile #{ firmware } firmware "
task firmware = > [ firmware + " .elf " , firmware + " .bin " , firmware + " .hex " ]
CLOBBER . include ( firmware + " .elf " )
CLOBBER . include ( firmware + " .bin " )
CLOBBER . include ( firmware + " .hex " )
CLOBBER . include ( firmware + " .list " )
CLOBBER . include ( firmware + " .map " )
end
# get dependencies of a file
# done is a list of already known dependencies
2021-03-10 14:13:54 +01:00
def dependencies ( source , done = [ ] )
2017-08-06 09:52:47 +02:00
d_path = source . ext ( " d " ) # get the dependency file
Rake :: Task [ d_path ] . invoke # ensure the dependency file exists
d_file = IO . read ( d_path ) # read the dependencies from dependency file
2021-03-10 14:13:54 +01:00
d_file = d_file . split ( ': ' ) [ 1 ] . gsub ( " \n " , '' ) . gsub ( '\\ ' , '' ) . gsub ( / \ s+ / , ' ' ) . split ( ' ' ) # get a list of dependencies
2017-08-06 09:52:47 +02:00
d_list = [ ] # list of dependencies
# only save dependencies which are in our source directories
d_file . each do | d |
SRC_DIRS . each do | dir |
2021-03-10 14:13:54 +01:00
if File . dirname ( d ) == dir then
2021-03-10 14:14:19 +01:00
d_list << d unless d . end_with? ( " .inc " )
2017-08-06 09:52:47 +02:00
end
end
end
# get the dependencies of these dependencies, if we don't know them already
done << source . ext ( " o " )
done . uniq!
d_list . each do | d |
d = d . ext ( " o " )
next if done . include? d
done += dependencies ( d , done )
end
done . uniq!
return done
end
desc " compile libopencm3 "
2020-12-10 23:51:03 +01:00
file " #{ LIBOPENCM3_LIBS } /lib #{ LIBOPENCM3_LIB } .a " do
2017-08-06 09:52:47 +02:00
sh " make --directory #{ LIBOPENCM3_DIR } "
end
task :doc = > [ " Doxyfile " , " README.md " ] do | t |
sh " doxygen #{ t . source } "
end
desc " compile source into object "
2020-11-27 15:29:57 +01:00
rule '.o' = > [ '.c' , proc { | f | File . file? ( f . ext ( " h " ) ) ? f . ext ( " h " ) : [ ] } , proc { | f | dependencies ( f ) . collect { | d | File . file? ( d . ext ( " h " ) ) ? d . ext ( " h " ) : [ ] } } , " #{ LIBOPENCM3_LIBS } /lib #{ LIBOPENCM3_LIB } .a " ] do | t |
2017-10-04 14:25:34 +02:00
sh " #{ CC } #{ cflags } #{ archflags } -o #{ t . name } -c #{ t . prerequisites [ 0 ] } "
2017-08-06 09:52:47 +02:00
end
desc " generate dependencies "
2020-11-27 15:29:57 +01:00
rule '.d' = > [ '.c' , " #{ LIBOPENCM3_LIBS } /lib #{ LIBOPENCM3_LIB } .a " ] do | t |
2017-10-04 14:25:34 +02:00
sh " #{ CC } #{ cflags } #{ archflags } -MM -MF #{ t . name } -c #{ t . prerequisites [ 0 ] } "
2017-08-06 09:52:47 +02:00
end
desc " link binary "
2020-11-27 15:29:57 +01:00
rule '.elf' = > [ '.o' , proc { | f | dependencies ( f ) } , '.ld' , " #{ LIBOPENCM3_LIBS } /lib #{ LIBOPENCM3_LIB } .a " ] do | t |
2018-06-08 13:40:38 +02:00
sh " #{ LD } #{ archflags } #{ ldflags . join ( ' ' ) } #{ t . prerequisites [ 0 .. - 3 ] . join ( ' ' ) } -T #{ t . name . ext ( 'ld' ) } #{ ldflags_linker . collect { | flag | " -Wl, " + flag } . join ( ' ' ) } #{ library_paths . collect { | path | " -L " + path } . join ( ' ' ) } #{ ldlibs . collect { | lib | " -l " + lib } . join ( ' ' ) } -Wl,--start-group #{ ldlibs_linker . collect { | lib | " -l " + lib } . join ( ' ' ) } -Wl,--end-group --output #{ t . name } "
2017-08-06 09:52:47 +02:00
end
desc " export binary "
rule '.bin' = > '.elf' do | t |
2018-02-18 15:21:18 +01:00
sh " #{ OBJCOPY } --output-target binary #{ t . source } #{ t . name } "
2017-08-06 09:52:47 +02:00
end
desc " export intel hex file "
rule '.hex' = > '.elf' do | t |
2018-02-18 15:21:18 +01:00
sh " #{ OBJCOPY } --output-target ihex #{ t . source } #{ t . name } "
2017-08-06 09:52:47 +02:00
end
desc " export list "
rule '.list' = > '.elf' do | t |
sh " #{ OBJDUMP } -S #{ t . source } > #{ t . name } "
end
desc " export map "
rule '.map' = > '.elf' do | t |
sh " #{ OBJDUMP } -S #{ t . source } > #{ t . name } "
end
SRC_DIRS . each do | src_dir |
CLEAN . include ( src_dir + " /*.o " )
CLEAN . include ( src_dir + " /*.d " )
end
# SWD/JTAG adapter used
# supported are : STLINKV2 (ST-Link V2), BMP (Black Magic Probe)
2020-03-15 18:24:23 +01:00
SWD_ADAPTER = ENV [ " SWD_ADAPTER " ] || " STLINKV2 "
2017-08-06 09:52:47 +02:00
# openOCD path to control the adapter
OOCD = ENV [ " OOCD " ] || " openocd "
# openOCD adapted name
2019-04-01 15:17:06 +02:00
OOCD_INTERFACE = ENV [ " OOCD_INTERFACE " ] || ( SWD_ADAPTER == " STLINKV2 " ? " stlink " : " " )
2017-08-06 09:52:47 +02:00
# openOCD target for the micro-controller
2020-11-24 15:51:03 +01:00
OOCD_TARGET = " stm32f4x "
2017-08-06 09:52:47 +02:00
# Black Magic Probe port
BMP_PORT = ENV [ " BMP_PORT " ] || " /dev/ttyACM0 "
2020-11-27 16:39:11 +01:00
desc " flash application "
task :flash = > :dfu_application
2017-08-06 09:52:47 +02:00
desc " flash application using USB DFU "
2020-11-27 16:39:11 +01:00
task :dfu_application = > APPLICATION + " .bin " do | t |
2018-08-11 18:41:23 +02:00
sh " dfu-util --device 1209:4356 --download #{ t . source } "
2017-08-06 09:52:47 +02:00
end
2020-11-27 16:39:11 +01:00
desc " flash application using USB DFU "
task :dfu_bootloader = > BOOTLOADER + " .bin " do | t |
sh " dfu-util --device 0483:df11 --cfg 1 --intf 0 --alt 0 --dfuse-address 0x08000000 --download #{ t . source } --reset "
end
2020-11-24 15:51:03 +01:00
desc " remove STM32F4 protection using SWD "
2019-04-01 15:16:09 +02:00
task :remove_protection do
case SWD_ADAPTER
when " STLINKV2 "
2020-11-30 14:51:06 +01:00
sh " #{ OOCD } --file interface/ #{ OOCD_INTERFACE } .cfg --command 'transport select hla_swd' --file target/ #{ OOCD_TARGET } .cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f2x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f2x mass_erase 0' --command 'shutdown' "
2019-04-01 15:16:09 +02:00
when " BMP "
sh " #{ GDB } --eval-command='target extended-remote #{ BMP_PORT } ' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor option erase' --eval-command='monitor erase_mass' --eval-command='kill' --eval-command='quit' "
end
end
2017-08-06 09:52:47 +02:00
desc " flash bootloader using SWD "
2020-11-27 16:39:11 +01:00
task :swd_bootloader = > BOOTLOADER + " .hex " do | t |
2017-08-06 09:52:47 +02:00
case SWD_ADAPTER
when " STLINKV2 "
2020-11-24 15:51:03 +01:00
sh " #{ OOCD } --file interface/ #{ OOCD_INTERFACE } .cfg --command 'transport select hla_swd' --file target/ #{ OOCD_TARGET } .cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{ t . source } ' --command 'reset' --command 'shutdown' "
2017-08-06 09:52:47 +02:00
when " BMP "
2020-01-04 14:32:52 +01:00
sh " #{ GDB } --eval-command='target extended-remote #{ BMP_PORT } ' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor erase_mass' --eval-command='load' --eval-command='kill' --eval-command='quit' #{ t . source } "
2017-08-06 09:52:47 +02:00
end
end
2020-11-27 16:39:11 +01:00
task :swd = > :swd_application
2017-08-06 09:52:47 +02:00
desc " flash application using SWD "
2020-11-27 16:39:11 +01:00
task :swd_application = > APPLICATION + " .hex " do | t |
2017-08-06 09:52:47 +02:00
case SWD_ADAPTER
when " STLINKV2 "
2020-11-24 15:51:03 +01:00
sh " #{ OOCD } --file interface/ #{ OOCD_INTERFACE } .cfg --command 'transport select hla_swd' --file target/ #{ OOCD_TARGET } .cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{ t . source } ' --command 'reset' --command 'shutdown' "
2017-08-06 09:52:47 +02:00
when " BMP "
sh " #{ GDB } --eval-command='target extended-remote #{ BMP_PORT } ' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{ t . source } "
end
end
# debug application using GDB
desc " debug application using GDB "
task :debug = > APPLICATION + " .elf " do | t |
case SWD_ADAPTER
when " STLINKV2 "
# for GDB to work with openOCD the firmware needs to be reloaded
2020-11-24 15:51:03 +01:00
exec ( " #{ GDB } --eval-command='target remote | #{ OOCD } --file interface/ #{ OOCD_INTERFACE } .cfg --command \" transport select hla_swd \" --file target/ #{ OOCD_TARGET } .cfg --command \" gdb_port pipe; log_output /dev/null; init \" ' #{ t . source } " )
2017-08-06 09:52:47 +02:00
when " BMP "
2018-02-18 15:21:18 +01:00
exec ( " #{ GDB } --eval-command='target extended-remote #{ BMP_PORT } ' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{ t . source } " )
2017-08-06 09:52:47 +02:00
end
end
2020-01-04 14:33:23 +01:00
# debug application using GDB
desc " debug bootloader using GDB "
task :debug_bootloader = > BOOTLOADER + " .elf " do | t |
case SWD_ADAPTER
when " STLINKV2 "
# for GDB to work with openOCD the firmware needs to be reloaded
2020-11-24 15:51:03 +01:00
exec ( " #{ GDB } --eval-command='target remote | #{ OOCD } --file interface/ #{ OOCD_INTERFACE } .cfg --command \" transport select hla_swd \" --file target/ #{ OOCD_TARGET } .cfg --command \" gdb_port pipe; log_output /dev/null; init \" ' --eval-command='monitor reset init' #{ t . source } " )
2020-01-04 14:33:23 +01:00
when " BMP "
exec ( " #{ GDB } --eval-command='target extended-remote #{ BMP_PORT } ' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{ t . source } " )
end
end