[FL-1443, FL-1289] Move assets compilation to separate Makefile. Add scripts folder. Add OTP flashing with DFU. (#531)

* Assets: move assets compilation to separate Makefile. Move all scripts to scripts folder. Add scripts ReadMe. Add precompiled assets.
* Split assets.py into separate entities. Option bytes for FL-1289 and checker/setter.
* Cli: explicitly initialize variable befor use in api_hal_vcp_rx_with_timeout
* Rename ob_check script to ob.
This commit is contained in:
あく 2021-06-23 17:58:44 +03:00 committed by GitHub
parent 359bbdfe69
commit 8116bfcbab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1110 additions and 87 deletions

View File

@ -51,7 +51,7 @@ size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
}
bool cli_cmd_interrupt_received(Cli* cli) {
char c;
char c = '\0';
api_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1);
return c == CliSymbolAsciiETX;
}

11
assets/Makefile Normal file
View File

@ -0,0 +1,11 @@
PROJECT_ROOT = $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..)
include $(PROJECT_ROOT)/assets/assets.mk
$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
@echo "\tASSETS\t" $@
@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_COMPILED_DIR)
clean:
@echo "\tCLEAN\t"
@$(RM) $(ASSETS)

View File

@ -1,3 +1,13 @@
# Requirements
- Python3
- ImageMagic
- Make
# Compiling
make all
# Asset naming rules
## Images and Animations

View File

@ -1,10 +1,10 @@
ASSETS_DIR := $(PROJECT_ROOT)/assets
ASSETS_COMPILLER := $(ASSETS_DIR)/assets.py
ASSETS_OUTPUT_DIR := $(ASSETS_DIR)/output
ASSETS_COMPILLER := $(PROJECT_ROOT)/scripts/assets.py
ASSETS_COMPILED_DIR := $(ASSETS_DIR)/compiled
ASSETS_SOURCE_DIR := $(ASSETS_DIR)/icons
ASSETS_SOURCES += $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate')
ASSETS += $(ASSETS_OUTPUT_DIR)/assets_icons.c
ASSETS += $(ASSETS_COMPILED_DIR)/assets_icons.c
CFLAGS += -I$(ASSETS_OUTPUT_DIR)
C_SOURCES += $(ASSETS_OUTPUT_DIR)/assets_icons.c
CFLAGS += -I$(ASSETS_COMPILED_DIR)
C_SOURCES += $(ASSETS_COMPILED_DIR)/assets_icons.c

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,131 @@
#pragma once
#include <gui/icon.h>
typedef enum {
I_SDQuestion_35x43,
I_SDError_43x35,
I_Health_16x16,
I_FaceCharging_29x14,
I_BatteryBody_52x28,
I_Voltage_16x16,
I_Temperature_16x16,
I_FaceNopower_29x14,
I_FaceNormal_29x14,
I_Battery_16x16,
I_FaceConfused_29x14,
I_PassportBottom_128x17,
I_DoorLeft_8x56,
I_DoorLocked_10x56,
I_DoorRight_8x56,
I_DoorLeft_70x55,
I_PassportLeft_6x47,
I_DoorRight_70x55,
I_LockPopup_100x49,
I_WalkR2_32x32,
I_WalkL2_32x32,
I_WalkRB1_32x32,
I_Home_painting_17x20,
I_WalkLB2_32x32,
I_Sofa_40x13,
I_WalkLB1_32x32,
I_PC_22x29,
I_WalkL1_32x32,
I_TV_20x20,
I_WalkR1_32x32,
I_WalkRB2_32x32,
I_TV_20x24,
I_dir_10px,
I_Nfc_10px,
I_sub1_10px,
I_ir_10px,
I_ibutt_10px,
I_unknown_10px,
I_ble_10px,
I_125_10px,
I_FX_SittingB_40x27,
I_BigGames_24x24,
I_BigProfile_24x24,
I_DolphinOkay_41x43,
I_DolphinFirstStart5_45x53,
I_DolphinFirstStart4_67x53,
I_DolphinFirstStart2_59x51,
I_DolphinFirstStart0_70x53,
I_DolphinFirstStart6_58x54,
I_DolphinFirstStart1_59x53,
I_DolphinFirstStart8_56x51,
I_DolphinFirstStart7_61x51,
I_Flipper_young_80x60,
I_BigBurger_24x24,
I_FX_Bang_32x6,
I_DolphinFirstStart3_57x48,
I_BadUsb_9x8,
I_PlaceholderR_30x13,
I_Background_128x8,
I_Lock_8x8,
I_Battery_26x8,
I_PlaceholderL_11x13,
I_Battery_19x8,
I_SDcardMounted_11x8,
I_SDcardFail_11x8,
I_USBConnected_15x8,
I_Bluetooth_5x8,
I_Background_128x11,
I_IrdaArrowUp_4x8,
I_IrdaLearnShort_128x31,
I_IrdaArrowDown_4x8,
I_IrdaLearn_128x64,
I_IrdaSend_128x64,
I_IrdaSendShort_128x34,
I_passport_happy1_43x45,
I_passport_bad3_43x45,
I_passport_okay2_43x45,
I_passport_bad2_43x45,
I_passport_okay3_43x45,
I_passport_bad1_43x45,
I_passport_happy3_43x45,
I_passport_happy2_43x45,
I_passport_okay1_43x45,
I_ButtonRightSmall_3x5,
I_ButtonLeft_4x7,
I_ButtonLeftSmall_3x5,
I_ButtonRight_4x7,
I_ButtonCenter_7x7,
A_Games_14,
A_Plugins_14,
A_Passport_14,
A_Sub1ghz_14,
A_NFC_14,
A_Tamagotchi_14,
A_FileManager_14,
A_125khz_14,
A_U2F_14,
A_Infrared_14,
A_Power_14,
A_Settings_14,
A_iButton_14,
A_Bluetooth_14,
A_GPIO_14,
I_DolphinMafia_115x62,
I_DolphinExcited_64x63,
I_iButtonDolphinSuccess_109x60,
I_iButtonDolphinVerySuccess_108x52,
I_iButtonKey_49x44,
I_DolphinNice_96x59,
I_DolphinWait_61x59,
A_Wink_128x64,
A_MDWL_32x32,
A_MDWR_32x32,
A_WatchingTV_128x64,
A_MDI_32x32,
A_MDWRB_32x32,
A_MDIB_32x32,
A_FX_Sitting_40x27,
A_MDWLB_32x32,
I_KeySave_24x11,
I_KeyBackspaceSelected_16x9,
I_KeySaveSelected_24x11,
I_KeyBackspace_16x9,
} IconName;
Icon * assets_icons_get(IconName name);

View File

@ -0,0 +1,5 @@
#pragma once
#include <assets_icons.h>
const IconData * assets_icons_get_data(IconName name);

View File

@ -1 +0,0 @@
*

View File

@ -8,7 +8,7 @@ ASM_SOURCES += $(wildcard src/*.s)
C_SOURCES += $(wildcard src/*.c)
CPP_SOURCES += $(wildcard src/*.cpp)
TARGET ?= f5
TARGET ?= f6
TARGET_DIR = targets/$(TARGET)
include $(TARGET_DIR)/target.mk

View File

@ -3,6 +3,7 @@ PROJECT = firmware
include $(PROJECT_ROOT)/make/base.mk
include $(PROJECT_ROOT)/assets/assets.mk
CFLAGS += -I$(ASSETS_COMPILED_DIR)
include $(PROJECT_ROOT)/core/core.mk
include $(PROJECT_ROOT)/applications/applications.mk
include $(PROJECT_ROOT)/lib/lib.mk
@ -10,7 +11,7 @@ include $(PROJECT_ROOT)/lib/lib.mk
CFLAGS += -Werror -Wno-address-of-packed-member
CPPFLAGS += -Werror
TARGET ?= f5
TARGET ?= f6
TARGET_DIR = targets/$(TARGET)

View File

@ -1,9 +0,0 @@
#!/bin/bash
set -x -e
rm bootloader/.obj/f*/flash || true
make -C bootloader -j9 flash
rm firmware/.obj/f*/flash || true
make -C firmware -j9 flash

View File

@ -47,15 +47,15 @@ $(OBJ_DIR)/$(PROJECT).bin: $(OBJ_DIR)/$(PROJECT).elf
@echo "\tBIN\t" $@
@$(BIN) $< $@
$(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
$(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS
@echo "\tCC\t" $< "->" $@
@$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/%.o: %.s $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
$(OBJ_DIR)/%.o: %.s $(OBJ_DIR)/BUILD_FLAGS
@echo "\tASM\t" $< "->" $@
@$(AS) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/%.o: %.cpp $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
$(OBJ_DIR)/%.o: %.cpp $(OBJ_DIR)/BUILD_FLAGS
@echo "\tCPP\t" $< "->" $@
@$(CPP) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
@ -67,10 +67,6 @@ $(OBJ_DIR)/upload: $(OBJ_DIR)/$(PROJECT).bin
dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS)
touch $@
$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
@echo "\tASSETS\t" $@
@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_OUTPUT_DIR)
flash: $(OBJ_DIR)/flash
upload: $(OBJ_DIR)/upload
@ -104,7 +100,6 @@ bm_debug: flash
clean:
@echo "\tCLEAN\t"
@$(RM) $(OBJ_DIR)/*
@$(RM) $(ASSETS)
z: clean
$(MAKE) all
@ -123,7 +118,7 @@ format:
@echo "Formatting sources with clang-format"
@clang-format -style=file -i $(FORMAT_SOURCES)
generate_cscope_db: $(ASSETS)
generate_cscope_db:
@echo "$(C_SOURCES) $(CPP_SOURCES) $(ASM_SOURCES)" | tr ' ' '\n' > $(OBJ_DIR)/source.list.p
@cat ~/headers.list >> $(OBJ_DIR)/source.list.p
@cat $(OBJ_DIR)/source.list.p | sed -e "s|^[^//]|$$PWD/&|g" > $(OBJ_DIR)/source.list

51
scripts/ReadMe.md Normal file
View File

@ -0,0 +1,51 @@
# About
This folder contains differnt scripts that automates routine actions.
Flashing scripts are based on cli version of [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html).
You will need to add STM32_Programmer_CLI to your path to use them.
# Flashing empty MCU/Flipper
Always flash your device in the folllowing sequence:
- OTP (Only on empty MCU)
- Core2 firmware
- Core1 firmware
- Option Bytes
## Otp flashing
!!! Flashing incorrect OTP may permanently brick your device !!!
Normally OTP data generated and flashed at factory.
In case if MCU was replaced you'll need correct OTP data to be able to use companion applications.
Use `otp.py` to generate OTP data and `flash_otp_version_*` to flash OTP zone.
You will need exact main board revision to genrate OTP data. It can be found on main PCB.
!!! Flashing incorrect OTP may permanently brick your device !!!
## Core2 flashing
Script blindly updates FUS and Radiostack. This operation is going to corrupt bootloader and firmware.
Reflash Core1 after Core2.
## Core1 flashing
Script compiles and flashes both bootloader and firmware.
## Option Bytes
!!! Setting incorrect Otion Bytes may brick your MCU !!!
Defaults are mostly OK, but there are couple things that we'd like to tune.
Also OB may be damaged, so we've made couple scripts to check and set option bytes.
!!! Setting incorrect Otion Bytes may brick your MCU !!!
Checking option bytes:
ob.py check
Setting option bytes:
ob.py set

View File

@ -6,9 +6,6 @@ import subprocess
import io
import os
import sys
import re
import struct
import datetime
ICONS_SUPPORTED_FORMATS = ["png"]
@ -51,7 +48,7 @@ Icon * assets_icons_get(IconName name) {
"""
class Assets:
class Main:
def __init__(self):
# command args
self.parser = argparse.ArgumentParser()
@ -67,22 +64,6 @@ class Assets:
"-o", "--output-directory", help="Output directory"
)
self.parser_icons.set_defaults(func=self.icons)
self.parser_otp = self.subparsers.add_parser(
"otp", help="OTP HW version generator"
)
self.parser_otp.add_argument(
"--version", type=int, help="Version", required=True
)
self.parser_otp.add_argument(
"--firmware", type=int, help="Firmware", required=True
)
self.parser_otp.add_argument("--body", type=int, help="Body", required=True)
self.parser_otp.add_argument(
"--connect", type=int, help="Connect", required=True
)
self.parser_otp.add_argument("--name", type=str, help="Name", required=True)
self.parser_otp.add_argument("file", help="Output file")
self.parser_otp.set_defaults(func=self.otp)
# logging
self.logger = logging.getLogger()
@ -101,39 +82,6 @@ class Assets:
# execute requested function
self.args.func()
def otp(self):
self.logger.debug(f"Generating OTP")
if self.args.name:
name = re.sub(
"[^a-zA-Z0-9.]", "", self.args.name
) # Filter all special characters
name = list(
map(str, map(ord, name[0:8]))
) # Strip to 8 chars and map to ascii codes
while len(name) < 8:
name.append("0")
n1, n2, n3, n4, n5, n6, n7, n8 = map(int, name)
data = struct.pack(
"<BBBBLBBBBBBBB",
self.args.version,
self.args.firmware,
self.args.body,
self.args.connect,
int(datetime.datetime.now().timestamp()),
n1,
n2,
n3,
n4,
n5,
n6,
n7,
n8,
)
open(self.args.file, "wb").write(data)
def icons(self):
self.logger.debug(f"Converting icons")
icons_c = open(os.path.join(self.args.output_directory, "assets_icons.c"), "w")
@ -248,4 +196,4 @@ class Assets:
if __name__ == "__main__":
Assets()()
Main()()

12
scripts/flash_core1_main_swd.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
set -x -e
SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
rm $PROJECT_DIR/bootloader/.obj/f*/flash || true
make -C $PROJECT_DIR/bootloader -j9 flash
rm $PROJECT_DIR/firmware/.obj/f*/flash || true
make -C $PROJECT_DIR/firmware -j9 flash

View File

@ -2,8 +2,9 @@
set -x -e
COPRO_DIR="lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
COPRO_DIR="$PROJECT_DIR/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
STM32_Programmer_CLI -c port=swd -fwupgrade $COPRO_DIR/stm32wb5x_FUS_fw_1_0_2.bin 0x080EC000 || true
STM32_Programmer_CLI -c port=swd

View File

@ -0,0 +1,17 @@
#!/bin/bash
set -x -e
if [ "$#" -ne 1 ]; then
echo "OTP file required"
exit
fi
if [ ! -f $1 ]; then
echo "Unable to open OTP file"
exit
fi
STM32_Programmer_CLI -c port=usb1 -d $1 0x1FFF7000
STM32_Programmer_CLI -c port=usb1 -r8 0x1FFF7000 8

113
scripts/ob.py Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env python3
import logging
import argparse
import subprocess
import sys
import os
class Main:
def __init__(self):
# command args
self.parser = argparse.ArgumentParser()
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
self.subparsers = self.parser.add_subparsers(help="sub-command help")
self.parser_check = self.subparsers.add_parser(
"check", help="Check Option Bytes"
)
self.parser_check.set_defaults(func=self.check)
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
self.parser_set.set_defaults(func=self.set)
# logging
self.logger = logging.getLogger()
# OB
self.ob = {}
def __call__(self):
self.args = self.parser.parse_args()
if "func" not in self.args:
self.parser.error("Choose something to do")
# configure log output
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
self.logger.setLevel(self.log_level)
self.handler = logging.StreamHandler(sys.stdout)
self.handler.setLevel(self.log_level)
self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
self.handler.setFormatter(self.formatter)
self.logger.addHandler(self.handler)
# execute requested function
self.loadOB()
self.args.func()
def loadOB(self):
self.logger.info(f"Loading Option Bytes data")
file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
file = open(file_path, "r")
for line in file.readlines():
k, v, o = line.split(":")
self.ob[k.strip()] = v.strip(), o.strip()
def check(self):
self.logger.info(f"Checking Option Bytes")
try:
output = subprocess.check_output(
["STM32_Programmer_CLI", "-q", "-c port=swd", "-ob displ"]
)
assert output
except Exception as e:
self.logger.error(f"Failed to call STM32_Programmer_CLI")
self.logger.exception(e)
return
ob_correct = True
for line in output.decode().split("\n"):
line = line.strip()
if not ":" in line:
self.logger.debug(f"Skipping line: {line}")
continue
key, data = line.split(":", 1)
key = key.strip()
data = data.strip()
if not key in self.ob.keys():
self.logger.debug(f"Skipping key: {key}")
continue
self.logger.debug(f"Processing key: {key} {data}")
value, comment = data.split(" ", 1)
value = value.strip()
comment = comment.strip()
if self.ob[key][0] != value:
self.logger.error(
f"Invalid OB: {key} {value}, expected: {self.ob[key][0]}"
)
ob_correct = False
if ob_correct:
self.logger.info(f"OB Check OK")
else:
self.logger.error(f"OB Check FAIL")
exit(255)
def set(self):
self.logger.info(f"Setting Option Bytes")
options = []
for key, (value, attr) in self.ob.items():
if "w" in attr:
options.append(f"{key}={value}")
try:
output = subprocess.check_output(
[
"STM32_Programmer_CLI",
"-q",
"-c port=swd",
f"-ob {' '.join(options)}",
]
)
assert output
self.logger.info(f"Success")
except Exception as e:
self.logger.error(f"Failed to call STM32_Programmer_CLI")
self.logger.exception(e)
return
if __name__ == "__main__":
Main()()

34
scripts/ob_check.data Normal file
View File

@ -0,0 +1,34 @@
RDP:0xAA:r
BOR_LEV:0x4:rw
nBOOT0:0x1:rw
nBOOT1:0x1:rw
nSWBOOT0:0x1:rw
SRAM2RST:0x0:rw
SRAM2PE:0x1:rw
nRST_STOP:0x1:rw
nRST_STDBY:0x1:rw
nRSTSHDW:0x1:rw
WWDGSW:0x1:rw
IWGDSTDBY:0x1:rw
IWDGSTOP:0x1:rw
IWDGSW:0x1:rw
IPCCDBA:0x0:rw
ESE:0x1:r
SFSA:0xCB:r
FSD:0x0:r
DDS:0x1:r
C2OPT:0x1:r
NBRSD:0x0:r
SNBRSA:0xF:r
BRSD:0x0:r
SBRSA:0xA:r
SBRV:0x32C00:r
PCROP1A_STRT:0x1FF:r
PCROP1A_END:0x0:r
PCROP_RDP:0x1:r
PCROP1B_STRT:0x1FF:r
PCROP1B_END:0x0:r
WRP1A_STRT:0xFF:r
WRP1A_END:0x0:r
WRP1B_STRT:0xFF:r
WRP1B_END:0x0:r

91
scripts/otp.py Executable file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
import logging
import argparse
import os
import sys
import re
import struct
import datetime
class Main:
def __init__(self):
# command args
self.parser = argparse.ArgumentParser()
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
self.subparsers = self.parser.add_subparsers(help="sub-command help")
self.parser_generate = self.subparsers.add_parser(
"generate", help="OTP HW version generator"
)
self.parser_generate.add_argument(
"--version", type=int, help="Version", required=True
)
self.parser_generate.add_argument(
"--firmware", type=int, help="Firmware", required=True
)
self.parser_generate.add_argument(
"--body", type=int, help="Body", required=True
)
self.parser_generate.add_argument(
"--connect", type=int, help="Connect", required=True
)
self.parser_generate.add_argument(
"--name", type=str, help="Name", required=True
)
self.parser_generate.add_argument("file", help="Output file")
self.parser_generate.set_defaults(func=self.generate)
# logging
self.logger = logging.getLogger()
def __call__(self):
self.args = self.parser.parse_args()
if "func" not in self.args:
self.parser.error("Choose something to do")
# configure log output
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
self.logger.setLevel(self.log_level)
self.handler = logging.StreamHandler(sys.stdout)
self.handler.setLevel(self.log_level)
self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
self.handler.setFormatter(self.formatter)
self.logger.addHandler(self.handler)
# execute requested function
self.args.func()
def generate(self):
self.logger.debug(f"Generating OTP")
if self.args.name:
name = re.sub(
"[^a-zA-Z0-9.]", "", self.args.name
) # Filter all special characters
name = list(
map(str, map(ord, name[0:8]))
) # Strip to 8 chars and map to ascii codes
while len(name) < 8:
name.append("0")
n1, n2, n3, n4, n5, n6, n7, n8 = map(int, name)
data = struct.pack(
"<BBBBLBBBBBBBB",
self.args.version,
self.args.firmware,
self.args.body,
self.args.connect,
int(datetime.datetime.now().timestamp()),
n1,
n2,
n3,
n4,
n5,
n6,
n7,
n8,
)
open(self.args.file, "wb").write(data)
if __name__ == "__main__":
Main()()

View File

@ -1,4 +0,0 @@
#!/usr/bin/env bash
echo "RUN SYNTAX CHECK INSIDE CONTAINER"
docker-compose exec dev ./docker/syntax_check.sh