You've already forked torvalds-GuitarPedal
mirror of
https://github.com/torvalds/GuitarPedal.git
synced 2026-08-17 13:09:42 +00:00
'Software' was the directory everything that was not KiCad ended up in, which stopped describing anything a while ago - Validation and the web app are software too. Worse, it put the shared parts inside the firmware, where they read as the firmware's own. They are not. Effects/ has three consumers built from it: the firmware, Validation's bench, and the web app's controls, all generated from the same POT: comments by gen_effects.py. Audio/ has two - the bench compiles the same biquads, the same envelope followers and the same single_sample(), which is the whole reason a measurement on a workstation says anything about the pedal. Neither belongs under Firmware/, so neither is under it any more: Effects/ one file per effect Audio/ the DSP they are built from, and the audio loop Firmware/ the rest of what runs on the pedal, and the submodules WebMIDI/ the web app scripts/ what the build runs Validation/ unchanged Hardware/, Documentation/, Images/ CMakeLists.txt and the wrapper Makefile move to the top with them, because the build now consumes four of those directories and generates into a fifth. board.local and build/ come along; MIDI_CC_MAP.md is generated into Documentation/ rather than into the old Software/ root. scripts/ goes with the build rather than staying under the firmware, because six of the ten had nothing to do with the firmware: gen_effects.py reads Effects/ and writes to three different places, pow2/log2/quarter_sine generate Audio/'s tables, check-readme.py compares Effects/ against the README, and server.py serves the web app. Four of them are invoked from Validation, which was reaching into Firmware/ for tooling - the same burying this commit is undoing. The four that really are about the firmware are ELF checks the top-level build drives anyway, and a second scripts directory would only be a second place to look. C includes say "Audio/foo.h" and the generated map says "Effects/bar.h", with the repository root on the include path for both the firmware and the bench. Spelling the directory out rather than relying on a bare name is what keeps Audio/cycles.h shimmable: a quoted include searches the including file's own directory first. The submodules are renamed as well as moved. git mv updates their paths but leaves the section names, and 'Software/pico-sdk' surviving in .gitmodules would be the word this commit removes, still load-bearing. That meant the nested modules under pico-sdk too - six .git files pointing into .git/modules/Software - which is why 'git submodule update --init --recursive' is worth running once after pulling this. Verified rather than assumed: a clean configure and build, make check (failing only on the missing-eeprom case it already failed on), check-effects, all four analysis pages reproducing every series and drawing every chart, and a flash to the board that still measures a routed reverb where it did before. One latent bug fell out of it. bench/coeff declared only quarter_sine.h of the three generated math tables, and Audio/util.h includes pow2.h and log2.h as well - so building that target with an empty gen/ could never have worked. 'make bench' builds bench/bench first, which generates all three, so it stayed hidden until this rebuilt everything from nothing. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
492 lines
17 KiB
CMake
492 lines
17 KiB
CMake
cmake_minimum_required(VERSION 3.13...3.27)
|
|
|
|
#
|
|
# What we build for, and how we pass floats around.
|
|
#
|
|
# The part is an RP2354A - an RP2350A with the flash on the die. The
|
|
# default float ABI is softfp, which is a compatibility mode for parts
|
|
# that might not have an FPU. This one does, and the whole firmware is
|
|
# built around 32-bit floats, so passing them in core registers is pure
|
|
# loss - a 'sample_t' is two floats, and softfp hands it back through
|
|
# memory rather than in s0/s1.
|
|
#
|
|
# The two belong together: asking for hard float only makes sense
|
|
# because of the platform we asked for. Both are read by the toolchain
|
|
# file, so they have to be set before the SDK picks one - which is also
|
|
# why they are not the usual cache options further down.
|
|
#
|
|
set(PICO_PLATFORM rp2350)
|
|
set(PICO_HARD_FLOAT_ABI 1)
|
|
|
|
# initialize the SDK based on PICO_SDK_PATH
|
|
# note: this must happen before project()
|
|
include(Firmware/pico-sdk/pico_sdk_init.cmake)
|
|
|
|
project(rp2350)
|
|
|
|
# initialize the Raspberry Pi Pico SDK
|
|
pico_sdk_init()
|
|
|
|
#
|
|
# Which boards exist, and which one this tree is currently about.
|
|
#
|
|
# Every board is built as its own target with its own name, all of them
|
|
# at once, because they differ in ways that a single binary cannot paper
|
|
# over: the unified board shares four GPIOs with the split map and needs
|
|
# the ADC pins for its expression jack. There is no "the" firmware any
|
|
# more, so there is no build/pedal.elf either - an artifact whose name
|
|
# does not say what it is for is exactly what goes on the wrong board.
|
|
#
|
|
# What is configured is only which one is the *default*: what a plain
|
|
# 'make' builds and what 'make flash' flashes. Any other board is one
|
|
# explicit target away ('make split', 'make flash-split')
|
|
# without disturbing that, and 'make all-boards' builds the lot, which
|
|
# is how a change to shared code stops silently breaking a board nobody
|
|
# has selected for a month.
|
|
#
|
|
# There are only two, and the line between them is the only one that
|
|
# needs a build. The split family has two MCU boards - one with a rotary
|
|
# encoder, one with MIDI jacks - whose pins do not clash, so one binary
|
|
# drives either. Its audio boards differ too, mono against stereo, and
|
|
# that difference is discovered at runtime because the older codec needs
|
|
# i2c setup anyway. Neither is a reason to compile twice.
|
|
#
|
|
# What is left is the pin map, which the unified board shares four GPIOs
|
|
# of and no more. That is the whole of the build-time question.
|
|
#
|
|
set(PEDAL_BOARDS split unified)
|
|
|
|
# ...and the same list as prose, for the messages below.
|
|
list(JOIN PEDAL_BOARDS " " PEDAL_BOARDS_PRETTY)
|
|
|
|
#
|
|
# Set and forget, in a file rather than in the cache.
|
|
#
|
|
# A cmake cache variable is set-and-forget right up until somebody
|
|
# deletes build/, and that is not hypothetical: reconfiguring silently
|
|
# reset the eeprom size option once and the pedal ran perfectly and
|
|
# forgot every scene across a reboot. board.local survives 'rm -rf
|
|
# build', is not tracked, and is read again whenever it changes.
|
|
#
|
|
# There is deliberately no default. A wrong board should be a configure
|
|
# error you fix once, not a working build for hardware you do not have.
|
|
#
|
|
set(BOARD_LOCAL "${CMAKE_CURRENT_SOURCE_DIR}/board.local")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${BOARD_LOCAL}")
|
|
|
|
if (EXISTS "${BOARD_LOCAL}")
|
|
file(STRINGS "${BOARD_LOCAL}" PEDAL_BOARD LIMIT_COUNT 1)
|
|
string(STRIP "${PEDAL_BOARD}" PEDAL_BOARD)
|
|
endif()
|
|
|
|
if (NOT PEDAL_BOARD)
|
|
message(FATAL_ERROR
|
|
"No board selected. Say which one this tree is about:\n"
|
|
" echo unified > ${BOARD_LOCAL}\n"
|
|
"Known boards: ${PEDAL_BOARDS_PRETTY}")
|
|
endif()
|
|
|
|
if (NOT PEDAL_BOARD IN_LIST PEDAL_BOARDS)
|
|
message(FATAL_ERROR
|
|
"'${PEDAL_BOARD}' in ${BOARD_LOCAL} is not a board.\n"
|
|
"Known boards: ${PEDAL_BOARDS_PRETTY}")
|
|
endif()
|
|
|
|
message("Default board: ${PEDAL_BOARD}")
|
|
|
|
set(SAMPLES_PER_SEC 48000.0)
|
|
set(SCRIPT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
|
|
|
|
#
|
|
# How much flash there actually is.
|
|
#
|
|
# PICO_BOARD is not set, so the SDK defaults it to 'pico2' - a board
|
|
# with 4MB of flash hanging off the QSPI pins. This is an RP2354A and
|
|
# the 2MB is on the die. Nothing has cared so far, because nothing has
|
|
# addressed flash by anything but a symbol.
|
|
#
|
|
# It starts mattering the moment something works backwards from the end
|
|
# of flash, because 'the end' would be an address that is not there.
|
|
# flash_range_erase() guards itself with
|
|
#
|
|
# hard_assert(flash_offs + count <= PICO_FLASH_SIZE_BYTES)
|
|
#
|
|
# so getting this right is also what makes the SDK's own bounds check
|
|
# mean something.
|
|
#
|
|
set(FLASH_SIZE_BYTES 0x200000)
|
|
|
|
#
|
|
# The save area: sixty-four 4kB slots at the top of flash.
|
|
#
|
|
# 4kB because that is the erase size, and there is no reason to be
|
|
# cleverer when the part has 2MB and the firmware uses 84kB. Sixty-four
|
|
# rather than the thirty-two that would do, because slots are expected
|
|
# to be live at once - one per scene, plus whatever else - and a region
|
|
# with no spare slots cannot rotate, so 'enough for one copy of
|
|
# everything' is the one size that does not work.
|
|
#
|
|
set(SAVE_SLOT_SIZE 0x1000)
|
|
set(SAVE_SLOT_COUNT 64)
|
|
math(EXPR SAVE_AREA_SIZE "${SAVE_SLOT_SIZE} * ${SAVE_SLOT_COUNT}"
|
|
OUTPUT_FORMAT HEXADECIMAL)
|
|
math(EXPR SAVE_AREA_OFFSET "${FLASH_SIZE_BYTES} - ${SAVE_AREA_SIZE}"
|
|
OUTPUT_FORMAT HEXADECIMAL)
|
|
|
|
#
|
|
# Only the audio core runs from RAM: everything marked with
|
|
# __audio_func() lands in a '.time_critical.audio_*' section, which the
|
|
# default linker script already pulls out of flash. The rest is cold
|
|
# enough to run from XIP.
|
|
#
|
|
# Its constant data has to be there too, marked __not_in_flash("audio"),
|
|
# because the audio core keeps playing while core 0 writes flash with
|
|
# XIP switched off. check-audio.py enforces both halves.
|
|
#
|
|
|
|
# Math table generation
|
|
set(POW2_STEP_SHIFT 8)
|
|
set(POW2_HEADER "${CMAKE_CURRENT_BINARY_DIR}/pow2.h")
|
|
add_custom_command(
|
|
OUTPUT "${POW2_HEADER}"
|
|
COMMAND "${SCRIPT_DIR}/pow2.py"
|
|
"${POW2_HEADER}"
|
|
"${POW2_STEP_SHIFT}"
|
|
DEPENDS "${SCRIPT_DIR}/pow2.py"
|
|
)
|
|
list(APPEND PEDAL_GENERATED "${POW2_HEADER}")
|
|
|
|
set(LOG2_STEP_SHIFT 8)
|
|
set(LOG2_HEADER "${CMAKE_CURRENT_BINARY_DIR}/log2.h")
|
|
add_custom_command(
|
|
OUTPUT "${LOG2_HEADER}"
|
|
COMMAND "${SCRIPT_DIR}/log2.py"
|
|
"${LOG2_HEADER}"
|
|
"${LOG2_STEP_SHIFT}"
|
|
DEPENDS "${SCRIPT_DIR}/log2.py"
|
|
)
|
|
list(APPEND PEDAL_GENERATED "${LOG2_HEADER}")
|
|
|
|
set(SINE_STEP_SHIFT 8)
|
|
set(SINE_HEADER "${CMAKE_CURRENT_BINARY_DIR}/quarter_sine.h")
|
|
add_custom_command(
|
|
OUTPUT "${SINE_HEADER}"
|
|
COMMAND "${SCRIPT_DIR}/quarter_sine.py"
|
|
"${SINE_HEADER}"
|
|
"${SINE_STEP_SHIFT}"
|
|
DEPENDS "${SCRIPT_DIR}/quarter_sine.py"
|
|
)
|
|
list(APPEND PEDAL_GENERATED "${SINE_HEADER}")
|
|
|
|
file(GLOB EFFECT_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Effects/*.h")
|
|
set(EFFECT_MAP_HEADER "${CMAKE_CURRENT_BINARY_DIR}/effect_map.h")
|
|
set(EFFECT_JS "${CMAKE_CURRENT_SOURCE_DIR}/WebMIDI/effects.js")
|
|
set(EFFECT_MD "${CMAKE_CURRENT_SOURCE_DIR}/Documentation/MIDI_CC_MAP.md")
|
|
|
|
# Written alongside effect_map.h rather than named on the command line -
|
|
# the generator puts it next to it - but it is still an output, and the
|
|
# build graph should know that rather than getting away with it.
|
|
set(EFFECT_SCHEMA "${CMAKE_CURRENT_BINARY_DIR}/midi_schema.h")
|
|
|
|
add_custom_command(
|
|
OUTPUT "${EFFECT_MAP_HEADER}" "${EFFECT_SCHEMA}" "${EFFECT_JS}" "${EFFECT_MD}"
|
|
COMMAND "${SCRIPT_DIR}/gen_effects.py"
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/Effects"
|
|
"${EFFECT_MAP_HEADER}"
|
|
"${EFFECT_JS}"
|
|
"${EFFECT_MD}"
|
|
${EFFECT_HEADERS}
|
|
DEPENDS "${SCRIPT_DIR}/gen_effects.py" ${EFFECT_HEADERS}
|
|
)
|
|
list(APPEND PEDAL_GENERATED "${EFFECT_MAP_HEADER}")
|
|
|
|
#
|
|
# The WS2812B waveform, as a 256-entry table.
|
|
#
|
|
# The protocol used to live in ws2812.pio, in delay slots and a branch,
|
|
# with a '.clock_div 18' that quietly meant a different bit rate on
|
|
# every system clock this firmware has run at. It is a table now, which
|
|
# can be checked with a diff on a machine that has no PIO in it - see
|
|
# pixels.h and pio/bitstream.pio.
|
|
#
|
|
set(WS2812_TABLE "${CMAKE_CURRENT_BINARY_DIR}/ws2812_table.h")
|
|
add_custom_command(
|
|
OUTPUT "${WS2812_TABLE}"
|
|
COMMAND "${SCRIPT_DIR}/ws2812_table.py" "${WS2812_TABLE}"
|
|
DEPENDS "${SCRIPT_DIR}/ws2812_table.py"
|
|
)
|
|
list(APPEND PEDAL_GENERATED "${WS2812_TABLE}")
|
|
|
|
#
|
|
# Everything above is generated once and shared by every board: none of
|
|
# it depends on which pins anything is on.
|
|
#
|
|
# It hangs off one target rather than being listed in each board's
|
|
# sources, which matters with more than one of them - two targets naming
|
|
# the same generated file can run its rule twice under 'make -j', and
|
|
# gen_effects.py writes into the source tree (effects.js, MIDI_CC_MAP.md)
|
|
# where doing that twice at once is a race with a real loser.
|
|
#
|
|
add_custom_target(pedal_generated DEPENDS ${PEDAL_GENERATED})
|
|
|
|
#
|
|
# The PIO programs are shared for the same reason - a .pio says what the
|
|
# state machine does, and which pin it does it on arrives at runtime in
|
|
# *_program_init().
|
|
#
|
|
# They go through an INTERFACE library because pico_generate_pio_header()
|
|
# writes to one fixed path per .pio file: calling it once per board would
|
|
# declare three rules for the same output and cmake refuses that outright.
|
|
# The SDK's own function has a branch for INTERFACE targets, so this is
|
|
# the supported way round rather than a trick.
|
|
#
|
|
# Our own sources only - we don't get to pick the SDK's warnings
|
|
set_source_files_properties(Firmware/pedal.c Firmware/usb-device.c PROPERTIES
|
|
COMPILE_OPTIONS -Wdouble-promotion)
|
|
|
|
set(PIO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Firmware/pio")
|
|
|
|
add_library(pedal_pio INTERFACE)
|
|
pico_generate_pio_header(pedal_pio ${PIO_DIR}/bitstream.pio)
|
|
pico_generate_pio_header(pedal_pio ${PIO_DIR}/debounce.pio)
|
|
pico_generate_pio_header(pedal_pio ${PIO_DIR}/rotary.pio)
|
|
pico_generate_pio_header(pedal_pio ${PIO_DIR}/i2s.pio)
|
|
|
|
#
|
|
# ...and the header-generating targets it made, named after it, which
|
|
# each board depends on explicitly. Linking an INTERFACE library does
|
|
# not reliably carry its add_dependencies() across, and "the header is
|
|
# not there yet" is not a failure worth debugging twice.
|
|
#
|
|
set(PEDAL_PIO_TARGETS
|
|
pedal_pio_bitstream_pio_h
|
|
pedal_pio_debounce_pio_h
|
|
pedal_pio_rotary_pio_h
|
|
pedal_pio_i2s_pio_h)
|
|
|
|
#
|
|
# One board, one target, one name. Everything below here is per-board;
|
|
# everything above it is not.
|
|
#
|
|
function(add_pedal_board board)
|
|
|
|
set(tgt "pedal-${board}")
|
|
|
|
add_executable(${tgt})
|
|
|
|
add_dependencies(${tgt} pedal_generated ${PEDAL_PIO_TARGETS})
|
|
target_link_libraries(${tgt} pedal_pio)
|
|
|
|
#
|
|
# Which board this is, in the three places it has to be true at once:
|
|
# the pin map the compiler sees, the name the binary carries so picotool
|
|
# can answer for a file on disk, and the name the firmware can compare
|
|
# against what actually answers on the i2c bus.
|
|
#
|
|
target_compile_definitions(${tgt} PRIVATE
|
|
PEDAL_BOARD_HEADER="boards/${board}.h"
|
|
PEDAL_BOARD_NAME="${board}")
|
|
|
|
target_compile_definitions(${tgt} PRIVATE
|
|
PICO_FLASH_SIZE_BYTES=${FLASH_SIZE_BYTES}
|
|
SAVE_SLOT_SIZE=${SAVE_SLOT_SIZE}
|
|
SAVE_SLOT_COUNT=${SAVE_SLOT_COUNT}
|
|
SAVE_AREA_OFFSET=${SAVE_AREA_OFFSET})
|
|
|
|
target_compile_definitions(${tgt} PRIVATE
|
|
SYS_CLK_KHZ=153600
|
|
PLL_SYS_VCO_FREQ_HZ=768000000
|
|
PLL_SYS_POSTDIV1=5
|
|
PLL_SYS_POSTDIV2=1
|
|
|
|
#
|
|
# Wait longer for the crystal before believing it.
|
|
#
|
|
# One board came up dead on roughly a quarter of cold power cycles -
|
|
# no USB, no BOOTSEL, nothing in the host's log - and was rock solid
|
|
# once running. It hangs in pll_init(), spinning on PLL_CS_LOCK,
|
|
# which is before main() and before any watchdog tick exists, so
|
|
# nothing in this firmware could see it and no reflash could recover
|
|
# it.
|
|
#
|
|
# The PLL was not the problem; its reference was. The counter that
|
|
# gates XOSC_STATUS_STABLE is clocked by the crystal itself, so it
|
|
# measures oscillation and not amplitude - a crystal that is still
|
|
# building trips STABLE anyway, and pll_init() gets handed a
|
|
# reference too poor to lock to. At the default multiplier of 6 that
|
|
# is about 6ms. This makes it about 64ms.
|
|
#
|
|
# 20 cold power cycles out of 20 clean afterwards, against about 5 in
|
|
# 20 before. The cost is 58ms once per boot, which nobody can see
|
|
# next to USB enumeration.
|
|
#
|
|
# The default is what is wrong here, not the crystal. The part is
|
|
# the one the datasheet asks for, with the load caps and the damping
|
|
# resistor it asks for, and this has been seen on more than one board
|
|
# - so "that one crystal is marginal" does not survive contact with
|
|
# the evidence. Nor does "our layout", entirely: 18 board files in
|
|
# the SDK override this, from eight different vendors, and 17 of them
|
|
# pick exactly 64. Everyone who builds something that is not the
|
|
# reference design and looks at this raises it. What none of them
|
|
# do is leave it at 6 - and pico.h and pico2.h, which never had to
|
|
# care, are where the 6 comes from.
|
|
#
|
|
# What this does NOT do is make a dead crystal start; nothing here
|
|
# can, and a board whose crystal never oscillates still hangs in
|
|
# xosc_init() instead. Surviving that needs a bounded wait in place
|
|
# of the SDK's two unbounded ones, which means replacing the __weak
|
|
# runtime_init_clocks() rather than setting anything.
|
|
#
|
|
PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64
|
|
SAMPLES_PER_SEC=${SAMPLES_PER_SEC}f
|
|
CFG_TUSB_MCU=OPT_MCU_RP2040
|
|
CFG_TUSB_OS=OPT_OS_PICO
|
|
CFG_TUSB_DEBUG=0
|
|
#
|
|
# The reset interface picotool looks for. The sdk picks defaults
|
|
# for these from whether LIB_TINYUSB_* is defined, which comes from
|
|
# linking its tinyusb target - and this builds the submodule's
|
|
# sources directly, so they all land on the branch meant for a
|
|
# project not using tinyusb at all. Stated rather than inherited.
|
|
#
|
|
# The baud-rate escape is the 1200-baud trick and wants a CDC
|
|
# interface to have a baud rate on. There isn't one.
|
|
#
|
|
PICO_ENABLE_USB_RESET_VIA_VENDOR_INTERFACE=1
|
|
PICO_ENABLE_USB_RESET_VIA_BAUD_RATE=0
|
|
PICO_USB_RESET_INCLUDE_DEFAULT_APP_DRIVER_CB=1)
|
|
|
|
target_sources(${tgt} PRIVATE
|
|
Firmware/pedal.c
|
|
"${SINE_HEADER}"
|
|
Firmware/tinyusb/src/tusb.c
|
|
Firmware/tinyusb/src/common/tusb_fifo.c
|
|
Firmware/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c
|
|
)
|
|
|
|
target_sources(${tgt} PRIVATE
|
|
Firmware/usb-device.c
|
|
Firmware/tinyusb/src/device/usbd.c
|
|
Firmware/tinyusb/src/class/audio/audio_device.c
|
|
Firmware/tinyusb/src/class/midi/midi_device.c
|
|
Firmware/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c
|
|
#
|
|
# Compiled in rather than linked as pico_usb_reset, which would
|
|
# drag in the sdk's own tinyusb alongside the submodule this
|
|
# builds against. The file includes "tusb.h" and gets whichever
|
|
# one is on the path.
|
|
#
|
|
Firmware/pico-sdk/src/rp2_common/pico_usb_reset/usb_reset.c)
|
|
|
|
target_link_libraries(${tgt}
|
|
pico_stdlib
|
|
pico_multicore
|
|
hardware_gpio
|
|
hardware_pwm
|
|
hardware_pio
|
|
hardware_i2c
|
|
hardware_adc
|
|
hardware_dma
|
|
hardware_flash
|
|
pico_unique_id
|
|
hardware_watchdog
|
|
pico_sha256)
|
|
|
|
target_include_directories(${tgt} PRIVATE
|
|
#
|
|
# The repository root, so that a header says "Audio/foo.h" and the
|
|
# generated effect map says "Effects/bar.h", and both mean it from
|
|
# wherever they are included. Those two are top-level rather than
|
|
# under Firmware/ because Validation's bench compiles the same
|
|
# files and WebMIDI is generated from them.
|
|
#
|
|
.
|
|
Firmware
|
|
Firmware/include
|
|
Firmware/tinyusb/src
|
|
Firmware/pico-sdk/src/rp2_common/pico_usb_reset/include
|
|
Firmware/pico-sdk/src/common/pico_usb_reset_interface_headers/include
|
|
"${CMAKE_CURRENT_BINARY_DIR}")
|
|
|
|
#
|
|
# We really don't care about IEEE precision, nor
|
|
# do we want double-precision math just because
|
|
# of some random constant (and adding ".0f" to them
|
|
# everywhere is just cray-cray)
|
|
#
|
|
# Add -fsingle-precision-constant / -Wdouble-promotion
|
|
# when starting to do FP for audio
|
|
#
|
|
target_compile_options(${tgt} PRIVATE
|
|
-Wall -O2
|
|
-ffast-math
|
|
-fsingle-precision-constant
|
|
-Wfloat-conversion)
|
|
|
|
# create map/bin/hex/uf2 file in addition to ELF.
|
|
pico_add_extra_outputs(${tgt})
|
|
|
|
# Nothing on the audio core is allowed to call out to a library
|
|
add_custom_command(TARGET ${tgt} POST_BUILD
|
|
COMMAND "${SCRIPT_DIR}/check-audio.py"
|
|
"$<TARGET_FILE:${tgt}>.map"
|
|
"$<TARGET_FILE:${tgt}>"
|
|
"${CMAKE_OBJDUMP}"
|
|
VERBATIM)
|
|
|
|
# ...and nothing anywhere is allowed to use a double
|
|
add_custom_command(TARGET ${tgt} POST_BUILD
|
|
COMMAND "${SCRIPT_DIR}/check-float.py"
|
|
"$<TARGET_FILE:${tgt}>"
|
|
"${CMAKE_NM}"
|
|
VERBATIM)
|
|
|
|
# ...and the image is not allowed to grow into the save area
|
|
add_custom_command(TARGET ${tgt} POST_BUILD
|
|
COMMAND "${SCRIPT_DIR}/check-flash.py"
|
|
"$<TARGET_FILE:${tgt}>"
|
|
"${FLASH_SIZE_BYTES}"
|
|
"${SAVE_AREA_OFFSET}"
|
|
"${CMAKE_NM}"
|
|
VERBATIM)
|
|
|
|
#
|
|
# ...and the README is supposed to know which effects exist.
|
|
#
|
|
# The odd one out here: it looks at no build artifact, and it warns
|
|
# rather than failing. It is here anyway because this is the moment it
|
|
# is useful - you changed an effect header, so the build ran - and
|
|
# because a check nobody runs catches nothing. The README's list is
|
|
# hand-written by choice and it drifted to eight of seventeen; this is
|
|
# the cheap backstop for that and nothing more.
|
|
#
|
|
add_custom_command(TARGET ${tgt} POST_BUILD
|
|
COMMAND "${SCRIPT_DIR}/check-readme.py"
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/Effects"
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/README.md"
|
|
VERBATIM)
|
|
|
|
|
|
#
|
|
# Only the default board is in 'all', so a plain 'make' builds the one
|
|
# thing this tree is currently about. The others are a named target
|
|
# away and cost nothing until asked for.
|
|
#
|
|
if (NOT board STREQUAL PEDAL_BOARD)
|
|
set_target_properties(${tgt} PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
|
endif()
|
|
|
|
endfunction()
|
|
|
|
foreach(board ${PEDAL_BOARDS})
|
|
add_pedal_board(${board})
|
|
list(APPEND PEDAL_BOARD_TARGETS "pedal-${board}")
|
|
endforeach()
|
|
|
|
#
|
|
# Build every board. Nothing needs this to develop, and that is the
|
|
# point of having it: a change to shared code that breaks a board nobody
|
|
# has selected in a month is otherwise found by flashing it.
|
|
#
|
|
add_custom_target(all-boards DEPENDS ${PEDAL_BOARD_TARGETS})
|