Skip to content

Commit

Permalink
Merge branch 'edge' into flex-stacker-gparser-full-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
vegano1 authored Nov 7, 2024
2 parents 98d8fc0 + 9bc6094 commit 1cead73
Show file tree
Hide file tree
Showing 17 changed files with 583 additions and 72 deletions.
55 changes: 55 additions & 0 deletions stm32-modules/flex-stacker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Flex-Stacker Firmware

This directory has the code for the Flex-Stacker.

## Relevant build system targets

### Cross-build
When cross-compiling the firmware (using the `stm32-cross` cmake preset, running `cmake --build ./build-stm32-cross`), you can
- Build the firmware: `cmake --build ./build-stm32-cross --target flex-stacker`
- Build the firmware, connect to a debugger, and upload it: `cmake --build ./build-stm32-cross --target flex-stacker-debug`
- Lint the firmware: `cmake --build ./build-stm32-cross --target flex-stacker-lint`
- Format the firmware: `cmake --build ./build-stm32-cross --target flex-stacker-format`
- Flash the firmware to a board: `cmake --build ./build-stm32-cross --target flex-stacker-flash`
- Flash the firmware __and__ the startup application to a board: `cmake --build ./build-stm32-cross --target flex-stacker-image-flash`
- Builds flex-stacker-image.hex, suitable for use with stm's programmer: `cmake --build ./build-stm32-cross --target flex-stacker-image-hex`
- Builds flex-stacker-image.bin, suitable for some other programmers: `cmake --build ./build-stm32-cross --target flex-stacker-image-bin`
- Build the startup app, which is also packaged into the image files: `cmake --build ./build-stm32-cross --target flex-stacker-startup`
- Delete all of the contents on a flex-stacker MCU, and wipe any memory protection: `cmake --build ./build-stm32-cross --target flex-stacker-clear`

The default board revision is currently a1 (EVT). You can change the revision to the NFF board by running cmake `stm32-cross` preset again with
`cmake --preset stm32-cross -DSTACKER_REVISION=nff .`
before re-building the firmware.

### Debugging
There's a target called `flex-stacker-debug` that will build the firmware and then spin up a gdb, spin up an openocd, and connect the two; load some useful python scripts; connect to an st-link that should be already plugged in; automatically upload the firmware, and drop you at a breakpoint at boot time. This should all download itself and be ready as soon as `cmake --preset=stm32-cross .` completes, with one exception: Gdb python support is incredibly weird and will somehow always find your python2 that the system has, no matter how hard you try to avoid this. The scripts should work fine, but you have to install setuptools so `pkg_resources` is available, since this isn't really something we want to "install" by downloading it to some random directory and dropping it in gdb's embedded python interpreter's package path, so do the lovely

`wget https://bootstrap.pypa.io/pip/2.7/get-pip.py`
`python2.7 ./get-pip.py`

You do not have to sudo this, and should not sudo it, it works perfectly fine as a user install.

Once you spin this up, it provides peripheral register names and access through https://github.com/1udo6arre/svd-tools.
That means you can do stuff like
`help svd` - get info on svd commands (you don't have to do the `svd /path/to/svd`, that's done automatically)
`svd get nvic` - print all the nvic registers

### Host-build
When compiling the firmware using your local compiler (using the `stm32-host` cmake preset, running `cmake --build ./build-stm32-host`), you can
- Build tests: `cmake --build ./build-stm32-host --target flex-stacker-tests`
- Run tests: `cmake --build ./build-stm32-host --target test`
- Format tests: `cmake --build ./build-stm32-host --target flex-stacker-format`
- Build simulator: `cmake --build ./build-stm32-host --target flex-stacker-simulator`
- Build and Test: `cmake --build ./build-stm32-host --target flex-stacker-build-and-test`

## File Structure
- `./tests/` contains the test-specific entrypoints and actual test code
- `./firmware` contains the code that only runs on the device itself
- `./simulator` contains the code that runs a local simulator
- `./src` contains the code that can be either cross- or host-compiled, and therefore can and should be tested

## Style

We enforce style with [clang-format](https://clang.llvm.org/docs/ClangFormat.html), using the [google C++ style](https://google.github.io/styleguide/cppguide.html). We lint using [clang-tidy](https://clang.llvm.org/extra/clang-tidy/). The configurations for these tools are in the root of the repo (they're hidden files on Unix-likes, `.clang-tidy` and `.clang-format`).

We use [Catch2](https://github.com/catchorg/Catch2) for testing.
4 changes: 4 additions & 0 deletions stm32-modules/flex-stacker/firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ set(${TARGET_MODULE_NAME}_FW_LINTABLE_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${COMMS_DIR}/freertos_comms_task.cpp
${SYSTEM_DIR}/freertos_idle_timer_task.cpp
${SYSTEM_DIR}/freertos_system_task.cpp
${SYSTEM_DIR}/system_policy.cpp
${UI_DIR}/freertos_ui_task.cpp
${MOTOR_CONTROL_DIR}/freertos_motor_task.cpp
${MOTOR_CONTROL_DIR}/freertos_motor_driver_task.cpp
Expand All @@ -32,6 +34,8 @@ set(${TARGET_MODULE_NAME}_FW_NONLINTABLE_SRCS
${SYSTEM_DIR}/stm32g4xx_it.c
${SYSTEM_DIR}/system_stm32g4xx.c
${SYSTEM_DIR}/hal_tick.c
${SYSTEM_DIR}/system_serial_number.c
${SYSTEM_DIR}/system_hardware.c
${UI_DIR}/ui_hardware.c
${COMMS_DIR}/usbd_conf.c
${COMMS_DIR}/usbd_desc.c
Expand Down
11 changes: 11 additions & 0 deletions stm32-modules/flex-stacker/firmware/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ static auto ui_task_entry = EntryPoint(ui_control_task::run);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto host_comms_entry = EntryPoint(host_comms_control_task::run);

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto system_task_entry = EntryPoint(system_control_task::run);

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto aggregator = tasks::FirmwareTasks::QueueAggregator();

Expand All @@ -51,6 +54,11 @@ static auto ui_task =
ot_utils::freertos_task::FreeRTOSTask<tasks::UI_STACK_SIZE, EntryPoint>(
ui_task_entry);

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto system_task =
ot_utils::freertos_task::FreeRTOSTask<tasks::SYSTEM_STACK_SIZE, EntryPoint>(
system_task_entry);

extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
switch (GPIO_Pin) {
case MOTOR_DIAG0_PIN:
Expand All @@ -65,13 +73,16 @@ extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
auto main() -> int {
HardwareInit();

system_task.start(tasks::SYSTEM_TASK_PRIORITY, "System", &aggregator);

driver_task.start(tasks::MOTOR_DRIVER_TASK_PRIORITY, "Motor Driver",
&aggregator);
motor_task.start(tasks::MOTOR_TASK_PRIORITY, "Motor", &aggregator);

host_comms_task.start(tasks::COMMS_TASK_PRIORITY, "Comms", &aggregator);

ui_task.start(tasks::UI_TASK_PRIORITY, "UI", &aggregator);

vTaskStartScheduler();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "firmware/freertos_tasks.hpp"
#include "firmware/system_policy.hpp"
#include "flex-stacker/system_task.hpp"

namespace system_control_task {

enum class Notifications : uint8_t {
INCOMING_MESSAGE = 1,
};

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static tasks::FirmwareTasks::SystemQueue
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
_queue(static_cast<uint8_t>(Notifications::INCOMING_MESSAGE),
"System Queue");

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto _top_task = system_task::SystemTask(_queue, nullptr);

auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void {
auto* handle = xTaskGetCurrentTaskHandle();
_queue.provide_handle(handle);
aggregator->register_queue(_queue);
_top_task.provide_aggregator(aggregator);

auto policy = SystemPolicy();
while (true) {
_top_task.run_once(policy);
}
}

}; // namespace system_control_task
60 changes: 60 additions & 0 deletions stm32-modules/flex-stacker/firmware/system/system_hardware.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "firmware/system_hardware.h"
#include "stm32g4xx_hal.h"
#include "stm32g4xx_hal_rcc.h"
#include "stm32g4xx_hal_cortex.h"
#include "stm32g4xx_hal_tim.h"

/** Local defines */
// This is the start of the sys memory region for the STM32G491
// from the reference manual and STM application note AN2606
#define SYSMEM_START 0x1fff0000
#define SYSMEM_BOOT (SYSMEM_START + 4)

// address 4 in the bootable region is the address of the first instruction that
// should run, aka the data that should be loaded into $pc.
const uint32_t *const sysmem_boot_loc = (uint32_t*)SYSMEM_BOOT;

/** PUBLIC FUNCTION IMPLEMENTATION */

void system_hardware_enter_bootloader(void) {

// We have to uninitialize as many of the peripherals as possible, because the bootloader
// expects to start as the system comes up

// The HAL has ways to turn off all the core clocking and the clock security system
HAL_RCC_DisableLSECSS();
HAL_RCC_DeInit();

// systick should be off at boot
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;

/* Clear Interrupt Enable Register & Interrupt Pending Register */
for (int i=0;i<8;i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}

// We have to make sure that the processor is mapping the system memory region to address 0,
// which the bootloader expects
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
// and now we're ready to set the system up to start executing system flash.
// arm cortex initialization means that
// address 0 in the bootable region is the address where the processor should start its stack
// which we have to do as late as possible because as soon as we do this the c and c++ runtime
// environment is no longer valid
__set_MSP(*((uint32_t*)SYSMEM_START));

// finally, jump to the bootloader. we do this in inline asm because we need
// this to be a naked call (no caller-side prep like stacking return addresses)
// and to have a naked function you need to define it as a function, not a
// function pointer, and we don't statically know the address here since it is
// whatever's contained in that second word of the bsystem memory region.
asm volatile (
"bx %0"
: // no outputs
: "r" (*sysmem_boot_loc)
: "memory" );
}
59 changes: 59 additions & 0 deletions stm32-modules/flex-stacker/firmware/system/system_policy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "firmware/system_policy.hpp"

#include <array>
#include <iterator>
#include <ranges>

#include "firmware/system_hardware.h"
#include "firmware/system_serial_number.h"
#include "flex-stacker/errors.hpp"

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
auto SystemPolicy::enter_bootloader() -> void {
system_hardware_enter_bootloader();
}

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
auto SystemPolicy::set_serial_number(
std::array<char, SYSTEM_SERIAL_NUMBER_LENGTH> system_serial_number)
-> errors::ErrorCode {
writable_serial to_write_struct = {0};
// convert bytes to uint64_t for system_set_serial_number
// write to 8 chars to each of first 3 addresses on last page of flash
for (uint8_t address = 0; address < ADDRESSES; address++) {
auto *input =
std::next(system_serial_number.begin(), address * ADDRESS_LENGTH);
auto *limit = std::next(input, ADDRESS_LENGTH);
uint64_t to_write = 0;
for (ssize_t byte_index = sizeof(to_write) - 1;
input != limit && byte_index >= 0;
std::advance(input, 1), byte_index--) {
to_write |= (static_cast<uint64_t>(*input) << (byte_index * 8));
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
to_write_struct.contents[address] = to_write;
}
if (!system_set_serial_number(&to_write_struct)) {
return errors::ErrorCode::SYSTEM_SERIAL_NUMBER_HAL_ERROR;
}
return errors::ErrorCode::NO_ERROR;
}

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
auto SystemPolicy::get_serial_number()
-> std::array<char, SYSTEM_SERIAL_NUMBER_LENGTH> {
std::array<char, SYSTEM_SERIAL_NUMBER_LENGTH> serial_number_array = {
"EMPTYSN"};
for (uint8_t address = 0; address < ADDRESSES; address++) {
uint64_t written_serial_number = system_get_serial_number(address);
// int to bytes
auto *output =
std::next(serial_number_array.begin(), address * ADDRESS_LENGTH);
auto *limit = std::next(output, ADDRESS_LENGTH);
for (ssize_t iter = sizeof(written_serial_number) - 1;
iter >= 0 && output != limit; iter--, std::advance(output, 1)) {
*output = (written_serial_number >> (iter * 8));
}
}
return serial_number_array;
}
47 changes: 47 additions & 0 deletions stm32-modules/flex-stacker/firmware/system/system_serial_number.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <stdlib.h>

#include "firmware/system_serial_number.h"

#include "stm32g4xx_hal.h"
#include "stm32g4xx_hal_def.h"
#include "stm32g4xx_hal_flash.h"
#include "stm32g4xx_hal_flash_ex.h"

static const uint32_t PAGE_ADDRESS = 0x0807F800; //last page in flash memory, 0x0807F800 for 512K (FF board) FLASH
static const uint32_t PAGE_INDEX = 255; //last page index in flash memory
static const uint8_t ADDRESS_SIZE = 8;

bool system_set_serial_number(struct writable_serial* to_write) {
FLASH_EraseInitTypeDef pageToErase = {
.TypeErase = FLASH_TYPEERASE_PAGES,
.Banks = FLASH_BANK_1,
.Page = PAGE_INDEX,
.NbPages = 1};
uint32_t pageErrorPtr = 0; //pointer to variable that contains the configuration information on faulty page in case of error
uint32_t ProgramAddress1 = PAGE_ADDRESS;
uint32_t ProgramAddress2 = PAGE_ADDRESS + ADDRESS_SIZE;
uint32_t ProgramAddress3 = PAGE_ADDRESS + (2 * ADDRESS_SIZE);

HAL_StatusTypeDef status = HAL_FLASH_Unlock();
if (status == HAL_OK) {
status = HAL_FLASHEx_Erase(&pageToErase, &pageErrorPtr);
if (status == HAL_OK) {
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, ProgramAddress1, to_write->contents[0]);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, ProgramAddress2, to_write->contents[1]);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, ProgramAddress3, to_write->contents[2]);
if (status == HAL_OK) {
status = HAL_FLASH_Lock();
}
}
else {
// Safe to drop status because this always succeeds
(void) HAL_FLASH_Lock();
}
}
return (status == HAL_OK);
}

uint64_t system_get_serial_number(uint8_t address) {
uint32_t AddressToRead = PAGE_ADDRESS + (address * ADDRESS_SIZE);
return *(uint64_t*)AddressToRead;
}
13 changes: 13 additions & 0 deletions stm32-modules/flex-stacker/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ if (NOT VERSION)
set(VERSION "(dev)")
endif()

set(ALLOWED_REVISIONS nff a1)
set(DEFAULT_REV a1)
if(DEFINED STACKER_REVISION)
list(FIND ALLOWED_REVISIONS ${STACKER_REVISION} index)
if(index EQUAL -1)
message(FATAL_ERROR "Invalid REVISION value: ${STACKER_REVISION}. Allowed values are: ${ALLOWED_REVISIONS}")
endif()
else()
set(STACKER_REVISION ${DEFAULT_REV})
endif()

set(${TARGET_MODULE_NAME}_VERSION "${VERSION}" CACHE STRING "${TARGET_MODULE_NAME} fw version" FORCE)
set(${TARGET_MODULE_NAME}_BOARD_REVISION "${TARGET_MODULE_NAME}-${STACKER_REVISION}" CACHE STRING "${TARGET_MODULE_NAME} board revision" FORCE)
message(STATUS "Building ${TARGET_MODULE_NAME} version: ${${TARGET_MODULE_NAME}_VERSION} for board revision: ${${TARGET_MODULE_NAME}_BOARD_REVISION}")

configure_file(./version.cpp.in ./version.cpp)

Expand Down
3 changes: 2 additions & 1 deletion stm32-modules/flex-stacker/src/version.cpp.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "core/version.hpp"

static constexpr const char* _FW_VERSION_GENERATED = "${${TARGET_MODULE_NAME}_VERSION}";
static constexpr const char* _HW_VERSION_GENERATED = "Opentrons-${TARGET_MODULE_NAME}";
static constexpr const char* _HW_VERSION_GENERATED =
"Opentrons-${${TARGET_MODULE_NAME}_BOARD_REVISION}";

const char* version::fw_version() {
return _FW_VERSION_GENERATED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ namespace host_comms_control_task {
// Actual function that runs in the task
auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void;
} // namespace host_comms_control_task

namespace system_control_task {
// Actual function that runs in the task
auto run(tasks::FirmwareTasks::QueueAggregator* aggregator) -> void;
} // namespace system_control_task
17 changes: 17 additions & 0 deletions stm32-modules/include/flex-stacker/firmware/system_hardware.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef SYSTEM_HARDWARE_H__
#define SYSTEM_HARDWARE_H__
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

#include <stdbool.h>

/**
* @brief Enter the bootloader. This function never returns.
*/
void system_hardware_enter_bootloader(void);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // _SYSTEM_HARDWARE_H__
24 changes: 24 additions & 0 deletions stm32-modules/include/flex-stacker/firmware/system_policy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <array>

#include "firmware/system_hardware.h"
#include "firmware/system_serial_number.h"
#include "flex-stacker/errors.hpp"
#include "systemwide.h"

class SystemPolicy {
private:
static constexpr std::size_t SYSTEM_SERIAL_NUMBER_LENGTH =
SYSTEM_WIDE_SERIAL_NUMBER_LENGTH;
static constexpr uint8_t ADDRESS_LENGTH = 8;
static constexpr uint8_t ADDRESSES =
SYSTEM_SERIAL_NUMBER_LENGTH / ADDRESS_LENGTH;

public:
auto enter_bootloader() -> void;
auto set_serial_number(
std::array<char, SYSTEM_SERIAL_NUMBER_LENGTH> system_serial_number)
-> errors::ErrorCode;
auto get_serial_number() -> std::array<char, SYSTEM_SERIAL_NUMBER_LENGTH>;
};
Loading

0 comments on commit 1cead73

Please sign in to comment.