diff --git a/.sublime-project b/.sublime-project index 4912c9974..5d751aaf6 100644 --- a/.sublime-project +++ b/.sublime-project @@ -10,7 +10,7 @@ "clangd": { "initializationOptions": { "clangd.compile-commands-dir": "build/latest", - "clangd.header-insertion": null, + "clangd.header-insertion": "never", "clangd.query-driver": "**", "clangd.clang-tidy": true, }, diff --git a/applications/debug/blink_test/blink_test.c b/applications/debug/blink_test/blink_test.c index 7dd2c9e96..638878da2 100644 --- a/applications/debug/blink_test/blink_test.c +++ b/applications/debug/blink_test/blink_test.c @@ -1,4 +1,3 @@ -#include #include #include diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index be7f7f9e6..6993901d2 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -72,7 +72,6 @@ CcidTestApp* ccid_test_app_alloc(void) { //message queue app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent)); - furi_check(app->event_queue); view_port_input_callback_set(app->view_port, ccid_test_app_input_callback, app->event_queue); return app; diff --git a/applications/debug/keypad_test/keypad_test.c b/applications/debug/keypad_test/keypad_test.c index 9e8881def..bc4036120 100644 --- a/applications/debug/keypad_test/keypad_test.c +++ b/applications/debug/keypad_test/keypad_test.c @@ -64,16 +64,9 @@ static void keypad_test_input_callback(InputEvent* input_event, void* ctx) { int32_t keypad_test_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); - furi_check(event_queue); - KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL}; state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!state.mutex) { - FURI_LOG_E(TAG, "cannot create mutex"); - return 0; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, keypad_test_render_callback, &state); diff --git a/applications/debug/text_box_element_test/text_box_element_test.c b/applications/debug/text_box_element_test/text_box_element_test.c index 2b9475d2b..8002f14b6 100644 --- a/applications/debug/text_box_element_test/text_box_element_test.c +++ b/applications/debug/text_box_element_test/text_box_element_test.c @@ -74,16 +74,8 @@ static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { int32_t text_box_element_test_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); - furi_check(event_queue); - TextBoxTestState state = {.idx = 0, .mutex = NULL}; state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - if(!state.mutex) { - FURI_LOG_E(TAG, "Cannot create mutex"); - return 0; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, text_box_test_render_callback, &state); diff --git a/applications/debug/unit_tests/test_runner.c b/applications/debug/unit_tests/test_runner.c index 6af807086..517a4fd24 100644 --- a/applications/debug/unit_tests/test_runner.c +++ b/applications/debug/unit_tests/test_runner.c @@ -72,6 +72,9 @@ void test_runner_free(TestRunner* instance) { free(instance); } +#define TEST_RUNNER_TMP_DIR EXT_PATH(".tmp") +#define TEST_RUNNER_TMP_UNIT_TESTS_DIR TEST_RUNNER_TMP_DIR "/unit_tests" + static bool test_runner_run_plugin(TestRunner* instance, const char* path) { furi_assert(instance); @@ -128,6 +131,16 @@ static void test_runner_run_internal(TestRunner* instance) { File* directory = storage_file_alloc(instance->storage); do { + if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_DIR)) { + FURI_LOG_E(TAG, "Cannot create dir %s", TEST_RUNNER_TMP_DIR); + break; + } + + if(!storage_simply_mkdir(instance->storage, TEST_RUNNER_TMP_UNIT_TESTS_DIR)) { + FURI_LOG_E(TAG, "Cannot create dir %s", TEST_RUNNER_TMP_UNIT_TESTS_DIR); + break; + } + if(!storage_dir_open(directory, PLUGINS_PATH)) { FURI_LOG_E(TAG, "Failed to open directory %s", PLUGINS_PATH); break; diff --git a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c index 03a9e5ab0..d26acf577 100644 --- a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c +++ b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c @@ -4,8 +4,8 @@ #include #include "../test.h" // IWYU pragma: keep +#define TEST_DIR_NAME EXT_PATH(".tmp/unit_tests/ff") #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") static const char* test_filetype = "Flipper File test"; static const uint32_t test_version = 666; diff --git a/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c index a6e1b7700..de6964cbe 100644 --- a/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c +++ b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c @@ -298,7 +298,8 @@ MU_TEST(flipper_format_string_test) { MU_TEST(flipper_format_file_test) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - mu_check(flipper_format_file_open_always(flipper_format, EXT_PATH("flipper.fff"))); + mu_check( + flipper_format_file_open_always(flipper_format, EXT_PATH(".tmp/unit_tests/flipper.fff"))); Stream* stream = flipper_format_get_raw_stream(flipper_format); mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version)); diff --git a/applications/debug/unit_tests/tests/furi/furi_event_loop.c b/applications/debug/unit_tests/tests/furi/furi_event_loop.c new file mode 100644 index 000000000..06afc4fd9 --- /dev/null +++ b/applications/debug/unit_tests/tests/furi/furi_event_loop.c @@ -0,0 +1,164 @@ +#include "../test.h" +#include +#include + +#define TAG "TestFuriEventLoop" + +#define EVENT_LOOP_EVENT_COUNT (256u) + +typedef struct { + FuriMessageQueue* mq; + + FuriEventLoop* producer_event_loop; + uint32_t producer_counter; + + FuriEventLoop* consumer_event_loop; + uint32_t consumer_counter; +} TestFuriData; + +bool test_furi_event_loop_producer_mq_callback(FuriMessageQueue* queue, void* context) { + furi_check(context); + + TestFuriData* data = context; + furi_check(data->mq == queue, "Invalid queue"); + + FURI_LOG_I( + TAG, "producer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter); + + // Remove and add should not cause crash + // if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) { + // furi_event_loop_message_queue_remove(data->producer_event_loop, data->mq); + // furi_event_loop_message_queue_add( + // data->producer_event_loop, + // data->mq, + // FuriEventLoopEventOut, + // test_furi_event_loop_producer_mq_callback, + // data); + // } + + if(data->producer_counter == EVENT_LOOP_EVENT_COUNT) { + furi_event_loop_stop(data->producer_event_loop); + return false; + } + + data->producer_counter++; + furi_check( + furi_message_queue_put(data->mq, &data->producer_counter, 0) == FuriStatusOk, + "furi_message_queue_put failed"); + furi_delay_us(furi_hal_random_get() % 1000); + + return true; +} + +int32_t test_furi_event_loop_producer(void* p) { + furi_check(p); + + FURI_LOG_I(TAG, "producer start"); + + TestFuriData* data = p; + + data->producer_event_loop = furi_event_loop_alloc(); + furi_event_loop_message_queue_subscribe( + data->producer_event_loop, + data->mq, + FuriEventLoopEventOut, + test_furi_event_loop_producer_mq_callback, + data); + + furi_event_loop_run(data->producer_event_loop); + + furi_event_loop_message_queue_unsubscribe(data->producer_event_loop, data->mq); + furi_event_loop_free(data->producer_event_loop); + + FURI_LOG_I(TAG, "producer end"); + + return 0; +} + +bool test_furi_event_loop_consumer_mq_callback(FuriMessageQueue* queue, void* context) { + furi_check(context); + + TestFuriData* data = context; + furi_check(data->mq == queue); + + furi_delay_us(furi_hal_random_get() % 1000); + furi_check(furi_message_queue_get(data->mq, &data->consumer_counter, 0) == FuriStatusOk); + + FURI_LOG_I( + TAG, "consumer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter); + + // Remove and add should not cause crash + // if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) { + // furi_event_loop_message_queue_remove(data->consumer_event_loop, data->mq); + // furi_event_loop_message_queue_add( + // data->consumer_event_loop, + // data->mq, + // FuriEventLoopEventIn, + // test_furi_event_loop_producer_mq_callback, + // data); + // } + + if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT) { + furi_event_loop_stop(data->consumer_event_loop); + return false; + } + + return true; +} + +int32_t test_furi_event_loop_consumer(void* p) { + furi_check(p); + + FURI_LOG_I(TAG, "consumer start"); + + TestFuriData* data = p; + + data->consumer_event_loop = furi_event_loop_alloc(); + furi_event_loop_message_queue_subscribe( + data->consumer_event_loop, + data->mq, + FuriEventLoopEventIn, + test_furi_event_loop_consumer_mq_callback, + data); + + furi_event_loop_run(data->consumer_event_loop); + + furi_event_loop_message_queue_unsubscribe(data->consumer_event_loop, data->mq); + furi_event_loop_free(data->consumer_event_loop); + + FURI_LOG_I(TAG, "consumer end"); + + return 0; +} + +void test_furi_event_loop(void) { + TestFuriData data = {}; + + data.mq = furi_message_queue_alloc(16, sizeof(uint32_t)); + + FuriThread* producer_thread = furi_thread_alloc(); + furi_thread_set_name(producer_thread, "producer_thread"); + furi_thread_set_stack_size(producer_thread, 1 * 1024); + furi_thread_set_callback(producer_thread, test_furi_event_loop_producer); + furi_thread_set_context(producer_thread, &data); + furi_thread_start(producer_thread); + + FuriThread* consumer_thread = furi_thread_alloc(); + furi_thread_set_name(consumer_thread, "consumer_thread"); + furi_thread_set_stack_size(consumer_thread, 1 * 1024); + furi_thread_set_callback(consumer_thread, test_furi_event_loop_consumer); + furi_thread_set_context(consumer_thread, &data); + furi_thread_start(consumer_thread); + + // Wait for thread to complete their tasks + furi_thread_join(producer_thread); + furi_thread_join(consumer_thread); + + // The test itself + mu_assert_int_eq(data.producer_counter, data.consumer_counter); + + // Release memory + furi_thread_free(consumer_thread); + furi_thread_free(producer_thread); + furi_message_queue_free(data.mq); +} diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index f08e4aa6b..be579d2b8 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -6,8 +6,8 @@ void test_furi_create_open(void); void test_furi_concurrent_access(void); void test_furi_pubsub(void); - void test_furi_memmgr(void); +void test_furi_event_loop(void); static int foo = 0; @@ -38,15 +38,19 @@ MU_TEST(mu_test_furi_memmgr) { test_furi_memmgr(); } +MU_TEST(mu_test_furi_event_loop) { + test_furi_event_loop(); +} + MU_TEST_SUITE(test_suite) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); - MU_RUN_TEST(test_check); // v2 tests MU_RUN_TEST(mu_test_furi_create_open); MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); + MU_RUN_TEST(mu_test_furi_event_loop); } int run_minunit_test_furi(void) { diff --git a/applications/debug/unit_tests/tests/rpc/rpc_test.c b/applications/debug/unit_tests/tests/rpc/rpc_test.c index 5bc2cc1f8..f5b9e762d 100644 --- a/applications/debug/unit_tests/tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/tests/rpc/rpc_test.c @@ -46,8 +46,8 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000 #define MAX_NAME_LENGTH 255 #define MAX_DATA_SIZE 512u // have to be exact as in rpc_storage.c +#define TEST_DIR_NAME EXT_PATH(".tmp/unit_tests/rpc") #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") #define MD5SUM_SIZE 16 #define PING_REQUEST 0 diff --git a/applications/debug/unit_tests/tests/stream/stream_test.c b/applications/debug/unit_tests/tests/stream/stream_test.c index ad41c08ec..4f1ba2fd7 100644 --- a/applications/debug/unit_tests/tests/stream/stream_test.c +++ b/applications/debug/unit_tests/tests/stream/stream_test.c @@ -15,6 +15,8 @@ static const char* stream_test_left_data = "There are two cardinal human sins "; static const char* stream_test_right_data = "from which all others derive: impatience and indolence."; +#define FILESTREAM_PATH EXT_PATH(".tmp/unit_tests/filestream.str") + MU_TEST_1(stream_composite_subtest, Stream* stream) { const size_t data_size = 128; uint8_t data[data_size]; @@ -304,15 +306,14 @@ MU_TEST(stream_composite_test) { // test file stream Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check( - file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); // test buffered file stream stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); furi_record_close(RECORD_STORAGE); @@ -346,7 +347,7 @@ MU_TEST(stream_write_read_save_load_test) { mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart)); mu_assert_int_eq( strlen(stream_test_data), - stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS)); + stream_save_to_file(stream_orig, storage, FILESTREAM_PATH, FSOM_CREATE_ALWAYS)); stream_free(stream_copy); stream_free(stream_orig); @@ -354,8 +355,7 @@ MU_TEST(stream_write_read_save_load_test) { // load from file, read Stream* stream_new = string_stream_alloc(); mu_assert_int_eq( - strlen(stream_test_data), - stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str"))); + strlen(stream_test_data), stream_load_from_file(stream_new, storage, FILESTREAM_PATH)); MU_RUN_TEST_1(stream_read_subtest, stream_new); stream_free(stream_new); @@ -396,15 +396,14 @@ MU_TEST(stream_split_test) { // test file stream Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check( - file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check(file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); // test buffered stream stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); @@ -424,8 +423,8 @@ MU_TEST(stream_buffered_write_after_read_test) { Storage* storage = furi_record_open(RECORD_STORAGE); Stream* stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data)); mu_check(stream_rewind(stream)); mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len)); @@ -458,8 +457,8 @@ MU_TEST(stream_buffered_large_file_test) { // write test data to file Stream* stream = buffered_file_stream_alloc(storage); - mu_check(buffered_file_stream_open( - stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + buffered_file_stream_open(stream, FILESTREAM_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(0, stream_size(stream)); mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data)); mu_assert_int_eq(furi_string_size(input_data), stream_size(stream)); diff --git a/applications/debug/unit_tests/unit_test_api_table_i.h b/applications/debug/unit_tests/unit_test_api_table_i.h index e6409f3ac..fc659aea9 100644 --- a/applications/debug/unit_tests/unit_test_api_table_i.h +++ b/applications/debug/unit_tests/unit_test_api_table_i.h @@ -6,11 +6,12 @@ #include #include +#include static constexpr auto unit_tests_api_table = sort(create_array_t( API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)), API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)), - API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)), + API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char*)), API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)), API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)), API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)), @@ -26,4 +27,17 @@ static constexpr auto unit_tests_api_table = sort(create_array_t( xQueueGenericSend, BaseType_t, (QueueHandle_t, const void* const, TickType_t, const BaseType_t)), + API_METHOD(furi_event_loop_alloc, FuriEventLoop*, (void)), + API_METHOD(furi_event_loop_free, void, (FuriEventLoop*)), + API_METHOD( + furi_event_loop_message_queue_subscribe, + void, + (FuriEventLoop*, + FuriMessageQueue*, + FuriEventLoopEvent, + FuriEventLoopMessageQueueCallback, + void*)), + API_METHOD(furi_event_loop_message_queue_unsubscribe, void, (FuriEventLoop*, FuriMessageQueue*)), + API_METHOD(furi_event_loop_run, void, (FuriEventLoop*)), + API_METHOD(furi_event_loop_stop, void, (FuriEventLoop*)), API_VARIABLE(PB_Main_msg, PB_Main_msg_t))); diff --git a/applications/debug/usb_mouse/usb_mouse.c b/applications/debug/usb_mouse/usb_mouse.c index 6e716e69a..2b7710451 100644 --- a/applications/debug/usb_mouse/usb_mouse.c +++ b/applications/debug/usb_mouse/usb_mouse.c @@ -40,7 +40,6 @@ static void usb_mouse_input_callback(InputEvent* input_event, void* ctx) { int32_t usb_mouse_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); - furi_check(event_queue); ViewPort* view_port = view_port_alloc(); FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c index 1a019cf59..9410c8537 100644 --- a/applications/main/nfc/helpers/mf_user_dict.c +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -17,7 +17,6 @@ MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); size_t dict_keys_num = keys_dict_get_total_keys(dict); instance->keys_num = MIN(max_keys_to_load, dict_keys_num); @@ -69,7 +68,6 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); bool key_delete_success = keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index d6abba3b3..eebed2a8d 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -21,7 +21,7 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) { nfc_render_mf_plus_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( - instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 44f9963af..eaa054149 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -16,19 +16,15 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { uint32_t flipper_dict_keys_total = 0; KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey)); - if(dict) { - flipper_dict_keys_total = keys_dict_get_total_keys(dict); - keys_dict_free(dict); - } + flipper_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); // Load user dict keys total uint32_t user_dict_keys_total = 0; dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - if(dict) { - user_dict_keys_total = keys_dict_get_total_keys(dict); - keys_dict_free(dict); - } + user_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); FuriString* temp_str = furi_string_alloc(); widget_add_string_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 4111cca81..a963f44ac 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -31,7 +31,6 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve // Add key to dict KeysDict* dict = keys_dict_alloc( NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); - furi_assert(dict); MfClassicKey key = {}; memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 598db5146..43bb25ab8 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -343,7 +343,6 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); - furi_check(instance->stream); SubGhzEnvironment* environment = subghz_cli_environment_init(); @@ -424,7 +423,6 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); - furi_check(instance->stream); // Configure radio furi_hal_subghz_reset(); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 7ba704219..709d69768 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -19,7 +19,6 @@ Cli* cli_alloc(void) { cli->session = NULL; cli->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(cli->mutex); cli->idle_sem = furi_semaphore_alloc(1, 0); diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 28c17ec16..4a75241e6 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -99,8 +99,8 @@ void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) { void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) { furi_assert(dolphin); furi_assert(event); + event->flag = furi_event_flag_alloc(); - furi_check(event->flag); furi_check( furi_message_queue_put(dolphin->event_queue, event, FuriWaitForever) == FuriStatusOk); furi_check( diff --git a/applications/services/gui/modules/popup.c b/applications/services/gui/modules/popup.c index 45014f6f4..5c9c75e4a 100644 --- a/applications/services/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -112,7 +112,6 @@ Popup* popup_alloc(void) { Popup* popup = malloc(sizeof(Popup)); popup->view = view_alloc(); popup->timer = furi_timer_alloc(popup_timer_callback, FuriTimerTypeOnce, popup); - furi_assert(popup->timer); popup->timer_period_in_ms = 1000; popup->timer_enabled = false; diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index a35c2fa38..51d023543 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -78,7 +78,6 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) { } else if(view->model_type == ViewModelTypeLocking) { ViewModelLocking* model = malloc(sizeof(ViewModelLocking)); model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); - furi_check(model->mutex); model->data = malloc(size); view->model = model; } else { diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index d4c2f61e7..0d0437736 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -29,8 +29,18 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { // Free ViewPort view_port_free(view_dispatcher->view_port); // Free internal queue - if(view_dispatcher->queue) { - furi_message_queue_free(view_dispatcher->queue); + if(view_dispatcher->input_queue) { + furi_event_loop_message_queue_unsubscribe( + view_dispatcher->event_loop, view_dispatcher->input_queue); + furi_message_queue_free(view_dispatcher->input_queue); + } + if(view_dispatcher->event_queue) { + furi_event_loop_message_queue_unsubscribe( + view_dispatcher->event_loop, view_dispatcher->event_queue); + furi_message_queue_free(view_dispatcher->event_queue); + } + if(view_dispatcher->event_loop) { + furi_event_loop_free(view_dispatcher->event_loop); } // Free dispatcher free(view_dispatcher); @@ -38,8 +48,25 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue == NULL); - view_dispatcher->queue = furi_message_queue_alloc(16, sizeof(ViewDispatcherMessage)); + furi_check(view_dispatcher->event_loop == NULL); + + view_dispatcher->event_loop = furi_event_loop_alloc(); + + view_dispatcher->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + furi_event_loop_message_queue_subscribe( + view_dispatcher->event_loop, + view_dispatcher->input_queue, + FuriEventLoopEventIn, + view_dispatcher_run_input_callback, + view_dispatcher); + + view_dispatcher->event_queue = furi_message_queue_alloc(8, sizeof(uint32_t)); + furi_event_loop_message_queue_subscribe( + view_dispatcher->event_loop, + view_dispatcher->event_queue, + FuriEventLoopEventIn, + view_dispatcher_run_event_callback, + view_dispatcher); } void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) { @@ -70,48 +97,45 @@ void view_dispatcher_set_tick_event_callback( view_dispatcher->tick_period = tick_period; } +FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) { + furi_check(view_dispatcher); + furi_check(view_dispatcher->event_loop); + + return view_dispatcher->event_loop; +} + void view_dispatcher_run(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); + furi_check(view_dispatcher->event_loop); uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever : view_dispatcher->tick_period; - ViewDispatcherMessage message; - while(1) { - if(furi_message_queue_get(view_dispatcher->queue, &message, tick_period) != FuriStatusOk) { - view_dispatcher_handle_tick_event(view_dispatcher); - continue; - } - if(message.type == ViewDispatcherMessageTypeStop) { - break; - } else if(message.type == ViewDispatcherMessageTypeInput) { - view_dispatcher_handle_input(view_dispatcher, &message.input); - } else if(message.type == ViewDispatcherMessageTypeCustomEvent) { - view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event); - } - } + + furi_event_loop_tick_set( + view_dispatcher->event_loop, + tick_period, + view_dispatcher_handle_tick_event, + view_dispatcher); + + furi_event_loop_run(view_dispatcher->event_loop); // Wait till all input events delivered + InputEvent input; while(view_dispatcher->ongoing_input) { - furi_message_queue_get(view_dispatcher->queue, &message, FuriWaitForever); - if(message.type == ViewDispatcherMessageTypeInput) { - uint8_t key_bit = (1 << message.input.key); - if(message.input.type == InputTypePress) { - view_dispatcher->ongoing_input |= key_bit; - } else if(message.input.type == InputTypeRelease) { - view_dispatcher->ongoing_input &= ~key_bit; - } + furi_message_queue_get(view_dispatcher->input_queue, &input, FuriWaitForever); + uint8_t key_bit = (1 << input.key); + if(input.type == InputTypePress) { + view_dispatcher->ongoing_input |= key_bit; + } else if(input.type == InputTypeRelease) { + view_dispatcher->ongoing_input &= ~key_bit; } } } void view_dispatcher_stop(ViewDispatcher* view_dispatcher) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeStop; - furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); + furi_check(view_dispatcher->event_loop); + furi_event_loop_stop(view_dispatcher->event_loop); } void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) { @@ -218,12 +242,9 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context) { void view_dispatcher_input_callback(InputEvent* event, void* context) { ViewDispatcher* view_dispatcher = context; - if(view_dispatcher->queue) { - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeInput; - message.input = *event; + if(view_dispatcher->input_queue) { furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == + furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) == FuriStatusOk); } else { view_dispatcher_handle_input(view_dispatcher, event); @@ -287,7 +308,8 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } } -void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) { +void view_dispatcher_handle_tick_event(void* context) { + ViewDispatcher* view_dispatcher = context; if(view_dispatcher->tick_event_callback) { view_dispatcher->tick_event_callback(view_dispatcher->event_context); } @@ -306,14 +328,11 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32 void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) { furi_check(view_dispatcher); - furi_check(view_dispatcher->queue); - - ViewDispatcherMessage message; - message.type = ViewDispatcherMessageTypeCustomEvent; - message.custom_event = event; + furi_check(view_dispatcher->event_loop); furi_check( - furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); + furi_message_queue_put(view_dispatcher->event_queue, &event, FuriWaitForever) == + FuriStatusOk); } static const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = { @@ -345,7 +364,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie view_port_update(view_dispatcher->view_port); } else { view_port_enabled_set(view_dispatcher->view_port, false); - if(view_dispatcher->queue) { + if(view_dispatcher->event_loop) { view_dispatcher_stop(view_dispatcher); } } @@ -361,3 +380,27 @@ void view_dispatcher_update(View* view, void* context) { view_port_update(view_dispatcher->view_port); } } + +bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context) { + furi_assert(context); + ViewDispatcher* instance = context; + furi_assert(instance->event_queue == queue); + + uint32_t event; + furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk); + view_dispatcher_handle_custom_event(instance, event); + + return true; +} + +bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context) { + furi_assert(context); + ViewDispatcher* instance = context; + furi_assert(instance->input_queue == queue); + + InputEvent input; + furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk); + view_dispatcher_handle_input(instance, &input); + + return true; +} diff --git a/applications/services/gui/view_dispatcher.h b/applications/services/gui/view_dispatcher.h index f8567ea1a..7627e5a0b 100644 --- a/applications/services/gui/view_dispatcher.h +++ b/applications/services/gui/view_dispatcher.h @@ -47,8 +47,8 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher); /** Enable queue support * - * If queue enabled all input and custom events will be dispatched throw - * internal queue + * Allocates event_loop, input and event message queues. Must be used with + * `view_dispatcher_run` * * @param view_dispatcher ViewDispatcher instance */ @@ -101,6 +101,20 @@ void view_dispatcher_set_tick_event_callback( */ void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context); +/** Get event_loop instance + * + * event_loop instance is allocated on `view_dispatcher_enable_queue` and used + * in view_dispatcher_run. + * + * You can add your objects into event_loop instance, but don't run the loop on + * your side it will cause issues with input processing on dispatcher stop. + * + * @param view_dispatcher ViewDispatcher instance + * + * @return The event_loop instance. + */ +FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher); + /** Run ViewDispatcher * * Use only after queue enabled diff --git a/applications/services/gui/view_dispatcher_i.h b/applications/services/gui/view_dispatcher_i.h index f30a84e6b..fcf426c31 100644 --- a/applications/services/gui/view_dispatcher_i.h +++ b/applications/services/gui/view_dispatcher_i.h @@ -5,7 +5,6 @@ #pragma once -#include #include #include "view_dispatcher.h" @@ -15,7 +14,10 @@ DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) struct ViewDispatcher { - FuriMessageQueue* queue; + FuriEventLoop* event_loop; + FuriMessageQueue* input_queue; + FuriMessageQueue* event_queue; + Gui* gui; ViewPort* view_port; ViewDict_t views; @@ -32,20 +34,6 @@ struct ViewDispatcher { void* event_context; }; -typedef enum { - ViewDispatcherMessageTypeInput, - ViewDispatcherMessageTypeCustomEvent, - ViewDispatcherMessageTypeStop, -} ViewDispatcherMessageType; - -typedef struct { - ViewDispatcherMessageType type; - union { - InputEvent input; - uint32_t custom_event; - }; -} ViewDispatcherMessage; - /** ViewPort Draw Callback */ void view_dispatcher_draw_callback(Canvas* canvas, void* context); @@ -56,7 +44,7 @@ void view_dispatcher_input_callback(InputEvent* event, void* context); void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); /** Tick handler */ -void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); +void view_dispatcher_handle_tick_event(void* context); /** Custom event handler */ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); @@ -66,3 +54,9 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie /** ViewDispatcher update event */ void view_dispatcher_update(View* view, void* context); + +/** ViewDispatcher run event loop event callback */ +bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context); + +/** ViewDispatcher run event loop input callback */ +bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context); diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 0f3008a36..aae53cd2a 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -90,8 +90,6 @@ static void render_callback(Canvas* canvas, void* ctx) { int32_t passport_app(void* p) { UNUSED(p); FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); - furi_assert(semaphore); - ViewPort* view_port = view_port_alloc(); Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); diff --git a/applications/system/snake_game/snake_game.c b/applications/system/snake_game/snake_game.c index d59eaf26f..567af8647 100644 --- a/applications/system/snake_game/snake_game.c +++ b/applications/system/snake_game/snake_game.c @@ -324,13 +324,6 @@ int32_t snake_game_app(void* p) { snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!snake_state->mutex) { - FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(snake_state); - return 255; - } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state); view_port_input_callback_set(view_port, snake_game_input_callback, event_queue); diff --git a/furi/core/base.h b/furi/core/base.h index 642ff2b6c..92a52a797 100644 --- a/furi/core/base.h +++ b/furi/core/base.h @@ -2,6 +2,7 @@ #include #include +#include #include #ifdef __cplusplus diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index beb9f6519..0f6230c19 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -67,6 +67,14 @@ void __furi_critical_exit(__FuriCriticalInfo info); #define FURI_CHECK_RETURN __attribute__((__warn_unused_result__)) #endif +#ifndef FURI_NAKED +#define FURI_NAKED __attribute__((naked)) +#endif + +#ifndef FURI_DEFAULT +#define FURI_DEFAULT(x) __attribute__((weak, alias(x))) +#endif + #ifdef __cplusplus } #endif diff --git a/furi/core/event_loop.c b/furi/core/event_loop.c new file mode 100644 index 000000000..f22834692 --- /dev/null +++ b/furi/core/event_loop.c @@ -0,0 +1,352 @@ +#include "event_loop_i.h" +#include "message_queue_i.h" + +#include "check.h" +#include "thread.h" + +#include +#include + +#include +#include + +struct FuriEventLoopItem { + // Source + FuriEventLoop* owner; + + // Tracking item + const FuriEventLoopContract* contract; + void* object; + FuriEventLoopEvent event; + + // Callback and context + FuriEventLoopMessageQueueCallback callback; + void* callback_context; + + // Waiting list + ILIST_INTERFACE(WaitingList, struct FuriEventLoopItem); +}; + +ILIST_DEF(WaitingList, FuriEventLoopItem, M_POD_OPLIST) + +static FuriEventLoopItem* furi_event_loop_item_alloc( + FuriEventLoop* owner, + const FuriEventLoopContract* contract, + void* object, + FuriEventLoopEvent event); + +static void furi_event_loop_item_free(FuriEventLoopItem* instance); + +static void furi_event_loop_item_set_callback( + FuriEventLoopItem* instance, + FuriEventLoopMessageQueueCallback callback, + void* callback_context); + +static void furi_event_loop_item_notify(FuriEventLoopItem* instance); + +/* Event Loop RB tree */ +#define FURI_EVENT_LOOP_TREE_RANK (4) + +BPTREE_DEF2( // NOLINT + FuriEventLoopTree, + FURI_EVENT_LOOP_TREE_RANK, + void*, /* pointer to object we track */ + M_PTR_OPLIST, + FuriEventLoopItem*, /* pointer to the FuriEventLoopItem */ + M_PTR_OPLIST) + +#define M_OPL_FuriEventLoopTree_t() BPTREE_OPLIST(FuriEventLoopTree, M_POD_OPLIST) + +#define FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX (2) + +typedef enum { + FuriEventLoopFlagEvent = (1 << 0), + FuriEventLoopFlagStop = (1 << 1), +} FuriEventLoopFlag; + +#define FuriEventLoopFlagAll (FuriEventLoopFlagEvent | FuriEventLoopFlagStop) + +typedef enum { + FuriEventLoopProcessStatusComplete, + FuriEventLoopProcessStatusIncomplete, + FuriEventLoopProcessStatusAgain, +} FuriEventLoopProcessStatus; + +typedef enum { + FuriEventLoopStateIdle, + FuriEventLoopStateProcessing, +} FuriEventLoopState; + +struct FuriEventLoop { + // Only works if all operations are done from the same thread + FuriThreadId thread_id; + + // Poller state + volatile FuriEventLoopState state; + + // Tree + FuriEventLoopTree_t tree; + // Tree waiting list + WaitingList_t waiting_list; + + // Tick event + uint32_t tick_interval; + FuriEventLoopTickCallback tick_callback; + void* tick_callback_context; +}; + +FuriEventLoop* furi_event_loop_alloc(void) { + FuriEventLoop* instance = malloc(sizeof(FuriEventLoop)); + + instance->thread_id = furi_thread_get_current_id(); + FuriEventLoopTree_init(instance->tree); + WaitingList_init(instance->waiting_list); + + return instance; +} + +void furi_event_loop_free(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + FuriEventLoopTree_clear(instance->tree); + free(instance); +} + +static FuriEventLoopProcessStatus + furi_event_loop_poll_process_event(FuriEventLoop* instance, FuriEventLoopItem* item) { + UNUSED(instance); + + if(!item->contract->get_level(item->object, item->event)) { + return FuriEventLoopProcessStatusComplete; + } + + if(item->callback(item->object, item->callback_context)) { + return FuriEventLoopProcessStatusIncomplete; + } else { + return FuriEventLoopProcessStatusAgain; + } +} + +void furi_event_loop_run(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + uint32_t timeout = instance->tick_callback ? instance->tick_interval : FuriWaitForever; + + while(true) { + uint32_t flags = 0; + BaseType_t ret = xTaskNotifyWaitIndexed( + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, timeout); + + instance->state = FuriEventLoopStateProcessing; + if(ret == pdTRUE) { + if(flags & FuriEventLoopFlagStop) { + instance->state = FuriEventLoopStateIdle; + break; + } else if(flags & FuriEventLoopFlagEvent) { + FuriEventLoopItem* item = NULL; + FURI_CRITICAL_ENTER(); + if(!WaitingList_empty_p(instance->waiting_list)) { + item = WaitingList_pop_front(instance->waiting_list); + WaitingList_init_field(item); + } + FURI_CRITICAL_EXIT(); + if(item) { + while(true) { + FuriEventLoopProcessStatus ret = + furi_event_loop_poll_process_event(instance, item); + if(ret == FuriEventLoopProcessStatusComplete) { + // Event processing complete, break from loop + break; + } else if(ret == FuriEventLoopProcessStatusIncomplete) { + // Event processing incomplete more processing needed + } else if(ret == FuriEventLoopProcessStatusAgain) { //-V547 + furi_event_loop_item_notify(item); + break; + } else { + furi_crash(); + } + } + } + } + } else { + if(instance->tick_callback) { + instance->tick_callback(instance->tick_callback_context); + } + } + instance->state = FuriEventLoopStateIdle; + } +} + +void furi_event_loop_stop(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + xTaskNotifyIndexed( + instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagStop, eSetBits); +} + +void furi_event_loop_tick_set( + FuriEventLoop* instance, + uint32_t interval, + FuriEventLoopTickCallback callback, + void* context) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + furi_check(callback ? interval > 0 : true); + + instance->tick_interval = interval; + instance->tick_callback = callback; + instance->tick_callback_context = context; +} + +void furi_event_loop_message_queue_subscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue, + FuriEventLoopEvent event, + FuriEventLoopMessageQueueCallback callback, + void* context) { + furi_check(instance); + furi_check(instance->thread_id == furi_thread_get_current_id()); + furi_check(instance->state == FuriEventLoopStateIdle); + furi_check(message_queue); + + FURI_CRITICAL_ENTER(); + + furi_check(FuriEventLoopTree_get(instance->tree, message_queue) == NULL); + + // Allocate and setup item + FuriEventLoopItem* item = furi_event_loop_item_alloc( + instance, &furi_message_queue_event_loop_contract, message_queue, event); + furi_event_loop_item_set_callback(item, callback, context); + + FuriEventLoopTree_set_at(instance->tree, message_queue, item); + + FuriEventLoopLink* link = item->contract->get_link(message_queue); + + if(item->event == FuriEventLoopEventIn) { + furi_check(link->item_in == NULL); + link->item_in = item; + } else if(item->event == FuriEventLoopEventOut) { + furi_check(link->item_out == NULL); + link->item_out = item; + } else { + furi_crash(); + } + + if(item->contract->get_level(item->object, item->event)) { + furi_event_loop_item_notify(item); + } + + FURI_CRITICAL_EXIT(); +} + +void furi_event_loop_message_queue_unsubscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue) { + furi_check(instance); + furi_check(instance->state == FuriEventLoopStateIdle); + furi_check(instance->thread_id == furi_thread_get_current_id()); + + FURI_CRITICAL_ENTER(); + + FuriEventLoopItem** item_ptr = FuriEventLoopTree_get(instance->tree, message_queue); + furi_check(item_ptr); + + FuriEventLoopItem* item = *item_ptr; + furi_check(item); + furi_check(item->owner == instance); + + FuriEventLoopLink* link = item->contract->get_link(message_queue); + + if(item->event == FuriEventLoopEventIn) { + furi_check(link->item_in == item); + link->item_in = NULL; + } else if(item->event == FuriEventLoopEventOut) { + furi_check(link->item_out == item); + link->item_out = NULL; + } else { + furi_crash(); + } + + furi_event_loop_item_free(item); + + FuriEventLoopTree_erase(instance->tree, message_queue); + + FURI_CRITICAL_EXIT(); +} + +/* + * Event Loop Item API, used internally + */ + +static FuriEventLoopItem* furi_event_loop_item_alloc( + FuriEventLoop* owner, + const FuriEventLoopContract* contract, + void* object, + FuriEventLoopEvent event) { + furi_assert(owner); + furi_assert(object); + + FuriEventLoopItem* instance = malloc(sizeof(FuriEventLoopItem)); + + instance->owner = owner; + instance->contract = contract; + instance->object = object; + instance->event = event; + + WaitingList_init_field(instance); + + return instance; +} + +static void furi_event_loop_item_free(FuriEventLoopItem* instance) { + furi_assert(instance); + free(instance); +} + +static void furi_event_loop_item_set_callback( + FuriEventLoopItem* instance, + FuriEventLoopMessageQueueCallback callback, + void* callback_context) { + furi_assert(instance); + furi_assert(!instance->callback); + + instance->callback = callback; + instance->callback_context = callback_context; +} + +static void furi_event_loop_item_notify(FuriEventLoopItem* instance) { + furi_assert(instance); + + FURI_CRITICAL_ENTER(); + + if(!instance->WaitingList.prev && !instance->WaitingList.next) { + WaitingList_push_back(instance->owner->waiting_list, instance); + } + + FURI_CRITICAL_EXIT(); + + xTaskNotifyIndexed( + instance->owner->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagEvent, + eSetBits); +} + +void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event) { + furi_assert(instance); + + FURI_CRITICAL_ENTER(); + + if(event == FuriEventLoopEventIn) { + if(instance->item_in) furi_event_loop_item_notify(instance->item_in); + } else if(event == FuriEventLoopEventOut) { + if(instance->item_out) furi_event_loop_item_notify(instance->item_out); + } else { + furi_crash(); + } + + FURI_CRITICAL_EXIT(); +} \ No newline at end of file diff --git a/furi/core/event_loop.h b/furi/core/event_loop.h new file mode 100644 index 000000000..7221a90bc --- /dev/null +++ b/furi/core/event_loop.h @@ -0,0 +1,134 @@ +/** + * @file event_loop.h + * @brief Furi Event Loop + * + * This module is designed to handle application event loop in fully + * asynchronous, reactive nature. On the low level this modules is + * inspired by epoll/kqueue concept, on the high level by asyncio + * event loop. + * + * This module is trying to best fit into Furi OS, so we don't + * provide any compatibility with other event driven APIs. But + * programming concepts are the same, except some runtime + * limitations from our side. + */ +#pragma once + +#include "base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Event Loop events */ +typedef enum { + FuriEventLoopEventOut, /**< On departure: item was retrieved from container, flag reset, etc... */ + FuriEventLoopEventIn, /**< On arrival: item was inserted into container, flag set, etc... */ +} FuriEventLoopEvent; + +/** Anonymous message queue type */ +typedef struct FuriEventLoop FuriEventLoop; + +/** Allocate Event Loop instance + * + * Couple things to keep in mind: + * - You can have 1 event_loop per 1 thread + * - You can not use event_loop instance in the other thread + * - Do not use blocking api to query object delegated to Event Loop + * + * @return The Event Loop instance + */ +FuriEventLoop* furi_event_loop_alloc(void); + +/** Free Event Loop instance + * + * @param instance The Event Loop instance + */ +void furi_event_loop_free(FuriEventLoop* instance); + +/** Continuously poll for events + * + * Can be stopped with `furi_event_loop_stop` + * + * @param instance The Event Loop instance + */ +void furi_event_loop_run(FuriEventLoop* instance); + +/** Stop Event Loop instance + * + * @param instance The Event Loop instance + */ +void furi_event_loop_stop(FuriEventLoop* instance); + +/* + * Tick related API + */ + +/** Tick callback type + * + * @param context The context for callback + */ +typedef void (*FuriEventLoopTickCallback)(void* context); + +/** Set Event Loop tick callback + * + * Tick callback called after specified inactivity time. It's not periodic. If + * Event Loop is busy then ticks will be skipped. + * + * @param instance The Event Loop instance + * @param[in] interval The tick interval + * @param[in] callback The callback to call + * @param context The context for callback + */ +void furi_event_loop_tick_set( + FuriEventLoop* instance, + uint32_t interval, + FuriEventLoopTickCallback callback, + void* context); + +/* + * Message queue related APIs + */ + +/** Anonymous message queue type */ +typedef struct FuriMessageQueue FuriMessageQueue; + +/** Callback type for message queue + * + * @param queue The queue that triggered event + * @param context The context that was provided on + * furi_event_loop_message_queue_subscribe call + * + * @return true if event was processed, false if we need to delay processing + */ +typedef bool (*FuriEventLoopMessageQueueCallback)(FuriMessageQueue* queue, void* context); + +/** Subscribe to message queue events + * + * @warning you can only have one subscription for one event type. + * + * @param instance The Event Loop instance + * @param message_queue The message queue to add + * @param[in] event The Event Loop event to trigger on + * @param[in] callback The callback to call on event + * @param context The context for callback + */ +void furi_event_loop_message_queue_subscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue, + FuriEventLoopEvent event, + FuriEventLoopMessageQueueCallback callback, + void* context); + +/** Unsubscribe from message queue + * + * @param instance The Event Loop instance + * @param message_queue The message queue + */ +void furi_event_loop_message_queue_unsubscribe( + FuriEventLoop* instance, + FuriMessageQueue* message_queue); + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/event_loop_i.h b/furi/core/event_loop_i.h new file mode 100644 index 000000000..8ddd10966 --- /dev/null +++ b/furi/core/event_loop_i.h @@ -0,0 +1,33 @@ +#pragma once + +#include "event_loop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FuriEventLoopItem FuriEventLoopItem; + +/* Link between Event Loop */ + +typedef struct { + FuriEventLoopItem* item_in; + FuriEventLoopItem* item_out; +} FuriEventLoopLink; + +void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event); + +/* Contract between event loop and an object */ + +typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(void* object); + +typedef uint32_t (*FuriEventLoopContractGetLevel)(void* object, FuriEventLoopEvent event); + +typedef struct { + const FuriEventLoopContractGetLink get_link; + const FuriEventLoopContractGetLevel get_level; +} FuriEventLoopContract; + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3a0e2c378..3827ddde3 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -63,8 +64,6 @@ task.h is included from an application file. */ #define heapBITS_PER_BYTE ((size_t)8) /* Heap start end symbols provided by linker */ -extern const void __heap_start__; -extern const void __heap_end__; uint8_t* ucHeap = (uint8_t*)&__heap_start__; /* Define the linked list structure. This is used to link free blocks in order diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 0454e289b..cda775abe 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,9 +1,4 @@ -#include "kernel.h" -#include "message_queue.h" -#include "check.h" - -#include -#include +#include "message_queue_i.h" // Internal FreeRTOS member names #define uxMessagesWaiting uxDummy4[0] @@ -12,6 +7,10 @@ struct FuriMessageQueue { StaticQueue_t container; + + // Event Loop Link + FuriEventLoopLink event_loop_link; + uint8_t buffer[]; }; @@ -42,6 +41,10 @@ void furi_message_queue_free(FuriMessageQueue* instance) { furi_check(furi_kernel_is_irq_or_masked() == 0U); furi_check(instance); + // Event Loop must be disconnected + furi_check(!instance->event_loop_link.item_in); + furi_check(!instance->event_loop_link.item_out); + vQueueDelete((QueueHandle_t)instance); free(instance); } @@ -82,6 +85,11 @@ FuriStatus } } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn); + } + + /* Return execution status */ return stat; } @@ -120,6 +128,10 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin } } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); + } + return stat; } @@ -182,5 +194,34 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { (void)xQueueReset(hQueue); } + if(stat == FuriStatusOk) { + furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); + } + + /* Return execution status */ return stat; } + +static FuriEventLoopLink* furi_message_queue_event_loop_get_link(void* object) { + FuriMessageQueue* instance = object; + furi_assert(instance); + return &instance->event_loop_link; +} + +static uint32_t furi_message_queue_event_loop_get_level(void* object, FuriEventLoopEvent event) { + FuriMessageQueue* instance = object; + furi_assert(instance); + + if(event == FuriEventLoopEventIn) { + return furi_message_queue_get_count(instance); + } else if(event == FuriEventLoopEventOut) { + return furi_message_queue_get_space(instance); + } else { + furi_crash(); + } +} + +const FuriEventLoopContract furi_message_queue_event_loop_contract = { + .get_link = furi_message_queue_event_loop_get_link, + .get_level = furi_message_queue_event_loop_get_level, +}; diff --git a/furi/core/message_queue.h b/furi/core/message_queue.h index a055a05f1..4c5b5af3c 100644 --- a/furi/core/message_queue.h +++ b/furi/core/message_queue.h @@ -4,7 +4,7 @@ */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/furi/core/message_queue_i.h b/furi/core/message_queue_i.h new file mode 100644 index 000000000..aa24cfe54 --- /dev/null +++ b/furi/core/message_queue_i.h @@ -0,0 +1,12 @@ +#pragma once + +#include "message_queue.h" + +#include "kernel.h" +#include "event_loop_i.h" +#include "check.h" + +#include +#include + +extern const FuriEventLoopContract furi_message_queue_event_loop_contract; \ No newline at end of file diff --git a/furi/core/pubsub.c b/furi/core/pubsub.c index 0fdd5d02d..7c1730632 100644 --- a/furi/core/pubsub.c +++ b/furi/core/pubsub.c @@ -20,7 +20,6 @@ FuriPubSub* furi_pubsub_alloc(void) { FuriPubSub* pubsub = malloc(sizeof(FuriPubSub)); pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(pubsub->mutex); FuriPubSubSubscriptionList_init(pubsub->items); diff --git a/furi/core/record.c b/furi/core/record.c index 6c44df846..fa384369a 100644 --- a/furi/core/record.c +++ b/furi/core/record.c @@ -39,7 +39,6 @@ static void furi_record_erase(const char* name, FuriRecordData* record_data) { void furi_record_init(void) { furi_record = malloc(sizeof(FuriRecord)); furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(furi_record->mutex); FuriRecordDataDict_init(furi_record->records); } diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index eefda0e79..879520010 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -1,6 +1,6 @@ -#include "base.h" -#include "check.h" #include "stream_buffer.h" + +#include "check.h" #include "common_defines.h" #include diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index 3cc9c1b67..eef8ee510 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -12,8 +12,8 @@ * interrupt that will read from the buffer (the reader). */ #pragma once -#include -#include + +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/furi/core/timer.h b/furi/core/timer.h index 2f55001f7..00056db18 100644 --- a/furi/core/timer.h +++ b/furi/core/timer.h @@ -4,7 +4,7 @@ */ #pragma once -#include "core/base.h" +#include "base.h" #ifdef __cplusplus extern "C" { diff --git a/furi/furi.h b/furi/furi.h index d8aec91c0..24e597acf 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -4,6 +4,7 @@ #include "core/check.h" #include "core/common_defines.h" +#include "core/event_loop.h" #include "core/event_flag.h" #include "core/kernel.h" #include "core/log.h" diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py index b680cb891..a47146ff5 100644 --- a/scripts/fbt_tools/fbt_hwtarget.py +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -76,7 +76,9 @@ class HardwareTargetLoader: self._processTargetDefinitions(inherited_target) def gatherSources(self): - sources = [self.startup_script] + sources = [] + if self.startup_script: + sources.append(self.startup_script) seen_filenames = set(self.excluded_sources) # print("Layers: ", self.layered_target_dirs) for target_dir in self.layered_target_dirs: diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 91999f7a0..d732e991d 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1098,6 +1098,13 @@ Function,+,furi_event_flag_free,void,FuriEventFlag* Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,-,furi_event_loop_alloc,FuriEventLoop*, +Function,-,furi_event_loop_free,void,FuriEventLoop* +Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*" +Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*" +Function,-,furi_event_loop_run,void,FuriEventLoop* +Function,-,furi_event_loop_stop,void,FuriEventLoop* +Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -2696,6 +2703,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 61d4fd43d..aa212f829 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1234,6 +1234,13 @@ Function,+,furi_event_flag_free,void,FuriEventFlag* Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,-,furi_event_loop_alloc,FuriEventLoop*, +Function,-,furi_event_loop_free,void,FuriEventLoop* +Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*" +Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*" +Function,-,furi_event_loop_run,void,FuriEventLoop* +Function,-,furi_event_loop_stop,void,FuriEventLoop* +Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -3603,6 +3610,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f7/application_ext.ld b/targets/f7/application_ext.ld index 01bb021b6..b6496290a 100644 --- a/targets/f7/application_ext.ld +++ b/targets/f7/application_ext.ld @@ -1,54 +1,47 @@ FORCE_COMMON_ALLOCATION -SECTIONS -{ - .text 0x00000000 : ALIGN(4) - { - *(.text) - *(.stub) - *(.text*) - *(.text.*) - *(.text._*) +SECTIONS { + .text 0x00000000 : ALIGN(4) { + *(.text) + *(.stub) + *(.text*) + *(.text.*) + *(.text._*) - KEEP (*(.init)) - KEEP (*(.fini)) - } + KEEP (*(.init)) + KEEP (*(.fini)) + } - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.*) - } + .rodata : { + *(.rodata) + *(.rodata1) + *(.rodata.*) + } - .data : - { - *(.data) - *(.data1) - *(.data.*) - } + .data : { + *(.data) + *(.data1) + *(.data.*) + } - .bss : - { - *(.bss) - *(.bss*) - *(.sbss) - *(.sbss*) - *(COMMON) - } + .bss : { + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + } - .ARM.attributes : - { - *(.ARM.attributes) - *(.ARM.attributes.*) - } + .ARM.attributes : { + *(.ARM.attributes) + *(.ARM.attributes.*) + } - /DISCARD/ : - { - *(.comment) - *(.comment.*) - *(.llvmbc) - *(.llvmcmd) - } + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.llvmbc) + *(.llvmcmd) + } } diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 7e713a08d..7d19cb54a 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -62,10 +62,11 @@ void furi_hal_init(void) { void furi_hal_switch(void* address) { __set_BASEPRI(0); - asm volatile("ldr r3, [%0] \n" - "msr msp, r3 \n" - "ldr r3, [%1] \n" - "mov pc, r3 \n" + // This code emulates system reset: sets MSP and calls Reset ISR + asm volatile("ldr r3, [%0] \n" // Load SP from new vector to r3 + "msr msp, r3 \n" // Set MSP from r3 + "ldr r3, [%1] \n" // Load Reset Handler address to r3 + "mov pc, r3 \n" // Set PC from r3 (jump to Reset ISR) : : "r"(address), "r"(address + 0x4) : "r3"); diff --git a/targets/f7/furi_hal/furi_hal_flash.c b/targets/f7/furi_hal/furi_hal_flash.c index cde9f9a7e..d6b840255 100644 --- a/targets/f7/furi_hal/furi_hal_flash.c +++ b/targets/f7/furi_hal/furi_hal_flash.c @@ -8,6 +8,7 @@ #include #include +#include #include @@ -57,9 +58,6 @@ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ (((__VALUE__) % 8UL) == 0UL)) -/* Free flash space borders, exported by linker */ -extern const void __free_flash_start__; - size_t furi_hal_flash_get_base(void) { return FLASH_BASE; } diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 5c2c315ef..5d6680ac2 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -67,13 +67,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { [FuriHalInterruptIdLpUart1] = LPUART1_IRQn, }; -__attribute__((always_inline)) static inline void - furi_hal_interrupt_call(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_call(FuriHalInterruptId index) { furi_check(furi_hal_interrupt_isr[index].isr); furi_hal_interrupt_isr[index].isr(furi_hal_interrupt_isr[index].context); } -__attribute__((always_inline)) static inline void +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_enable(FuriHalInterruptId index, uint16_t priority) { NVIC_SetPriority( furi_hal_interrupt_irqn[index], @@ -81,23 +80,19 @@ __attribute__((always_inline)) static inline void NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_get_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_get_pending(FuriHalInterruptId index) { NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_set_pending(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_set_pending(FuriHalInterruptId index) { NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]); } -__attribute__((always_inline)) static inline void - furi_hal_interrupt_disable(FuriHalInterruptId index) { +FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_disable(FuriHalInterruptId index) { NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]); } @@ -279,11 +274,11 @@ void MemManage_Handler(void) { // from 0x00 to 1MB, see FuriHalMpuRegionNULL furi_crash("NULL pointer dereference"); } else { - // write or read of MPU region 1 (FuriHalMpuRegionStack) + // write or read of MPU region 1 (FuriHalMpuRegionThreadStack) furi_crash("MPU fault, possibly stack overflow"); } } else if(FURI_BIT(SCB->CFSR, SCB_CFSR_MSTKERR_Pos)) { - // push to stack on MPU region 1 (FuriHalMpuRegionStack) + // push to stack on MPU region 1 (FuriHalMpuRegionThreadStack) furi_crash("MemManage fault, possibly stack overflow"); } @@ -318,9 +313,6 @@ void USB_LP_IRQHandler(void) { #endif } -void USB_HP_IRQHandler(void) { -} - void IPCC_C1_TX_IRQHandler(void) { HW_IPCC_Tx_Handler(); } @@ -347,4 +339,4 @@ void USART1_IRQHandler(void) { void LPUART1_IRQHandler(void) { furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); -} \ No newline at end of file +} diff --git a/targets/f7/furi_hal/furi_hal_mpu.c b/targets/f7/furi_hal/furi_hal_mpu.c index 16724c975..5fe3ab66b 100644 --- a/targets/f7/furi_hal/furi_hal_mpu.c +++ b/targets/f7/furi_hal/furi_hal_mpu.c @@ -1,6 +1,8 @@ #include #include +#include + #define FURI_HAL_MPU_ATTRIBUTES \ (LL_MPU_ACCESS_BUFFERABLE | LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | \ LL_MPU_TEX_LEVEL1 | LL_MPU_INSTRUCTION_ACCESS_ENABLE) @@ -12,6 +14,10 @@ void furi_hal_mpu_init(void) { // NULL pointer dereference protection furi_hal_mpu_protect_no_access(FuriHalMpuRegionNULL, 0x00, FuriHalMPURegionSize1MB); + furi_hal_mpu_protect_no_access( + FuriHalMpuRegionMainStack, + (uint32_t)(&_stack_end - &_stack_size), + FuriHalMPURegionSize32B); } void furi_hal_mpu_enable(void) { @@ -62,5 +68,5 @@ void furi_hal_mpu_set_stack_protection(uint32_t* stack) { if(stack_ptr < (uint32_t)stack) stack_ptr += (mask + 1); furi_hal_mpu_protect_read_only( - FuriHalMpuRegionStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); + FuriHalMpuRegionThreadStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); } \ No newline at end of file diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 0abce558f..e6233f624 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -86,8 +86,12 @@ to exclude the API function. */ #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTimerPendFunctionCall 1 -/* Furi-specific */ -#define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 +/* Workaround for various notification issues: + * - First one used by system primitives + * - Second one by thread event notification + * - Third one by FuriEventLoop + */ +#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3 extern __attribute__((__noreturn__)) void furi_thread_catch(void); #define configTASK_RETURN_ADDRESS (furi_thread_catch + 2) diff --git a/targets/f7/inc/stm32wb55_linker.h b/targets/f7/inc/stm32wb55_linker.h new file mode 100644 index 000000000..4b56a11be --- /dev/null +++ b/targets/f7/inc/stm32wb55_linker.h @@ -0,0 +1,34 @@ +/** + * @file stm32wb55_linker.h + * + * Linker defined symbols. Used in various part of firmware to understand + * hardware boundaries. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern const void _stack_end; /**< end of stack */ +extern const void _stack_size; /**< stack size */ + +extern const void _sidata; /**< data initial value start */ +extern const void _sdata; /**< data start */ +extern const void _edata; /**< data end */ + +extern const void _sbss; /**< bss start */ +extern const void _ebss; /**< bss end */ + +extern const void _sMB_MEM2; /**< RAM2a start */ +extern const void _eMB_MEM2; /**< RAM2a end */ + +extern const void __heap_start__; /**< RAM1 Heap start */ +extern const void __heap_end__; /**< RAM1 Heap end */ + +extern const void __free_flash_start__; /**< Free Flash space start */ + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/inc/stm32wb55_startup.h b/targets/f7/inc/stm32wb55_startup.h new file mode 100644 index 000000000..712388576 --- /dev/null +++ b/targets/f7/inc/stm32wb55_startup.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int main(void); +extern void __libc_init_array(void); + +void Default_Handler(void); + +#define DEFAULT FURI_DEFAULT("Default_Handler") + +/* 15 Unmask-able ISR */ +DEFAULT void NMI_Handler(void); +DEFAULT void HardFault_Handler(void); +DEFAULT void MemManage_Handler(void); +DEFAULT void BusFault_Handler(void); +DEFAULT void UsageFault_Handler(void); +DEFAULT void SVC_Handler(void); +DEFAULT void DebugMon_Handler(void); +DEFAULT void PendSV_Handler(void); +DEFAULT void SysTick_Handler(void); + +/* 63 Mask-able ISR */ +DEFAULT void WWDG_IRQHandler(void); +DEFAULT void PVD_PVM_IRQHandler(void); +DEFAULT void TAMP_STAMP_LSECSS_IRQHandler(void); +DEFAULT void RTC_WKUP_IRQHandler(void); +DEFAULT void FLASH_IRQHandler(void); +DEFAULT void RCC_IRQHandler(void); +DEFAULT void EXTI0_IRQHandler(void); +DEFAULT void EXTI1_IRQHandler(void); +DEFAULT void EXTI2_IRQHandler(void); +DEFAULT void EXTI3_IRQHandler(void); +DEFAULT void EXTI4_IRQHandler(void); +DEFAULT void DMA1_Channel1_IRQHandler(void); +DEFAULT void DMA1_Channel2_IRQHandler(void); +DEFAULT void DMA1_Channel3_IRQHandler(void); +DEFAULT void DMA1_Channel4_IRQHandler(void); +DEFAULT void DMA1_Channel5_IRQHandler(void); +DEFAULT void DMA1_Channel6_IRQHandler(void); +DEFAULT void DMA1_Channel7_IRQHandler(void); +DEFAULT void ADC1_IRQHandler(void); +DEFAULT void USB_HP_IRQHandler(void); +DEFAULT void USB_LP_IRQHandler(void); +DEFAULT void C2SEV_PWR_C2H_IRQHandler(void); +DEFAULT void COMP_IRQHandler(void); +DEFAULT void EXTI9_5_IRQHandler(void); +DEFAULT void TIM1_BRK_IRQHandler(void); +DEFAULT void TIM1_UP_TIM16_IRQHandler(void); +DEFAULT void TIM1_TRG_COM_TIM17_IRQHandler(void); +DEFAULT void TIM1_CC_IRQHandler(void); +DEFAULT void TIM2_IRQHandler(void); +DEFAULT void PKA_IRQHandler(void); +DEFAULT void I2C1_EV_IRQHandler(void); +DEFAULT void I2C1_ER_IRQHandler(void); +DEFAULT void I2C3_EV_IRQHandler(void); +DEFAULT void I2C3_ER_IRQHandler(void); +DEFAULT void SPI1_IRQHandler(void); +DEFAULT void SPI2_IRQHandler(void); +DEFAULT void USART1_IRQHandler(void); +DEFAULT void LPUART1_IRQHandler(void); +DEFAULT void SAI1_IRQHandler(void); +DEFAULT void TSC_IRQHandler(void); +DEFAULT void EXTI15_10_IRQHandler(void); +DEFAULT void RTC_Alarm_IRQHandler(void); +DEFAULT void CRS_IRQHandler(void); +DEFAULT void PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler(void); +DEFAULT void IPCC_C1_RX_IRQHandler(void); +DEFAULT void IPCC_C1_TX_IRQHandler(void); +DEFAULT void HSEM_IRQHandler(void); +DEFAULT void LPTIM1_IRQHandler(void); +DEFAULT void LPTIM2_IRQHandler(void); +DEFAULT void LCD_IRQHandler(void); +DEFAULT void QUADSPI_IRQHandler(void); +DEFAULT void AES1_IRQHandler(void); +DEFAULT void AES2_IRQHandler(void); +DEFAULT void RNG_IRQHandler(void); +DEFAULT void FPU_IRQHandler(void); +DEFAULT void DMA2_Channel1_IRQHandler(void); +DEFAULT void DMA2_Channel2_IRQHandler(void); +DEFAULT void DMA2_Channel3_IRQHandler(void); +DEFAULT void DMA2_Channel4_IRQHandler(void); +DEFAULT void DMA2_Channel5_IRQHandler(void); +DEFAULT void DMA2_Channel6_IRQHandler(void); +DEFAULT void DMA2_Channel7_IRQHandler(void); +DEFAULT void DMAMUX1_OVR_IRQHandler(void); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/src/stm32wb55_startup.c b/targets/f7/src/stm32wb55_startup.c new file mode 100644 index 000000000..1c1cfdcc3 --- /dev/null +++ b/targets/f7/src/stm32wb55_startup.c @@ -0,0 +1,199 @@ +#include +#include +#include + +/** System Core Clock speed + * + * CPU1: M4 on MSI clock after startup (4MHz). + * Modified by RCC LL HAL. + */ +uint32_t SystemCoreClock = 4000000UL; + +/** AHB Prescaler Table. Used by RCC LL HAL */ +const uint32_t AHBPrescTable[16UL] = + {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL}; +/** APB Prescaler Table. Used by RCC LL HAL */ +const uint32_t APBPrescTable[8UL] = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL}; +/** MSI Range Table. Used by RCC LL HAL */ +const uint32_t MSIRangeTable[16UL] = { + 100000UL, + 200000UL, + 400000UL, + 800000UL, + 1000000UL, + 2000000UL, + 4000000UL, + 8000000UL, + 16000000UL, + 24000000UL, + 32000000UL, + 48000000UL, + 0UL, + 0UL, + 0UL, + 0UL}; /* 0UL values are incorrect cases */ + +/** MCU Initialization Routine. Part of ST HAL convention, so we keep it.*/ +void SystemInit(void) { + // Set ISR Vector location +#if defined(VECT_TAB_SRAM) + // Point ISR Vector to SRAM + SCB->VTOR = SRAM1_BASE; +#else + // Point ISR Vector to 0x0, which is mapped to 0x08000000(Flash) + SCB->VTOR = 0x0; +#endif + +#if(__FPU_PRESENT == 1) && (__FPU_USED == 1) + // Enable access to FPU + SCB->CPACR |= + ((3UL << (10UL * 2UL)) | (3UL << (11UL * 2UL))); /* set CP10 and CP11 Full Access */ +#endif + + // Reset the RCC clock configuration to the default reset state + // Set MSION bit + RCC->CR |= RCC_CR_MSION; + // Reset CFGR register + RCC->CFGR = 0x00070000U; + // Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits + RCC->CR &= (uint32_t)0xFAF6FEFBU; + // Reset LSI1 and LSI2 bits + RCC->CSR &= (uint32_t)0xFFFFFFFAU; + // Reset HSI48ON bit + RCC->CRRCR &= (uint32_t)0xFFFFFFFEU; + // Reset PLLCFGR register + RCC->PLLCFGR = 0x22041000U; +#if defined(STM32WB55xx) || defined(STM32WB5Mxx) + // Reset PLLSAI1CFGR register + RCC->PLLSAI1CFGR = 0x22041000U; +#endif + // Reset HSEBYP bit + RCC->CR &= 0xFFFBFFFFU; + // Disable all RCC related interrupts + RCC->CIER = 0x00000000; +} + +void Default_Handler(void) { + furi_crash("NotImplemented"); +} + +/** Start your journey here */ +FURI_NAKED void Reset_Handler(void) { + // Funny thing: SP and MSP are set to _stack_end if we came here after MCU reset + // Now, what if we came from boot loader? Lets set SP to _stack_end again. + // By the way Furi stage loader doing it too, but we don't know who called us. + asm volatile("ldr r0, =_stack_end"); + asm volatile("mov sp, r0"); + + // ST chip initialization routine + SystemInit(); + + // Copy data section from flash + memcpy((void*)&_sdata, &_sidata, &_edata - &_sdata); + + // Wipe BSS + memset((void*)&_sbss, 0x00, &_ebss - &_sbss); + + // Core2 related quirks: wipe MB_MEM2 section + memset((void*)&_sMB_MEM2, 0x00, &_eMB_MEM2 - &_sMB_MEM2); + + // libc init array + __libc_init_array(); + + // Our main + main(); + + // You should never exit from main, but we'll catch you if you do + furi_crash("WhyExit?"); +} + +/** ISR type */ +typedef void (*element_t)(void); + +/** System initialization vector. Contains: main stack end address, 15 pointers + * to unmask-able ISR and 63 to mask-able ISR. */ +PLACE_IN_SECTION(".isr_vector") +const element_t reset_vector[] = { + /* Main stack top */ + (element_t)&_stack_end, + /* 15 Unmaskable ISR */ + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + NULL, + NULL, + NULL, + NULL, + SVC_Handler, + DebugMon_Handler, + NULL, + PendSV_Handler, + SysTick_Handler, + /* 63 Maskable ISR */ + WWDG_IRQHandler, + PVD_PVM_IRQHandler, + TAMP_STAMP_LSECSS_IRQHandler, + RTC_WKUP_IRQHandler, + FLASH_IRQHandler, + RCC_IRQHandler, + EXTI0_IRQHandler, + EXTI1_IRQHandler, + EXTI2_IRQHandler, + EXTI3_IRQHandler, + EXTI4_IRQHandler, + DMA1_Channel1_IRQHandler, + DMA1_Channel2_IRQHandler, + DMA1_Channel3_IRQHandler, + DMA1_Channel4_IRQHandler, + DMA1_Channel5_IRQHandler, + DMA1_Channel6_IRQHandler, + DMA1_Channel7_IRQHandler, + ADC1_IRQHandler, + USB_HP_IRQHandler, + USB_LP_IRQHandler, + C2SEV_PWR_C2H_IRQHandler, + COMP_IRQHandler, + EXTI9_5_IRQHandler, + TIM1_BRK_IRQHandler, + TIM1_UP_TIM16_IRQHandler, + TIM1_TRG_COM_TIM17_IRQHandler, + TIM1_CC_IRQHandler, + TIM2_IRQHandler, + PKA_IRQHandler, + I2C1_EV_IRQHandler, + I2C1_ER_IRQHandler, + I2C3_EV_IRQHandler, + I2C3_ER_IRQHandler, + SPI1_IRQHandler, + SPI2_IRQHandler, + USART1_IRQHandler, + LPUART1_IRQHandler, + SAI1_IRQHandler, + TSC_IRQHandler, + EXTI15_10_IRQHandler, + RTC_Alarm_IRQHandler, + CRS_IRQHandler, + PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler, + IPCC_C1_RX_IRQHandler, + IPCC_C1_TX_IRQHandler, + HSEM_IRQHandler, + LPTIM1_IRQHandler, + LPTIM2_IRQHandler, + LCD_IRQHandler, + QUADSPI_IRQHandler, + AES1_IRQHandler, + AES2_IRQHandler, + RNG_IRQHandler, + FPU_IRQHandler, + DMA2_Channel1_IRQHandler, + DMA2_Channel2_IRQHandler, + DMA2_Channel3_IRQHandler, + DMA2_Channel4_IRQHandler, + DMA2_Channel5_IRQHandler, + DMA2_Channel6_IRQHandler, + DMA2_Channel7_IRQHandler, + DMAMUX1_OVR_IRQHandler, +}; diff --git a/targets/f7/src/system_stm32wbxx.c b/targets/f7/src/system_stm32wbxx.c deleted file mode 100644 index 77430fda8..000000000 --- a/targets/f7/src/system_stm32wbxx.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "stm32wbxx.h" - -/*!< Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */ -/* #define VECT_TAB_SRAM */ - -#ifndef VECT_TAB_OFFSET -#define VECT_TAB_OFFSET \ - 0x0 /*!< Vector Table base offset field. This value must be a multiple of 0x200. */ -#endif - -#define VECT_TAB_BASE_ADDRESS \ - SRAM1_BASE /*!< Vector Table base offset field. This value must be a multiple of 0x200. */ - -/* The SystemCoreClock variable is updated in three ways: - 1) by calling CMSIS function SystemCoreClockUpdate() - 2) by calling HAL API function HAL_RCC_GetHCLKFreq() - 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency - Note: If you use this function to configure the system clock; then there - is no need to call the 2 first functions listed above, since SystemCoreClock - variable is updated automatically. - */ -uint32_t SystemCoreClock = 4000000UL; /*CPU1: M4 on MSI clock after startup (4MHz)*/ - -const uint32_t AHBPrescTable[16UL] = - {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL}; - -const uint32_t APBPrescTable[8UL] = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL}; - -const uint32_t MSIRangeTable[16UL] = { - 100000UL, - 200000UL, - 400000UL, - 800000UL, - 1000000UL, - 2000000UL, - 4000000UL, - 8000000UL, - 16000000UL, - 24000000UL, - 32000000UL, - 48000000UL, - 0UL, - 0UL, - 0UL, - 0UL}; /* 0UL values are incorrect cases */ - -/** - * @brief Setup the microcontroller system. - * @param None - * @retval None - */ -void SystemInit(void) { - /* Configure the Vector Table location add offset address ------------------*/ -#if defined(VECT_TAB_SRAM) && defined(VECT_TAB_BASE_ADDRESS) - /* program in SRAMx */ - SCB->VTOR = VECT_TAB_BASE_ADDRESS | - VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAMx for CPU1 */ -#else /* program in FLASH */ - SCB->VTOR = VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ -#endif - -/* FPU settings ------------------------------------------------------------*/ -#if(__FPU_PRESENT == 1) && (__FPU_USED == 1) - SCB->CPACR |= - ((3UL << (10UL * 2UL)) | (3UL << (11UL * 2UL))); /* set CP10 and CP11 Full Access */ -#endif - - /* Reset the RCC clock configuration to the default reset state ------------*/ - /* Set MSION bit */ - RCC->CR |= RCC_CR_MSION; - - /* Reset CFGR register */ - RCC->CFGR = 0x00070000U; - - /* Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits */ - RCC->CR &= (uint32_t)0xFAF6FEFBU; - - /*!< Reset LSI1 and LSI2 bits */ - RCC->CSR &= (uint32_t)0xFFFFFFFAU; - - /*!< Reset HSI48ON bit */ - RCC->CRRCR &= (uint32_t)0xFFFFFFFEU; - - /* Reset PLLCFGR register */ - RCC->PLLCFGR = 0x22041000U; - -#if defined(STM32WB55xx) || defined(STM32WB5Mxx) - /* Reset PLLSAI1CFGR register */ - RCC->PLLSAI1CFGR = 0x22041000U; -#endif - - /* Reset HSEBYP bit */ - RCC->CR &= 0xFFFBFFFFU; - - /* Disable all interrupts */ - RCC->CIER = 0x00000000; -} diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c index 261adb5ca..d15474e52 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -109,7 +109,7 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest memmove((void*)(SRAM1_BASE), img, stat.fsize); LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); - furi_hal_switch((void*)SRAM1_BASE); + furi_hal_switch(0x0); return true; } while(false); diff --git a/targets/f7/startup_stm32wb55xx_cm4.s b/targets/f7/startup_stm32wb55xx_cm4.s deleted file mode 100644 index c5c2b3fc3..000000000 --- a/targets/f7/startup_stm32wb55xx_cm4.s +++ /dev/null @@ -1,444 +0,0 @@ -/** - ****************************************************************************** - * @file startup_stm32wb55xx_cm4.s - * @author MCD Application Team - * @brief STM32WB55xx devices vector table GCC toolchain. - * This module performs: - * - Set the initial SP - * - Set the initial PC == Reset_Handler, - * - Set the vector table entries with the exceptions ISR address - * - Branches to main in the C library (which eventually - * calls main()). - * After Reset the Cortex-M4 processor is in Thread mode, - * priority is Privileged, and the Stack is set to Main. - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ - - .syntax unified - .cpu cortex-m4 - .fpu softvfp - .thumb - -.global g_pfnVectors -.global Default_Handler - -/* start address for the initialization values of the .data section. -defined in linker script */ -.word _sidata -/* start address for the .data section. defined in linker script */ -.word _sdata -/* end address for the .data section. defined in linker script */ -.word _edata -/* start address for the .bss section. defined in linker script */ -.word _sbss -/* end address for the .bss section. defined in linker script */ -.word _ebss -/* start address for the .MB_MEM2 section. defined in linker script */ -.word _sMB_MEM2 -/* end address for the .MB_MEM2 section. defined in linker script */ -.word _eMB_MEM2 - -/* INIT_BSS macro is used to fill the specified region [start : end] with zeros */ -.macro INIT_BSS start, end - ldr r0, =\start - ldr r1, =\end - movs r3, #0 - bl LoopFillZerobss -.endm - -/* INIT_DATA macro is used to copy data in the region [start : end] starting from 'src' */ -.macro INIT_DATA start, end, src - ldr r0, =\start - ldr r1, =\end - ldr r2, =\src - movs r3, #0 - bl LoopCopyDataInit -.endm - -.section .text.data_initializers -CopyDataInit: - ldr r4, [r2, r3] - str r4, [r0, r3] - adds r3, r3, #4 - -LoopCopyDataInit: - adds r4, r0, r3 - cmp r4, r1 - bcc CopyDataInit - bx lr - -FillZerobss: - str r3, [r0] - adds r0, r0, #4 - -LoopFillZerobss: - cmp r0, r1 - bcc FillZerobss - bx lr - - .section .text.Reset_Handler - .weak Reset_Handler - .type Reset_Handler, %function -Reset_Handler: - ldr r0, =_estack - mov sp, r0 /* set stack pointer */ -/* Call the clock system intitialization function.*/ - bl SystemInit - -/* Copy the data segment initializers from flash to SRAM */ - INIT_DATA _sdata, _edata, _sidata - -/* Zero fill the bss segments. */ - INIT_BSS _sbss, _ebss - INIT_BSS _sMB_MEM2, _eMB_MEM2 - -/* Call static constructors */ - bl __libc_init_array -/* Call the application s entry point.*/ - bl main - -LoopForever: - b LoopForever - -.size Reset_Handler, .-Reset_Handler - -/** - * @brief This is the code that gets called when the processor receives an - * unexpected interrupt. This simply enters an infinite loop, preserving - * the system state for examination by a debugger. - * - * @param None - * @retval None -*/ - .section .text.Default_Handler,"ax",%progbits -Default_Handler: -Infinite_Loop: - b Infinite_Loop - .size Default_Handler, .-Default_Handler -/****************************************************************************** -* -* The minimal vector table for a Cortex-M4. Note that the proper constructs -* must be placed on this to ensure that it ends up at physical address -* 0x0000.0000. -* -******************************************************************************/ - .section .isr_vector,"a",%progbits - .type g_pfnVectors, %object - .size g_pfnVectors, .-g_pfnVectors - - -g_pfnVectors: - .word _estack - .word Reset_Handler - .word NMI_Handler - .word HardFault_Handler - .word MemManage_Handler - .word BusFault_Handler - .word UsageFault_Handler - .word 0 - .word 0 - .word 0 - .word 0 - .word SVC_Handler - .word DebugMon_Handler - .word 0 - .word PendSV_Handler - .word SysTick_Handler - .word WWDG_IRQHandler - .word PVD_PVM_IRQHandler - .word TAMP_STAMP_LSECSS_IRQHandler - .word RTC_WKUP_IRQHandler - .word FLASH_IRQHandler - .word RCC_IRQHandler - .word EXTI0_IRQHandler - .word EXTI1_IRQHandler - .word EXTI2_IRQHandler - .word EXTI3_IRQHandler - .word EXTI4_IRQHandler - .word DMA1_Channel1_IRQHandler - .word DMA1_Channel2_IRQHandler - .word DMA1_Channel3_IRQHandler - .word DMA1_Channel4_IRQHandler - .word DMA1_Channel5_IRQHandler - .word DMA1_Channel6_IRQHandler - .word DMA1_Channel7_IRQHandler - .word ADC1_IRQHandler - .word USB_HP_IRQHandler - .word USB_LP_IRQHandler - .word C2SEV_PWR_C2H_IRQHandler - .word COMP_IRQHandler - .word EXTI9_5_IRQHandler - .word TIM1_BRK_IRQHandler - .word TIM1_UP_TIM16_IRQHandler - .word TIM1_TRG_COM_TIM17_IRQHandler - .word TIM1_CC_IRQHandler - .word TIM2_IRQHandler - .word PKA_IRQHandler - .word I2C1_EV_IRQHandler - .word I2C1_ER_IRQHandler - .word I2C3_EV_IRQHandler - .word I2C3_ER_IRQHandler - .word SPI1_IRQHandler - .word SPI2_IRQHandler - .word USART1_IRQHandler - .word LPUART1_IRQHandler - .word SAI1_IRQHandler - .word TSC_IRQHandler - .word EXTI15_10_IRQHandler - .word RTC_Alarm_IRQHandler - .word CRS_IRQHandler - .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .word IPCC_C1_RX_IRQHandler - .word IPCC_C1_TX_IRQHandler - .word HSEM_IRQHandler - .word LPTIM1_IRQHandler - .word LPTIM2_IRQHandler - .word LCD_IRQHandler - .word QUADSPI_IRQHandler - .word AES1_IRQHandler - .word AES2_IRQHandler - .word RNG_IRQHandler - .word FPU_IRQHandler - .word DMA2_Channel1_IRQHandler - .word DMA2_Channel2_IRQHandler - .word DMA2_Channel3_IRQHandler - .word DMA2_Channel4_IRQHandler - .word DMA2_Channel5_IRQHandler - .word DMA2_Channel6_IRQHandler - .word DMA2_Channel7_IRQHandler - .word DMAMUX1_OVR_IRQHandler - -/******************************************************************************* -* -* Provide weak aliases for each Exception handler to the Default_Handler. -* As they are weak aliases, any function with the same name will override -* this definition. -* -*******************************************************************************/ - .weak NMI_Handler - .thumb_set NMI_Handler,Default_Handler - - .weak HardFault_Handler - .thumb_set HardFault_Handler,Default_Handler - - .weak MemManage_Handler - .thumb_set MemManage_Handler,Default_Handler - - .weak BusFault_Handler - .thumb_set BusFault_Handler,Default_Handler - - .weak UsageFault_Handler - .thumb_set UsageFault_Handler,Default_Handler - - .weak SVC_Handler - .thumb_set SVC_Handler,Default_Handler - - .weak DebugMon_Handler - .thumb_set DebugMon_Handler,Default_Handler - - .weak PendSV_Handler - .thumb_set PendSV_Handler,Default_Handler - - .weak SysTick_Handler - .thumb_set SysTick_Handler,Default_Handler - - .weak WWDG_IRQHandler - .thumb_set WWDG_IRQHandler,Default_Handler - - .weak PVD_PVM_IRQHandler - .thumb_set PVD_PVM_IRQHandler,Default_Handler - - .weak TAMP_STAMP_LSECSS_IRQHandler - .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler - - .weak RTC_WKUP_IRQHandler - .thumb_set RTC_WKUP_IRQHandler,Default_Handler - - .weak FLASH_IRQHandler - .thumb_set FLASH_IRQHandler,Default_Handler - - .weak RCC_IRQHandler - .thumb_set RCC_IRQHandler,Default_Handler - - .weak EXTI0_IRQHandler - .thumb_set EXTI0_IRQHandler,Default_Handler - - .weak EXTI1_IRQHandler - .thumb_set EXTI1_IRQHandler,Default_Handler - - .weak EXTI2_IRQHandler - .thumb_set EXTI2_IRQHandler,Default_Handler - - .weak EXTI3_IRQHandler - .thumb_set EXTI3_IRQHandler,Default_Handler - - .weak EXTI4_IRQHandler - .thumb_set EXTI4_IRQHandler,Default_Handler - - .weak DMA1_Channel1_IRQHandler - .thumb_set DMA1_Channel1_IRQHandler,Default_Handler - - .weak DMA1_Channel2_IRQHandler - .thumb_set DMA1_Channel2_IRQHandler,Default_Handler - - .weak DMA1_Channel3_IRQHandler - .thumb_set DMA1_Channel3_IRQHandler,Default_Handler - - .weak DMA1_Channel4_IRQHandler - .thumb_set DMA1_Channel4_IRQHandler,Default_Handler - - .weak DMA1_Channel5_IRQHandler - .thumb_set DMA1_Channel5_IRQHandler,Default_Handler - - .weak DMA1_Channel6_IRQHandler - .thumb_set DMA1_Channel6_IRQHandler,Default_Handler - - .weak DMA1_Channel7_IRQHandler - .thumb_set DMA1_Channel7_IRQHandler,Default_Handler - - .weak ADC1_IRQHandler - .thumb_set ADC1_IRQHandler,Default_Handler - - .weak USB_HP_IRQHandler - .thumb_set USB_HP_IRQHandler,Default_Handler - - .weak USB_LP_IRQHandler - .thumb_set USB_LP_IRQHandler,Default_Handler - - .weak C2SEV_PWR_C2H_IRQHandler - .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler - - .weak COMP_IRQHandler - .thumb_set COMP_IRQHandler,Default_Handler - - .weak EXTI9_5_IRQHandler - .thumb_set EXTI9_5_IRQHandler,Default_Handler - - .weak TIM1_BRK_IRQHandler - .thumb_set TIM1_BRK_IRQHandler,Default_Handler - - .weak TIM1_UP_TIM16_IRQHandler - .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler - - .weak TIM1_TRG_COM_TIM17_IRQHandler - .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler - - .weak TIM1_CC_IRQHandler - .thumb_set TIM1_CC_IRQHandler,Default_Handler - - .weak TIM2_IRQHandler - .thumb_set TIM2_IRQHandler,Default_Handler - - .weak PKA_IRQHandler - .thumb_set PKA_IRQHandler,Default_Handler - - .weak I2C1_EV_IRQHandler - .thumb_set I2C1_EV_IRQHandler,Default_Handler - - .weak I2C1_ER_IRQHandler - .thumb_set I2C1_ER_IRQHandler,Default_Handler - - .weak I2C3_EV_IRQHandler - .thumb_set I2C3_EV_IRQHandler,Default_Handler - - .weak I2C3_ER_IRQHandler - .thumb_set I2C3_ER_IRQHandler,Default_Handler - - .weak SPI1_IRQHandler - .thumb_set SPI1_IRQHandler,Default_Handler - - .weak SPI2_IRQHandler - .thumb_set SPI2_IRQHandler,Default_Handler - - .weak USART1_IRQHandler - .thumb_set USART1_IRQHandler,Default_Handler - - .weak LPUART1_IRQHandler - .thumb_set LPUART1_IRQHandler,Default_Handler - - .weak SAI1_IRQHandler - .thumb_set SAI1_IRQHandler,Default_Handler - - .weak TSC_IRQHandler - .thumb_set TSC_IRQHandler,Default_Handler - - .weak EXTI15_10_IRQHandler - .thumb_set EXTI15_10_IRQHandler,Default_Handler - - .weak RTC_Alarm_IRQHandler - .thumb_set RTC_Alarm_IRQHandler,Default_Handler - - .weak CRS_IRQHandler - .thumb_set CRS_IRQHandler,Default_Handler - - .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler - - .weak IPCC_C1_RX_IRQHandler - .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler - - .weak IPCC_C1_TX_IRQHandler - .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler - - .weak HSEM_IRQHandler - .thumb_set HSEM_IRQHandler,Default_Handler - - .weak LPTIM1_IRQHandler - .thumb_set LPTIM1_IRQHandler,Default_Handler - - .weak LPTIM2_IRQHandler - .thumb_set LPTIM2_IRQHandler,Default_Handler - - .weak LCD_IRQHandler - .thumb_set LCD_IRQHandler,Default_Handler - - .weak QUADSPI_IRQHandler - .thumb_set QUADSPI_IRQHandler,Default_Handler - - .weak AES1_IRQHandler - .thumb_set AES1_IRQHandler,Default_Handler - - .weak AES2_IRQHandler - .thumb_set AES2_IRQHandler,Default_Handler - - .weak RNG_IRQHandler - .thumb_set RNG_IRQHandler,Default_Handler - - .weak FPU_IRQHandler - .thumb_set FPU_IRQHandler,Default_Handler - - .weak DMA2_Channel1_IRQHandler - .thumb_set DMA2_Channel1_IRQHandler,Default_Handler - - .weak DMA2_Channel2_IRQHandler - .thumb_set DMA2_Channel2_IRQHandler,Default_Handler - - .weak DMA2_Channel3_IRQHandler - .thumb_set DMA2_Channel3_IRQHandler,Default_Handler - - .weak DMA2_Channel4_IRQHandler - .thumb_set DMA2_Channel4_IRQHandler,Default_Handler - - .weak DMA2_Channel5_IRQHandler - .thumb_set DMA2_Channel5_IRQHandler,Default_Handler - - .weak DMA2_Channel6_IRQHandler - .thumb_set DMA2_Channel6_IRQHandler,Default_Handler - - .weak DMA2_Channel7_IRQHandler - .thumb_set DMA2_Channel7_IRQHandler,Default_Handler - - .weak DMAMUX1_OVR_IRQHandler - .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/targets/f7/stm32wb55xx_flash.ld b/targets/f7/stm32wb55xx_flash.ld index df4c5b726..3fb789645 100644 --- a/targets/f7/stm32wb55xx_flash.ld +++ b/targets/f7/stm32wb55xx_flash.ld @@ -1,198 +1,134 @@ -/** -***************************************************************************** -** -** File : stm32wb55xx_flash.ld -** -** Abstract : System Workbench Minimal System calls file -** -** For more information about which c-functions -** need which of these lowlevel functions -** please consult the Newlib libc-manual -** -** Environment : System Workbench for MCU -** -** Distribution: The file is distributed “as is,” without any warranty -** of any kind. -** -***************************************************************************** -** -**

© COPYRIGHT(c) 2019 Ac6

-** -** Redistribution and use in source and binary forms, with or without modification, -** are permitted provided that the following conditions are met: -** 1. Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** 3. Neither the name of Ac6 nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -***************************************************************************** -*/ - -/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ -_estack = 0x20030000; /* end of RAM */ +_stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x400; /* required amount of heap */ -_Min_Stack_Size = 0x1000; /* required amount of stack */ +_stack_size = 0x1000; /* required amount of stack */ -/* Specify the memory areas */ -MEMORY -{ -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K -RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 -RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K +MEMORY { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } -/* Define output sections */ -SECTIONS -{ - /* The startup code goes first into FLASH */ - .isr_vector : - { +SECTIONS { + /* The startup code goes first into FLASH */ + .isr_vector : { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : { + . = ALIGN(4); + *lib*.a:*(.text .text.*) /* code from libraries before apps */ + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(*_DRIVER_CONTEXT) + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> FLASH + + /* Uninitialized data section */ . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH + .bss : { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) - /* The program code and other data goes into FLASH */ - .text : - { - . = ALIGN(4); - *lib*.a:*(.text .text.*) /* code from libraries before apps */ - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.glue_7) /* glue arm to thumb code */ - *(.glue_7t) /* glue thumb to arm code */ - *(.eh_frame) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 - KEEP (*(.init)) - KEEP (*(.fini)) + /* User_heap_stack section, used to check that there is enough RAM left */ + /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ + ._user_heap_stack(DSECT) : { + . = ALIGN(8); + __heap_start__ = .; + . = ORIGIN(RAM1) + LENGTH(RAM1) - _stack_size; + __heap_end__ = .; + . = . + _stack_size; + . = ALIGN(8); + } >RAM1 - . = ALIGN(4); - _etext = .; /* define a global symbols at end of code */ - } >FLASH + /* Free Flash space, that can be used for internal storage */ + .free_flash(DSECT) : { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + } >FLASH - /* Constant data goes into FLASH */ - .rodata : - { - . = ALIGN(4); - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - . = ALIGN(4); - } >FLASH + /* Remove information from the standard libraries */ + /DISCARD/ : { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >FLASH - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >FLASH - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } >FLASH - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >FLASH - - /* used by the startup to initialize data */ - _sidata = LOADADDR(.data); - - /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : - { - . = ALIGN(4); - _sdata = .; /* create a global symbol at data start */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - *(*_DRIVER_CONTEXT) - - . = ALIGN(4); - _edata = .; /* define a global symbol at data end */ - } >RAM1 AT> FLASH - - /* Uninitialized data section */ - . = ALIGN(4); - .bss : - { - /* This is used by the startup in order to initialize the .bss secion */ - _sbss = .; /* define a global symbol at bss start */ - __bss_start__ = _sbss; - *(.bss) - *(.bss*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end */ - __bss_end__ = _ebss; - } >RAM1 - - /* User_heap_stack section, used to check that there is enough RAM left */ - /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ - ._user_heap_stack(DSECT): - { - . = ALIGN(8); - __heap_start__ = .; - . = ORIGIN(RAM1) + LENGTH(RAM1) - _Min_Stack_Size; - __heap_end__ = .; - . = . + _Min_Stack_Size; - . = ALIGN(8); - } >RAM1 - - /* Free Flash space, that can be used for internal storage */ - .free_flash(DSECT): - { - __free_flash_start__ = .; - . = ORIGIN(FLASH) + LENGTH(FLASH); - } >FLASH - - /* Remove information from the standard libraries */ - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - .ARM.attributes 0 : { *(.ARM.attributes) } - ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A - ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A - ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + .ARM.attributes 0 : { *(.ARM.attributes) } + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } - - diff --git a/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld index 0ac9be4df..cae30b6e9 100644 --- a/targets/f7/stm32wb55xx_ram_fw.ld +++ b/targets/f7/stm32wb55xx_ram_fw.ld @@ -1,196 +1,132 @@ -/** -***************************************************************************** -** -** File : stm32wb55xx_ram_fw.ld -** -** Abstract : System Workbench Minimal System calls file -** -** For more information about which c-functions -** need which of these lowlevel functions -** please consult the Newlib libc-manual -** -** Environment : System Workbench for MCU -** -** Distribution: The file is distributed “as is,” without any warranty -** of any kind. -** -***************************************************************************** -** -**

© COPYRIGHT(c) 2019 Ac6

-** -** Redistribution and use in source and binary forms, with or without modification, -** are permitted provided that the following conditions are met: -** 1. Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** 3. Neither the name of Ac6 nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -***************************************************************************** -*/ - -/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ -_estack = 0x20030000; /* end of RAM */ +_stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x400; /* required amount of heap */ -_Min_Stack_Size = 0x1000; /* required amount of stack */ +_stack_size = 0x1000; /* required amount of stack */ -/* Specify the memory areas */ -MEMORY -{ -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K -RAM1 (xrw) : ORIGIN = 0x20000000, LENGTH = 0x30000 -RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K +MEMORY { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM1 (xrw) : ORIGIN = 0x20000000, LENGTH = 0x30000 + RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } -/* Define output sections */ -SECTIONS -{ - /* The startup code goes first into FLASH */ - .isr_vector : - { +SECTIONS { + /* The startup code goes first into FLASH */ + .isr_vector : { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >RAM1 + + /* The program code and other data goes into FLASH */ + .text : { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >RAM1 + + /* Constant data goes into FLASH */ + .rodata : { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >RAM1 + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >RAM1 + + .preinit_array : { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >RAM1 + .init_array : { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >RAM1 + .fini_array : { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >RAM1 + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> RAM1 + + /* Uninitialized data section */ . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >RAM1 + .bss : { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) - /* The program code and other data goes into FLASH */ - .text : - { - . = ALIGN(4); - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.glue_7) /* glue arm to thumb code */ - *(.glue_7t) /* glue thumb to arm code */ - *(.eh_frame) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 - KEEP (*(.init)) - KEEP (*(.fini)) + /* User_heap_stack section, used to check that there is enough RAM left */ + /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ + ._user_heap_stack(DSECT) : { + . = ALIGN(8); + __heap_start__ = .; + . = ORIGIN(RAM1) + LENGTH(RAM1) - _stack_size; + __heap_end__ = .; + . = . + _stack_size; + . = ALIGN(8); + } >RAM1 - . = ALIGN(4); - _etext = .; /* define a global symbols at end of code */ - } >RAM1 + /* Free Flash space, that can be used for internal storage */ + /*.free_flash(DSECT) : { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + } >FLASH*/ - /* Constant data goes into FLASH */ - .rodata : - { - . = ALIGN(4); - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - . = ALIGN(4); - } >RAM1 + /* Remove information from the standard libraries */ + /DISCARD/ : { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >RAM1 - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >RAM1 - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } >RAM1 - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >RAM1 - - /* used by the startup to initialize data */ - _sidata = LOADADDR(.data); - - /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : - { - . = ALIGN(4); - _sdata = .; /* create a global symbol at data start */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - - . = ALIGN(4); - _edata = .; /* define a global symbol at data end */ - } >RAM1 AT> RAM1 - - - /* Uninitialized data section */ - . = ALIGN(4); - .bss : - { - /* This is used by the startup in order to initialize the .bss secion */ - _sbss = .; /* define a global symbol at bss start */ - __bss_start__ = _sbss; - *(.bss) - *(.bss*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end */ - __bss_end__ = _ebss; - } >RAM1 - - /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack(DSECT) : - { - . = ALIGN(8); - __heap_start__ = .; - . = ORIGIN(RAM1) + LENGTH(RAM1) - _Min_Stack_Size; - __heap_end__ = .; - . = . + _Min_Stack_Size; - . = ALIGN(8); - } >RAM1 - - /* Free Flash space, that can be used for internal storage */ - /*.free_flash(DSECT): - { - __free_flash_start__ = .; - . = ORIGIN(FLASH) + LENGTH(FLASH); - } >FLASH*/ - - /* Remove information from the standard libraries */ - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - .ARM.attributes 0 : { *(.ARM.attributes) } - ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A - ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A - ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + .ARM.attributes 0 : { *(.ARM.attributes) } + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } - - diff --git a/targets/f7/target.json b/targets/f7/target.json index eae92a5cd..665864d7d 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -13,7 +13,6 @@ "ble_glue/services", "ble_glue/profiles" ], - "startup_script": "startup_stm32wb55xx_cm4.s", "linker_script_flash": "stm32wb55xx_flash.ld", "linker_script_ram": "stm32wb55xx_ram_fw.ld", "linker_script_app": "application_ext.ld", diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h index 719df0275..cf483553f 100644 --- a/targets/furi_hal_include/furi_hal.h +++ b/targets/furi_hal_include/furi_hal.h @@ -55,9 +55,15 @@ void furi_hal_deinit_early(void); /** Init FuriHal */ void furi_hal_init(void); -/** Transfer execution to address +/** Jump to the void* * - * @param[in] address pointer to new executable + * Allow your code to transfer control to another firmware. + * + * @warning This code doesn't reset system before jump. Call it only from + * main thread, no kernel should be running. Ensure that no + * peripheral blocks active and no interrupts are pending. + * + * @param address The System Vector address(start of your new firmware) */ void furi_hal_switch(void* address); diff --git a/targets/furi_hal_include/furi_hal_mpu.h b/targets/furi_hal_include/furi_hal_mpu.h index 7a5759c17..1d91b123d 100644 --- a/targets/furi_hal_include/furi_hal_mpu.h +++ b/targets/furi_hal_include/furi_hal_mpu.h @@ -14,8 +14,9 @@ extern "C" { typedef enum { FuriHalMpuRegionNULL = 0x00, // region 0 used to protect null pointer dereference - FuriHalMpuRegionStack = 0x01, // region 1 used to protect stack - FuriHalMpuRegion2 = 0x02, + FuriHalMpuRegionMainStack = 0x01, // region 1 used to protect Main Stack + FuriHalMpuRegionThreadStack = + 0x02, // region 2 used to protect currently executed RTOS Thread Stack FuriHalMpuRegion3 = 0x03, FuriHalMpuRegion4 = 0x04, FuriHalMpuRegion5 = 0x05,