From 09e8d4851e3915b551b397f6448ca949af7e2129 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Wed, 8 Jan 2025 11:52:20 -0800 Subject: [PATCH 1/6] Aux core runs an arbitrary sequence of modules (which is actually the same seq as before) Moved aux core patch player interrupts to its own class --- firmware/src/core_a7/aux_core_main.cc | 83 +++----------------- firmware/src/core_a7/aux_core_player.hh | 95 +++++++++++++++++++++++ firmware/src/core_a7/smp_api.hh | 6 -- firmware/src/patch_play/multicore_play.hh | 17 ++-- 4 files changed, 114 insertions(+), 87 deletions(-) create mode 100644 firmware/src/core_a7/aux_core_player.hh diff --git a/firmware/src/core_a7/aux_core_main.cc b/firmware/src/core_a7/aux_core_main.cc index 9b8b5de4c..3d535a801 100644 --- a/firmware/src/core_a7/aux_core_main.cc +++ b/firmware/src/core_a7/aux_core_main.cc @@ -1,17 +1,14 @@ +#include "aux_core_player.hh" #include "conf/hsem_conf.hh" #include "core_a7/a7_shared_memory.hh" #include "core_a7/async_thread_control.hh" -#include "core_a7/smp_api.hh" #include "debug.hh" #include "drivers/hsem.hh" -#include "drivers/smp.hh" -#include "drivers/timekeeper.hh" #include "dynload/plugin_manager.hh" #include "fs/filesystem.hh" #include "fs/norflash_layout.hh" #include "gui/ui.hh" #include "internal_plugin_manager.hh" -#include "patch_play/patch_player.hh" using FrameBufferT = std::array; @@ -31,29 +28,22 @@ extern "C" void aux_core_main() { pr_info("A7 Core 2 starting\n"); - auto patch_player = A7SharedMemoryS::ptrs.patch_player; - auto patch_playloader = A7SharedMemoryS::ptrs.patch_playloader; - auto file_storage_proxy = A7SharedMemoryS::ptrs.patch_storage; - auto open_patch_manager = A7SharedMemoryS::ptrs.open_patch_manager; - auto sync_params = A7SharedMemoryS::ptrs.sync_params; - auto patch_mod_queue = A7SharedMemoryS::ptrs.patch_mod_queue; - auto ramdisk_storage = A7SharedMemoryS::ptrs.ramdrive; #ifdef CONSOLE_USE_USB UartLog::use_usb(A7SharedMemoryS::ptrs.console_buffer); #endif LVGLDriver gui{MMDisplay::flush_to_screen, MMDisplay::read_input, MMDisplay::wait_cb, framebuf1, framebuf2}; - RamDiskOps ramdisk_ops{*ramdisk_storage}; + RamDiskOps ramdisk_ops{*A7SharedMemoryS::ptrs.ramdrive}; FatFileIO ramdisk{&ramdisk_ops, Volume::RamDisk}; AssetFS asset_fs{AssetVolFlashOffset}; Filesystem::Init(ramdisk); - PluginManager plugin_manager{*file_storage_proxy, ramdisk}; - Ui ui{*patch_playloader, - *file_storage_proxy, - *open_patch_manager, - *sync_params, - *patch_mod_queue, + PluginManager plugin_manager{*A7SharedMemoryS::ptrs.patch_storage, ramdisk}; + Ui ui{*A7SharedMemoryS::ptrs.patch_playloader, + *A7SharedMemoryS::ptrs.patch_storage, + *A7SharedMemoryS::ptrs.open_patch_manager, + *A7SharedMemoryS::ptrs.sync_params, + *A7SharedMemoryS::ptrs.patch_mod_queue, plugin_manager, ramdisk}; ui.update_screen(); @@ -61,62 +51,7 @@ extern "C" void aux_core_main() { InternalPluginManager internal_plugin_manager{ramdisk, asset_fs}; - struct AuxCoreModulesToRun { - uint32_t starting_idx = 1; - uint32_t num_modules = 0; - uint32_t idx_increment = 2; - } modules_to_run; - - constexpr auto PlayModuleListIRQn = SMPControl::IRQn(SMPCommand::PlayModuleList); - InterruptManager::register_and_start_isr(PlayModuleListIRQn, 1, 0, [&modules_to_run, &patch_player]() { - // Debug::Pin1::high(); - for (unsigned i = modules_to_run.starting_idx; i < modules_to_run.num_modules; - i += modules_to_run.idx_increment) - { - patch_player->modules[i]->update(); - } - // Debug::Pin1::low(); - SMPThread::signal_done(); - }); - - constexpr auto NewModuleListIRQn = SMPControl::IRQn(SMPCommand::NewModuleList); - InterruptManager::register_and_start_isr(NewModuleListIRQn, 0, 0, [&modules_to_run]() { - modules_to_run.starting_idx = SMPControl::read(); - modules_to_run.num_modules = SMPControl::read(); - modules_to_run.idx_increment = SMPControl::read(); - SMPThread::signal_done(); - }); - - constexpr auto ReadPatchLightsIRQn = SMPControl::IRQn(SMPCommand::ReadPatchLights); - InterruptManager::register_and_start_isr(ReadPatchLightsIRQn, 2, 0, [patch_player, &ui]() { - if (ui.new_patch_data == false) { - - for (auto &w : ui.lights().watch_lights) { - if (w.is_active()) { - auto val = patch_player->get_module_light(w.module_id, w.light_id); - w.value = val; - } - } - - for (auto &d : ui.displays().watch_displays) { - if (d.is_active()) { - auto text = std::span(d.text._data, d.text.capacity); - auto sz = patch_player->get_display_text(d.module_id, d.light_id, text); - d.text._data[sz] = '\0'; - } - } - - for (auto &p : ui.watched_params().active_watched_params()) { - if (p.is_active()) { - p.value = patch_player->get_param(p.module_id, p.param_id); - } - } - - ui.new_patch_data = true; - } - - SMPThread::signal_done(); - }); + AuxPlayer aux_player{*A7SharedMemoryS::ptrs.patch_player, ui}; // Wait for M4 to be ready (so USB and SD are available) while (mdrivlib::HWSemaphore::is_locked()) diff --git a/firmware/src/core_a7/aux_core_player.hh b/firmware/src/core_a7/aux_core_player.hh new file mode 100644 index 000000000..ecb7b2a9f --- /dev/null +++ b/firmware/src/core_a7/aux_core_player.hh @@ -0,0 +1,95 @@ +#pragma once +#include "core_a7/smp_api.hh" +#include "drivers/interrupt.hh" +#include "drivers/smp.hh" +#include "gui/ui.hh" +#include "patch_play/patch_player.hh" +#include "util/fixed_vector.hh" + +namespace MetaModule +{ + +struct AuxPlayer { + PatchPlayer &patch_player; + Ui &ui; + + FixedVector module_ids; + + AuxPlayer(PatchPlayer &patch_player, Ui &ui) + : patch_player{patch_player} + , ui{ui} { + using namespace mdrivlib; + + constexpr auto NewModuleListIRQn = SMPControl::IRQn(SMPCommand::NewModuleList); + InterruptManager::register_and_start_isr(NewModuleListIRQn, 0, 0, [this]() { assign_module_list(); }); + + constexpr auto PlayModuleListIRQn = SMPControl::IRQn(SMPCommand::PlayModuleList); + InterruptManager::register_and_start_isr(PlayModuleListIRQn, 1, 0, [this]() { play_modules(); }); + + constexpr auto ReadPatchLightsIRQn = SMPControl::IRQn(SMPCommand::ReadPatchLights); + InterruptManager::register_and_start_isr(ReadPatchLightsIRQn, 2, 0, [this]() { read_patch_gui_elements(); }); + } + + void play_modules() { + for (auto i : module_ids) { + // Debug::Pin1::high(); + patch_player.modules[i]->update(); + // Debug::Pin1::low(); + } + + mdrivlib::SMPThread::signal_done(); + } + + void assign_module_list() { + using namespace mdrivlib; + + module_ids.clear(); + + auto num_modules = SMPControl::read(); + // pr_dbg("Core 2 will play %u modules:\n", num_modules); + + if (num_modules < module_ids.max_size()) { + for (auto i = 0u; i < num_modules; i++) { + auto id = SMPControl::read(i + 2); + module_ids.push_back(id); + // pr_dbg("%u\n", id); + } + + } else + pr_err("Error: %u modules requested to run on core 2, max is %z\n", num_modules, module_ids.size()); + + SMPThread::signal_done(); + } + + void read_patch_gui_elements() { + if (ui.new_patch_data == false) { + + for (auto &w : ui.lights().watch_lights) { + if (w.is_active()) { + auto val = patch_player.get_module_light(w.module_id, w.light_id); + w.value = val; + } + } + + for (auto &d : ui.displays().watch_displays) { + if (d.is_active()) { + auto text = std::span(d.text._data, d.text.capacity); + auto sz = patch_player.get_display_text(d.module_id, d.light_id, text); + d.text._data[sz] = '\0'; + } + } + + for (auto &p : ui.watched_params().active_watched_params()) { + if (p.is_active()) { + p.value = patch_player.get_param(p.module_id, p.param_id); + } + } + + ui.new_patch_data = true; + } + + SMPThread::signal_done(); + } +}; + +} // namespace MetaModule diff --git a/firmware/src/core_a7/smp_api.hh b/firmware/src/core_a7/smp_api.hh index 15c2a68ed..e04c3553d 100644 --- a/firmware/src/core_a7/smp_api.hh +++ b/firmware/src/core_a7/smp_api.hh @@ -13,12 +13,6 @@ namespace SMPRegister { enum : uint32_t { DoneZero, - ModuleID, - ParamID, - ParamVal, - FunctionAddress, NumModulesInPatch, - UpdateModuleOffset, - Unused, }; } // namespace SMPRegister diff --git a/firmware/src/patch_play/multicore_play.hh b/firmware/src/patch_play/multicore_play.hh index defe4ee25..2fb48e016 100644 --- a/firmware/src/patch_play/multicore_play.hh +++ b/firmware/src/patch_play/multicore_play.hh @@ -11,7 +11,6 @@ public: void load_patch(unsigned num_modules) { - num_modules_ = num_modules; //Module 0 is the hub //Module 1 is processed by first core //Module 2 is processed by second core @@ -20,9 +19,16 @@ public: //... etc if constexpr (mdrivlib::SMPControl::NumCores > 1) { mdrivlib::SMPThread::init(); - mdrivlib::SMPControl::write(2); //first module to process - mdrivlib::SMPControl::write(num_modules); - mdrivlib::SMPControl::write(ModuleStride); + + if (num_modules == 0) + num_modules = 1; + + mdrivlib::SMPControl::write((num_modules - 1) / 2); + + for (auto i = 2u, module_id = 2u; module_id < num_modules; module_id += 2) { + mdrivlib::SMPControl::write(i++, module_id); + } + mdrivlib::SMPControl::notify(); } } @@ -44,9 +50,6 @@ public: mdrivlib::SMPThread::join(); } } - -private: - unsigned num_modules_ = 0; }; } // namespace MetaModule From 06c11b8693961aae0782100717fb05526e6431c3 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Wed, 8 Jan 2025 11:44:59 -0800 Subject: [PATCH 2/6] Update cpputil (Partition class) --- firmware/lib/cpputil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/lib/cpputil b/firmware/lib/cpputil index 3e510c2b1..6822ef580 160000 --- a/firmware/lib/cpputil +++ b/firmware/lib/cpputil @@ -1 +1 @@ -Subproject commit 3e510c2b15483501351730a1203b96e230f6a498 +Subproject commit 6822ef580f8db031445269d7af56a178fe9e5acb From f83a98598953321d959fb2d02f7c45295219f081 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Wed, 8 Jan 2025 11:46:25 -0800 Subject: [PATCH 3/6] Patch player measures each module to balance the load between cores Simulator: add CycleCounter so we can do load measurements --- firmware/src/patch_play/balance_modules.hh | 62 ++++++++++++++++ firmware/src/patch_play/multicore_play.hh | 29 +++----- firmware/src/patch_play/patch_player.hh | 85 ++++++++++++---------- simulator/stubs/drivers/cycle_counter.hh | 57 +++++++++++++++ 4 files changed, 173 insertions(+), 60 deletions(-) create mode 100644 firmware/src/patch_play/balance_modules.hh create mode 100644 simulator/stubs/drivers/cycle_counter.hh diff --git a/firmware/src/patch_play/balance_modules.hh b/firmware/src/patch_play/balance_modules.hh new file mode 100644 index 000000000..4899c3136 --- /dev/null +++ b/firmware/src/patch_play/balance_modules.hh @@ -0,0 +1,62 @@ +#pragma once +#include "CoreModules/CoreProcessor.hh" +#include "drivers/cycle_counter.hh" +#include "util/partition.hh" +#include +#include + +#include "console/pr_dbg.hh" + +namespace MetaModule +{ + +template +struct Balancer { + Partition cores; + + void split_modules(std::span> modules, unsigned num_modules, auto &&prepare) { + mdrivlib::CycleCounter counter; + counter.init(); + std::vector times(num_modules - 1); + + for (size_t i = 1; i < num_modules; i++) { + // Run 512 samples, and discard + for (auto j = 0; j < 512; j++) { + modules[i]->update(); + } + + // Run another 512 samples and measure + times[i - 1] = 0; + for (auto j = 0; j < 512; j++) { + counter.start_measurement(); + modules[i]->update(); + counter.end_measurement(); + times[i - 1] += counter.get_last_measurement_raw(); + prepare(); + } + + // times[i - 1] = counter.get_last_measurement_raw(); + pr_dbg("Module %u: %u\n", i, times[i - 1]); + } + + cores.calc_partitions(times); + + // Adjust indices since we skip module 0 + for (auto &part : cores.parts) { + for (auto &idx : part) + idx++; + } + + // Debug output: + for (auto core = 0u; core < NumCores; core++) { + unsigned sum = 0; + for (auto idx : cores.parts[core]) { + pr_dbg("Core %d: Module %u: %u\n", core, idx, times[idx - 1]); + sum += times[idx - 1]; + } + pr_dbg("Core %d Total: %u\n", core, sum); + } + } +}; + +} // namespace MetaModule diff --git a/firmware/src/patch_play/multicore_play.hh b/firmware/src/patch_play/multicore_play.hh index 2fb48e016..1084ec776 100644 --- a/firmware/src/patch_play/multicore_play.hh +++ b/firmware/src/patch_play/multicore_play.hh @@ -1,52 +1,41 @@ #pragma once #include "core_a7/smp_api.hh" #include "drivers/smp.hh" +#include namespace MetaModule { class MulticorePlayer { public: - static constexpr unsigned ModuleStride = mdrivlib::SMPControl::NumCores; + static constexpr unsigned NumCores = mdrivlib::SMPControl::NumCores; - void load_patch(unsigned num_modules) { - - //Module 0 is the hub - //Module 1 is processed by first core - //Module 2 is processed by second core - //Module 3 is processed by first core - //Module 4 is processed by second core - //... etc - if constexpr (mdrivlib::SMPControl::NumCores > 1) { + void assign_modules(std::span module_ids) { + if constexpr (NumCores > 1) { mdrivlib::SMPThread::init(); + mdrivlib::SMPControl::write(SMPRegister::NumModulesInPatch, module_ids.size()); - if (num_modules == 0) - num_modules = 1; - - mdrivlib::SMPControl::write((num_modules - 1) / 2); - - for (auto i = 2u, module_id = 2u; module_id < num_modules; module_id += 2) { + for (auto i = 2u; auto module_id : module_ids) { // regs 2 and up are the module ids mdrivlib::SMPControl::write(i++, module_id); } - mdrivlib::SMPControl::notify(); } } void update_modules() { - if constexpr (mdrivlib::SMPControl::NumCores > 1) { + if constexpr (NumCores > 1) { mdrivlib::SMPThread::split_with_command(); } } void read_patch_state() { - if constexpr (mdrivlib::SMPControl::NumCores > 1) { + if constexpr (NumCores > 1) { mdrivlib::SMPThread::split_with_command(); } } void join() { - if constexpr (mdrivlib::SMPControl::NumCores > 1) { + if constexpr (NumCores > 1) { mdrivlib::SMPThread::join(); } } diff --git a/firmware/src/patch_play/patch_player.hh b/firmware/src/patch_play/patch_player.hh index 530676a8b..f74eb8101 100644 --- a/firmware/src/patch_play/patch_player.hh +++ b/firmware/src/patch_play/patch_player.hh @@ -4,8 +4,6 @@ #include "CoreModules/moduleFactory.hh" #include "conf/panel_conf.hh" #include "conf/patch_conf.hh" -#include "core_a7/smp_api.hh" -#include "drivers/smp.hh" #include "midi/midi_message.hh" #include "midi/midi_router.hh" #include "null_module.hh" @@ -14,12 +12,12 @@ #include "patch/midi_def.hh" #include "patch/patch.hh" #include "patch/patch_data.hh" +#include "patch_play/balance_modules.hh" #include "patch_play/multicore_play.hh" #include "patch_play/patch_player_query_patch.hh" #include "pr_dbg.hh" #include "result_t.hh" #include "util/countzip.hh" -#include "util/math.hh" #include "util/oscs.hh" #include #include @@ -78,6 +76,7 @@ private: std::array in_patched{}; MulticorePlayer smp; + Balancer core_balancer; float samplerate = 48000.f; @@ -120,9 +119,6 @@ public: return {false, "Too many modules in the patch! Max is 32"}; } - // Tell the other core about the patch - smp.load_patch(num_modules); - // First module is the hub modules[0] = ModuleFactory::create(PanelDef::typeID); if (modules[0] != nullptr) @@ -181,6 +177,8 @@ public: active_knob_set = 0; catchup_manager.reset(modules, knob_maps[active_knob_set]); + rebalance_modules(); + // Test-run the modules once for (size_t i = 1; i < num_modules; i++) { modules[i]->update(); @@ -198,46 +196,51 @@ public: return {true}; } + void rebalance_modules() { + core_balancer.split_modules(modules, num_modules, [this] { update_int_cables(); }); + smp.assign_modules(core_balancer.cores.parts[MulticorePlayer::NumCores - 1]); + } + // Runs the patch void update_patch() { - if (num_modules <= 1) - return; - else if (num_modules == 2) + if (num_modules == 2) modules[1]->update(); - else { + + else if (num_modules > 2) { smp.update_modules(); - // Debug::Pin2::high(); - for (size_t module_i = 1; module_i < num_modules; module_i += smp.ModuleStride) { + for (auto module_i : core_balancer.cores.parts[0]) { + //for (auto &in: cable_ins[module_i]) { + // modules[module_i]->set_input(in.jack_id, in.out_cable->val); + //} + // Debug::Pin2::high(); modules[module_i]->update(); + //for (auto &out: cable_outs[module_i]) { + // out.val = modules[module_i]->get_output(out.jack_id); + //} + // Debug::Pin2::low(); } - // Debug::Pin2::low(); smp.join(); - } + } else + return; + + update_int_cables(); + update_midi_pulses(); + } + void update_int_cables() { for (auto &cable : pd.int_cables) { float out_val = modules[cable.out.module_id]->get_output(cable.out.jack_id); for (auto &input_jack : cable.ins) { modules[input_jack.module_id]->set_input(input_jack.jack_id, out_val); } } - - update_midi_pulses(); } void update_patch_singlecore() { - // Debug::Pin2::high(); for (size_t module_i = 1; module_i < num_modules; module_i++) { modules[module_i]->update(); } - // Debug::Pin2::low(); - - for (auto &cable : pd.int_cables) { - float out_val = modules[cable.out.module_id]->get_output(cable.out.jack_id); - for (auto &input_jack : cable.ins) { - modules[input_jack.module_id]->set_input(input_jack.jack_id, out_val); - } - } - + update_int_cables(); update_midi_pulses(); } @@ -266,7 +269,7 @@ public: clear_cache(); } - // K-rate setters/getters: + // Interface with audio stream: void set_panel_param(unsigned panel_knob_id, float val) { catchup_manager.set_panel_param(modules, knob_maps[active_knob_set], panel_knob_id, val); @@ -357,6 +360,17 @@ public: } } + void set_samplerate(float hz) { + samplerate = hz; + + for (auto &mp : midi_pulses) + mp.pulse.set_update_rate_hz(samplerate); + + for (size_t i = 1; i < num_modules; i++) { + modules[i]->set_samplerate(samplerate); + } + } + private: void set_all_connected_jacks(std::vector const &jacks, float val) { for (auto const &jack : jacks) @@ -413,6 +427,8 @@ public: return pd.midi_poly_num; } + // Patch Mods: + void apply_static_param(const StaticParam &sparam) { if (sparam.module_id < num_modules && modules[sparam.module_id]) modules[sparam.module_id]->set_param(sparam.param_id, sparam.value); @@ -587,7 +603,7 @@ public: reset_module(module_idx); - smp.load_patch(num_modules); + rebalance_modules(); } void remove_module(uint16_t module_idx) { @@ -683,18 +699,7 @@ public: //TODO: move async tasks to right core - smp.load_patch(num_modules); - } - - void set_samplerate(float hz) { - samplerate = hz; - - for (auto &mp : midi_pulses) - mp.pulse.set_update_rate_hz(samplerate); - - for (size_t i = 1; i < num_modules; i++) { - modules[i]->set_samplerate(samplerate); - } + rebalance_modules(); } // General info getters: diff --git a/simulator/stubs/drivers/cycle_counter.hh b/simulator/stubs/drivers/cycle_counter.hh new file mode 100644 index 000000000..5201debc5 --- /dev/null +++ b/simulator/stubs/drivers/cycle_counter.hh @@ -0,0 +1,57 @@ +#pragma once +#include +#include + +namespace mdrivlib +{ +class CycleCounter { +public: + CycleCounter() { + init(); + } + + void init() { + } + + void start_measurement() { + _start_tm = read_cycle_count(); + _period = _start_tm - _last_start_tm; + _last_start_tm = _start_tm; + } + + void end_measurement() { + _measured_tm = read_cycle_count() - _start_tm; + } + + uint32_t get_last_measurement_raw() { + return _measured_tm; + } + + uint32_t get_last_period_raw() { + return _period; + } + + float get_last_measurement_load_float() { + if (_period == 0) + return 0; + return (float)_measured_tm / (float)_period; + } + + uint32_t get_last_measurement_load_percent() { + if (_period == 0) + return 0; + return (_measured_tm * 100) / _period; + } + +private: + uint32_t _last_start_tm = 0; + uint32_t _start_tm = 0; + uint32_t _measured_tm = 0; + uint32_t _period = 0; + + uint32_t read_cycle_count() { + auto now = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(now).count(); + } +}; +} // namespace mdrivlib From f0fb7c77d5036d85c883ffad9bac37e368916116 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Tue, 7 Jan 2025 13:20:27 -0800 Subject: [PATCH 4/6] Account for core switching overhead when balancing modules --- firmware/src/patch_play/balance_modules.hh | 34 ++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/firmware/src/patch_play/balance_modules.hh b/firmware/src/patch_play/balance_modules.hh index 4899c3136..d4bc7285e 100644 --- a/firmware/src/patch_play/balance_modules.hh +++ b/firmware/src/patch_play/balance_modules.hh @@ -17,29 +17,33 @@ struct Balancer { void split_modules(std::span> modules, unsigned num_modules, auto &&prepare) { mdrivlib::CycleCounter counter; counter.init(); - std::vector times(num_modules - 1); + std::vector times(num_modules - 1, 0); - for (size_t i = 1; i < num_modules; i++) { - // Run 512 samples, and discard - for (auto j = 0; j < 512; j++) { - modules[i]->update(); - } + for (auto iter_i = 0; iter_i < 512 + 32; iter_i++) { + + for (size_t module_i = 1; module_i < num_modules; module_i++) { - // Run another 512 samples and measure - times[i - 1] = 0; - for (auto j = 0; j < 512; j++) { counter.start_measurement(); - modules[i]->update(); + modules[module_i]->update(); counter.end_measurement(); - times[i - 1] += counter.get_last_measurement_raw(); - prepare(); + + // Discard first 32 runs + if (iter_i >= 32) + times[module_i - 1] += counter.get_last_measurement_raw(); } - // times[i - 1] = counter.get_last_measurement_raw(); - pr_dbg("Module %u: %u\n", i, times[i - 1]); + prepare(); + } + + for (size_t module_i = 1; module_i < num_modules; module_i++) { + pr_dbg("Module %u: %u\n", module_i, times[module_i - 1]); } - cores.calc_partitions(times); + if (NumCores == 2) { + auto bias = std::array{0, 1000}; + cores.calc_partitions(times, bias); + } else + cores.calc_partitions(times); // Adjust indices since we skip module 0 for (auto &part : cores.parts) { From b61f23ff5b13a6cf43a0686b3655fc3fc06cc296 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Tue, 7 Jan 2025 16:28:14 -0800 Subject: [PATCH 5/6] Each core updates internal cable when module updates Create a cable cache to manage the data --- firmware/src/core_a7/aux_core_player.hh | 7 +-- firmware/src/patch_play/balance_modules.hh | 31 +++++++---- firmware/src/patch_play/cable_cache.hh | 64 ++++++++++++++++++++++ firmware/src/patch_play/patch_player.hh | 56 +++++++++---------- 4 files changed, 114 insertions(+), 44 deletions(-) create mode 100644 firmware/src/patch_play/cable_cache.hh diff --git a/firmware/src/core_a7/aux_core_player.hh b/firmware/src/core_a7/aux_core_player.hh index ecb7b2a9f..c4db323eb 100644 --- a/firmware/src/core_a7/aux_core_player.hh +++ b/firmware/src/core_a7/aux_core_player.hh @@ -31,10 +31,9 @@ struct AuxPlayer { } void play_modules() { - for (auto i : module_ids) { - // Debug::Pin1::high(); - patch_player.modules[i]->update(); - // Debug::Pin1::low(); + + for (auto module_i : module_ids) { + patch_player.step_module(module_i); } mdrivlib::SMPThread::signal_done(); diff --git a/firmware/src/patch_play/balance_modules.hh b/firmware/src/patch_play/balance_modules.hh index d4bc7285e..6e7a4ae36 100644 --- a/firmware/src/patch_play/balance_modules.hh +++ b/firmware/src/patch_play/balance_modules.hh @@ -1,6 +1,7 @@ #pragma once #include "CoreModules/CoreProcessor.hh" #include "drivers/cycle_counter.hh" +#include "patch/module_type_slug.hh" #include "util/partition.hh" #include #include @@ -14,32 +15,36 @@ template struct Balancer { Partition cores; - void split_modules(std::span> modules, unsigned num_modules, auto &&prepare) { + std::vector + measure_modules(std::span> modules, unsigned num_modules, auto run) { + mdrivlib::CycleCounter counter; - counter.init(); + + constexpr size_t NumIterations = 512; + constexpr size_t DropFirst = 32; + std::vector times(num_modules - 1, 0); - for (auto iter_i = 0; iter_i < 512 + 32; iter_i++) { + for (auto iter_i = 0u; iter_i < NumIterations + DropFirst; iter_i++) { for (size_t module_i = 1; module_i < num_modules; module_i++) { counter.start_measurement(); - modules[module_i]->update(); + run(module_i); counter.end_measurement(); - // Discard first 32 runs - if (iter_i >= 32) + if (iter_i >= DropFirst) times[module_i - 1] += counter.get_last_measurement_raw(); } - - prepare(); } - for (size_t module_i = 1; module_i < num_modules; module_i++) { - pr_dbg("Module %u: %u\n", module_i, times[module_i - 1]); - } + return times; + } + void balance_loads(std::span times) { if (NumCores == 2) { + // Core 2 needs extra time to respond to its interrupt + // units is 1/24MHz auto bias = std::array{0, 1000}; cores.calc_partitions(times, bias); } else @@ -50,12 +55,14 @@ struct Balancer { for (auto &idx : part) idx++; } + } + void print_times(std::span times, std::span slugs) { // Debug output: for (auto core = 0u; core < NumCores; core++) { unsigned sum = 0; for (auto idx : cores.parts[core]) { - pr_dbg("Core %d: Module %u: %u\n", core, idx, times[idx - 1]); + pr_dbg("Core %d: Module %u (%s): %u\n", core, idx, slugs[idx].c_str(), times[idx - 1]); sum += times[idx - 1]; } pr_dbg("Core %d Total: %u\n", core, sum); diff --git a/firmware/src/patch_play/cable_cache.hh b/firmware/src/patch_play/cable_cache.hh new file mode 100644 index 000000000..12b506136 --- /dev/null +++ b/firmware/src/patch_play/cable_cache.hh @@ -0,0 +1,64 @@ +#pragma once +#include "conf/patch_conf.hh" +#include "patch/patch_data.hh" +#include + +namespace MetaModule +{ + +struct CableCache { + CableCache() = default; + + struct CableOut { + float val; + uint16_t jack_id; + }; + + struct CableIn { + uint16_t jack_id; + uint16_t out_module_id; + uint16_t out_cache_idx; + //todo: profile using this instead: + // CableOut *out; + }; + + void clear() { + for (auto &out : outs) + out.clear(); + for (auto &in : ins) + in.clear(); + } + + void build(std::span cables) { + clear(); + + for (auto &cable : cables) { + if (cable.out.module_id >= outs.size()) + continue; + + auto &out = outs[cable.out.module_id]; + auto out_idx = out.size(); + out.emplace_back(0.f, cable.out.jack_id); + + for (auto &in : cable.ins) { + if (in.module_id < ins.size()) { + ins[in.module_id].emplace_back(in.jack_id, cable.out.module_id, out_idx); + } + } + } + } + + void add(Jack injack, Jack outjack) { + if (injack.module_id < ins.size() && outjack.module_id < outs.size()) { + auto &out = outs[outjack.module_id]; + auto out_idx = out.size(); + out.emplace_back(0.f, outjack.jack_id); + ins[injack.module_id].emplace_back(injack.jack_id, outjack.module_id, out_idx); + } + } + + // outs[N] and ins[N] are the cables connected to module id N + std::array, MAX_MODULES_IN_PATCH> outs; + std::array, MAX_MODULES_IN_PATCH> ins; +}; +} // namespace MetaModule diff --git a/firmware/src/patch_play/patch_player.hh b/firmware/src/patch_play/patch_player.hh index f74eb8101..166300448 100644 --- a/firmware/src/patch_play/patch_player.hh +++ b/firmware/src/patch_play/patch_player.hh @@ -13,6 +13,7 @@ #include "patch/patch.hh" #include "patch/patch_data.hh" #include "patch_play/balance_modules.hh" +#include "patch_play/cable_cache.hh" #include "patch_play/multicore_play.hh" #include "patch_play/patch_player_query_patch.hh" #include "pr_dbg.hh" @@ -34,6 +35,8 @@ namespace MetaModule class PatchPlayer { public: std::array, MAX_MODULES_IN_PATCH> modules; + CableCache cables; + unsigned num_modules = 0; std::atomic is_loaded = false; @@ -174,16 +177,13 @@ public: calc_multiple_module_indicies(); + cables.build(pd.int_cables); + active_knob_set = 0; catchup_manager.reset(modules, knob_maps[active_knob_set]); rebalance_modules(); - // Test-run the modules once - for (size_t i = 1; i < num_modules; i++) { - modules[i]->update(); - } - is_loaded = true; if (num_not_found == 1) return {true, std::string{"Module "} + not_found + std::string{" not known, ignoring."}}; @@ -197,7 +197,12 @@ public: } void rebalance_modules() { - core_balancer.split_modules(modules, num_modules, [this] { update_int_cables(); }); + auto cpu_times = + core_balancer.measure_modules(modules, num_modules, [this](unsigned module_i) { step_module(module_i); }); + core_balancer.balance_loads(cpu_times); + + core_balancer.print_times(cpu_times, pd.module_slugs); + smp.assign_modules(core_balancer.cores.parts[MulticorePlayer::NumCores - 1]); } @@ -209,38 +214,29 @@ public: else if (num_modules > 2) { smp.update_modules(); for (auto module_i : core_balancer.cores.parts[0]) { - //for (auto &in: cable_ins[module_i]) { - // modules[module_i]->set_input(in.jack_id, in.out_cable->val); - //} - // Debug::Pin2::high(); - modules[module_i]->update(); - //for (auto &out: cable_outs[module_i]) { - // out.val = modules[module_i]->get_output(out.jack_id); - //} - // Debug::Pin2::low(); + step_module(module_i); } smp.join(); } else return; - update_int_cables(); update_midi_pulses(); } - void update_int_cables() { - for (auto &cable : pd.int_cables) { - float out_val = modules[cable.out.module_id]->get_output(cable.out.jack_id); - for (auto &input_jack : cable.ins) { - modules[input_jack.module_id]->set_input(input_jack.jack_id, out_val); - } - } + void step_module(unsigned module_i) { + for (auto const &in : cables.ins[module_i]) + modules[module_i]->set_input(in.jack_id, cables.outs[in.out_module_id][in.out_cache_idx].val); + + modules[module_i]->update(); + + for (auto &out : cables.outs[module_i]) + out.val = modules[module_i]->get_output(out.jack_id); } void update_patch_singlecore() { for (size_t module_i = 1; module_i < num_modules; module_i++) { - modules[module_i]->update(); + step_module(module_i); } - update_int_cables(); update_midi_pulses(); } @@ -255,6 +251,7 @@ public: for (size_t i = 0; i < num_modules; i++) { modules[i].reset(nullptr); } + cables.clear(); pd.int_cables.clear(); pd.mapped_ins.clear(); pd.knob_sets.clear(); @@ -487,6 +484,7 @@ public: void add_internal_cable(Jack in, Jack out) { pd.add_internal_cable(in, out); + cables.add(in, out); modules[out.module_id]->mark_output_patched(out.jack_id); modules[in.module_id]->mark_input_patched(in.jack_id); } @@ -560,6 +558,8 @@ public: } pd.disconnect_injack(jack); + + cables.build(pd.int_cables); } void disconnect_outjack(Jack jack) { @@ -578,6 +578,8 @@ public: } pd.disconnect_outjack(jack); + + cables.build(pd.int_cables); } void reset_module(uint16_t module_id, std::string_view data = "") { @@ -650,7 +652,6 @@ public: unsigned ins_to_disconnect = 0; for (auto in : cable.ins) { - if (cable.out.module_id == module_idx) { modules[in.module_id]->mark_input_unpatched(in.jack_id); } @@ -697,8 +698,6 @@ public: modules[i]->id = i; } - //TODO: move async tasks to right core - rebalance_modules(); } @@ -706,6 +705,7 @@ public: // Jack patched/unpatched status + // Follow every internal cable and tell the modules that their jacks are patched void mark_patched_jacks() { for (auto const &cable : pd.int_cables) { modules[cable.out.module_id]->mark_output_patched(cable.out.jack_id); From 556940735d136defe79d4e928f333aa02ee63175 Mon Sep 17 00:00:00 2001 From: Dan Green Date: Wed, 8 Jan 2025 11:34:32 -0800 Subject: [PATCH 6/6] Process module outputs all at once, to mitigate multiple cores reading and writing the same jack at the same time --- firmware/src/core_a7/aux_core_player.hh | 6 ++++-- firmware/src/patch_play/patch_player.hh | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/firmware/src/core_a7/aux_core_player.hh b/firmware/src/core_a7/aux_core_player.hh index c4db323eb..c53336eed 100644 --- a/firmware/src/core_a7/aux_core_player.hh +++ b/firmware/src/core_a7/aux_core_player.hh @@ -32,6 +32,10 @@ struct AuxPlayer { void play_modules() { + for (auto module_i : module_ids) { + patch_player.process_module_outputs(module_i); + } + for (auto module_i : module_ids) { patch_player.step_module(module_i); } @@ -45,13 +49,11 @@ struct AuxPlayer { module_ids.clear(); auto num_modules = SMPControl::read(); - // pr_dbg("Core 2 will play %u modules:\n", num_modules); if (num_modules < module_ids.max_size()) { for (auto i = 0u; i < num_modules; i++) { auto id = SMPControl::read(i + 2); module_ids.push_back(id); - // pr_dbg("%u\n", id); } } else diff --git a/firmware/src/patch_play/patch_player.hh b/firmware/src/patch_play/patch_player.hh index 166300448..3bc7a0e89 100644 --- a/firmware/src/patch_play/patch_player.hh +++ b/firmware/src/patch_play/patch_player.hh @@ -213,6 +213,9 @@ public: else if (num_modules > 2) { smp.update_modules(); + for (auto module_i : core_balancer.cores.parts[0]) { + process_module_outputs(module_i); + } for (auto module_i : core_balancer.cores.parts[0]) { step_module(module_i); } @@ -223,14 +226,16 @@ public: update_midi_pulses(); } + void process_module_outputs(unsigned module_i) { + for (auto &out : cables.outs[module_i]) + out.val = modules[module_i]->get_output(out.jack_id); + } + void step_module(unsigned module_i) { for (auto const &in : cables.ins[module_i]) modules[module_i]->set_input(in.jack_id, cables.outs[in.out_module_id][in.out_cache_idx].val); modules[module_i]->update(); - - for (auto &out : cables.outs[module_i]) - out.val = modules[module_i]->get_output(out.jack_id); } void update_patch_singlecore() {