Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ lib/zephyr-workspace/*
*.gcov
build
settings.ini
bootable.uf2

**/__pycache__/
**/*.egg-info/

bin/
/bin/
_codeql_detected_source_root
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/fprime-zephyr"]
path = lib/fprime-zephyr
url = https://github.com/fprime-community/fprime-zephyr.git
[submodule "lib/fprime-extras"]
path = lib/fprime-extras
url = https://github.com/LeStarch/fprime-extras
6 changes: 6 additions & 0 deletions .west/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[manifest]
path = .
file = west.yml

[zephyr]
base = ./lib/zephyr-workspace/zephyr
1 change: 1 addition & 0 deletions FprimeZephyrReference/Components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Burnwire/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComDelay/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Drv/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FatalHandler")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FlashWorker/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FsSpace/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ImuManager/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LoadSwitch/")
Expand Down
15 changes: 3 additions & 12 deletions FprimeZephyrReference/Components/FatalHandler/FatalHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,14 @@ FatalHandler ::FatalHandler(const char* const compName) : FatalHandlerComponentB
FatalHandler ::~FatalHandler() {}

void FatalHandler::reboot() {
// When running in CI failsafe mode and the board is a teensy,
// then we should invoke bkpt #251 to trigger the soft reboot enabling a
// flash of new software
#if defined(FPRIME_CI_FAILSAFE_CYCLE_COUNT)
// Magic bootloader breakpoint, provided by PRJC
if (strncmp(CONFIG_BOARD, "teensy", 6) == 0) {
asm("bkpt #251");
}
#endif
// Otherwise, use Zephyr to reboot the system
sys_reboot(SYS_REBOOT_COLD);
}

void FatalHandler::FatalReceive_handler(const FwIndexType portNum, FwEventIdType Id) {
Fw::Logger::log("FATAL %" PRI_FwEventIdType "handled.\n", Id);
Os::Task::delay(Fw::TimeInterval(0, 1000)); // Delay to allow log to be processed
this->reboot(); // Reboot the system
Fw::Logger::log("FATAL %" PRI_FwEventIdType " handled.\n", Id);
Os::Task::delay(Fw::TimeInterval(1, 0)); // Delay to allow log to be processed
this->reboot(); // Reboot the system
}

} // namespace Components
37 changes: 37 additions & 0 deletions FprimeZephyrReference/Components/FlashWorker/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
####
# F Prime CMakeLists.txt:
#
# SOURCES: list of source files (to be compiled)
# AUTOCODER_INPUTS: list of files to be passed to the autocoders
# DEPENDS: list of libraries that this module depends on
#
# More information in the F´ CMake API documentation:
# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/
#
####
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/UpdateStatus/")

# Module names are derived from the path from the nearest project/library/framework
# root when not specifically overridden by the developer. i.e. The module defined by
# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`.

register_fprime_library(
AUTOCODER_INPUTS
"${CMAKE_CURRENT_LIST_DIR}/FlashWorker.fpp"
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/FlashWorker.cpp"
# DEPENDS
# MyPackage_MyOtherModule
)

### Unit Tests ###
# register_fprime_ut(
# AUTOCODER_INPUTS
# "${CMAKE_CURRENT_LIST_DIR}/FlashWorker.fpp"
# SOURCES
# "${CMAKE_CURRENT_LIST_DIR}/test/ut/FlashWorkerTestMain.cpp"
# "${CMAKE_CURRENT_LIST_DIR}/test/ut/FlashWorkerTester.cpp"
# DEPENDS
# STest # For rules-based testing
# UT_AUTO_HELPERS
# )
114 changes: 114 additions & 0 deletions FprimeZephyrReference/Components/FlashWorker/FlashWorker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// ======================================================================
// \title FlashWorker.cpp
// \author starchmd
// \brief cpp file for FlashWorker component implementation class
// ======================================================================

#include "FprimeZephyrReference/Components/FlashWorker/FlashWorker.hpp"

#include "Os/File.hpp"
#include <zephyr/dfu/flash_img.h>
#include <zephyr/dfu/mcuboot.h>

namespace Components {

// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------

FlashWorker ::FlashWorker(const char* const compName) : FlashWorkerComponentBase(compName), m_last_successful(IDLE) {}

FlashWorker ::~FlashWorker() {}

// ----------------------------------------------------------------------
// Flash helpers
// ----------------------------------------------------------------------

Update::UpdateStatus FlashWorker ::writeImage(const Fw::StringBase& file_name, Os::File& file) {
const FwSizeType CHUNK = static_cast<FwSizeType>(sizeof(this->m_data));
FW_ASSERT(file.isOpen());
FwSizeType size = 0;
Update::UpdateStatus return_status = Update::UpdateStatus::OP_OK;
int status = flash_img_init_id(&this->m_flash_context, FlashWorker::REGION_NUMBER);
// Read file size, and default to 0 if unavailable
Os::File::Status file_status = file.size(size);
// Loop through file chunk by chunk
for (FwSizeType i = 0; i < size && status == 0 && file_status == Os::File::Status::OP_OK; i += CHUNK) {
FwSizeType read_size = CHUNK;
file_status = file.read(this->m_data, read_size);
if (file_status != Os::File::Status::OP_OK) {
break;
}
status = flash_img_buffered_write(&this->m_flash_context, this->m_data, read_size, true);
if (status != 0) {
break;
}
}
if (file_status != Os::File::Status::OP_OK) {
this->log_WARNING_LO_ImageFileReadError(file_name, Os::FileStatus(static_cast<Os::FileStatus::T>(file_status)));
}
if (status != 0) {
// TODO image op error
}
return return_status;
}

// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------

Update::UpdateStatus FlashWorker ::confirmImage_handler(FwIndexType portNum) {
int status = boot_write_img_confirmed();
if (status != 0) {
this->log_WARNING_LO_ConfirmImageFailed(static_cast<I32>(-1 * status));
return Update::UpdateStatus::NEXT_BOOT_ERROR;
}
return Update::UpdateStatus::OP_OK;
}

Update::UpdateStatus FlashWorker ::nextBoot_handler(FwIndexType portNum, const Update::NextBootMode& mode) {
int permanent = (mode == Update::NextBootMode::PERMANENT) ? BOOT_UPGRADE_PERMANENT : BOOT_UPGRADE_TEST;

int status = boot_request_upgrade(permanent);
if (status != 0) {
this->log_WARNING_LO_NextBootSetFailed(mode, static_cast<I32>(-1 * status));
return Update::UpdateStatus::NEXT_BOOT_ERROR;
}
return Update::UpdateStatus::OP_OK;
}

void FlashWorker ::prepareImage_handler(FwIndexType portNum) {
Update::UpdateStatus return_status = Update::UpdateStatus::OP_OK;
int status = boot_erase_img_bank(FlashWorker::REGION_NUMBER);
if (status != 0) {
this->log_WARNING_LO_FlashEraseFailed(static_cast<I32>(-1 * status));
return_status = Update::UpdateStatus::PREPARATION_ERROR;
} else {
this->m_last_successful = PREPARE;
}
this->prepareImageDone_out(0, return_status);
}

void FlashWorker ::updateImage_handler(FwIndexType portNum, const Fw::StringBase& file) {
Os::File image_file;
Update::UpdateStatus return_status = Update::UpdateStatus::OP_OK;

if (this->m_last_successful != PREPARE) {
return_status = Update::UpdateStatus::UNPREPARED;
this->m_last_successful = IDLE;
this->log_WARNING_LO_NoImagePrepared();
} else {
Os::File::Status file_status = image_file.open(file.toChar(), Os::File::Mode::OPEN_READ);
if (file_status == Os::File::Status::OP_OK) {
return_status = this->writeImage(file, image_file);
this->m_last_successful = UPDATE;
} else {
return_status = Update::UpdateStatus::IMAGE_FILE_READ_ERROR;
this->m_last_successful = IDLE;
this->log_WARNING_LO_ImageFileReadError(file, static_cast<Os::FileStatus::T>(file_status));
}
}
this->updateImageDone_out(0, return_status);
}

} // namespace Components
34 changes: 34 additions & 0 deletions FprimeZephyrReference/Components/FlashWorker/FlashWorker.fpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module Components {
@ Performs long-running operations for the flash subsystem
active component FlashWorker {
import Update.UpdateWorker

event NoImagePrepared() severity warning low \
format "No image has been prepared for update"

event NextBootSetFailed(mode: Update.NextBootMode, error_number: I32) severity warning low \
format "Set next boot mode to {} failed (errno: {})"

event ConfirmImageFailed(error_number: I32) severity warning low \
format "Confirm image failed (errno: {})"

event FlashEraseFailed(error_number: I32) severity warning low \
format "Flash erase failed (errno: {})"

event ImageFileReadError(file_name: string, error: Os.FileStatus) severity warning low \
format "Failed to read {} with error {}"

###############################################################################
# Standard AC Ports: Required for Channels, Events, Commands, and Parameters #
###############################################################################
@ Port for requesting the current time
time get port timeCaller

@ Port for sending textual representation of events
text event port logTextOut

@ Port for sending events to downlink
event port logOut

}
}
65 changes: 65 additions & 0 deletions FprimeZephyrReference/Components/FlashWorker/FlashWorker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ======================================================================
// \title FlashWorker.hpp
// \author starchmd
// \brief hpp file for FlashWorker component implementation class
// ======================================================================

#ifndef Update_FlashWorker_HPP
#define Update_FlashWorker_HPP
#include "FprimeZephyrReference/Components/FlashWorker/FlashWorkerComponentAc.hpp"
#include "Os/File.hpp"
#include <zephyr/dfu/flash_img.h>
namespace Components {

class FlashWorker final : public FlashWorkerComponentBase {
public:
constexpr static U8 REGION_NUMBER = 1; // slot1
enum Step { IDLE, PREPARE, UPDATE };
// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------

//! Construct FlashWorker object
FlashWorker(const char* const compName //!< The component name
);

//! Destroy FlashWorker object
~FlashWorker();

private:
Update::UpdateStatus writeImage(const Fw::StringBase& file_name, Os::File& image_file);

private:
// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------

//! Handler implementation for confirmImage
//!
//! Confirm that the currently running image is good
Update::UpdateStatus confirmImage_handler(FwIndexType portNum //!< The port number
) override;

//! Handler implementation for nextBoot
//!
//! Set the next boot image and mode
Update::UpdateStatus nextBoot_handler(FwIndexType portNum, //!< The port number
const Update::NextBootMode& mode) override;

//! Handler implementation for prepareImage
void prepareImage_handler(FwIndexType portNum //!< The port number
) override;

//! Handler implementation for updateImage
void updateImage_handler(FwIndexType portNum, //!< The port number
const Fw::StringBase& file) override;

private:
Step m_last_successful;
U8 m_data[CONFIG_IMG_BLOCK_BUF_SIZE];
struct flash_img_context m_flash_context;
};

} // namespace Components

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
register_fprime_library(
AUTOCODER_INPUTS
"${CMAKE_CURRENT_LIST_DIR}/UpdateStatus.fpp"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# UpdateStatus/UpdateStatus.fpp:
#
# File provided by the UpdateWorker component to report status codes back to the Updater component. This is configured
# specifically for the FlashWorker implementation supplied by Proves.
#
# Copyright (c) 2025 Michael Starch
#
# Licensed under the Apache License, Version 2.0. See LICENSE for details.
#
module Update {
@ Status codes used by the Update subsystem.
enum FlashWorkerUpdateStatus {
OP_OK, @< REQUIRED: operation successful
BUSY, @< REQUIRED: operation could not be started because another operation is in-progress
UNPREPARED, @< Preparation step was not completed and not optional
PREPARATION_ERROR, @< An error occurred during the preparation step
IMAGE_FILE_READ_ERROR, @< An error occurred reading the image file
NEXT_BOOT_ERROR @< An error occurred setting the next boot image
}
}
Loading
Loading