gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 18c39a62ddf423a184b81e085a6a2f4f4d23a292
parent 937cfec4796bb566c42ca88b4b3bbe6bf0e66b3c
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Fri, 26 Jul 2024 23:29:09 +0200

syncronization work

Diffstat:
Msource/hardwareLib/haltDSP.cpp | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msource/hardwareLib/haltDSP.h | 23+++++++++++++++++++++--
Msource/nord/n2x/n2xLib/n2xdsp.cpp | 44+++++++++++++++++++++++++++-----------------
Msource/nord/n2x/n2xLib/n2xdsp.h | 9++++++---
Msource/nord/n2x/n2xLib/n2xhardware.cpp | 46++++++++++++++++++----------------------------
Msource/nord/n2x/n2xLib/n2xhardware.h | 19++++---------------
6 files changed, 139 insertions(+), 70 deletions(-)

diff --git a/source/hardwareLib/haltDSP.cpp b/source/hardwareLib/haltDSP.cpp @@ -10,12 +10,26 @@ namespace hwLib { } - void HaltDSP::haltDSP() + void HaltDSP::haltDSP(const bool _wait) { - if(m_halted) - return; if(++m_halted == 1) + { + ++m_irqRequestCount; + m_dsp.injectExternalInterrupt(m_irq); + + if(_wait) + { + const auto expectedCount = m_irqRequestCount; + std::unique_lock lock(m_mutex); + m_cvHalted.wait(lock, [this, expectedCount] + { + if(expectedCount != m_irqServedCount) + return false; + return true; + }); + } + } } bool HaltDSP::resumeDSP() @@ -23,12 +37,56 @@ namespace hwLib if(!m_halted) return false; if(--m_halted == 0) - m_semaphore.notify(); + m_blockSem.notify(); return true; } + void HaltDSP::wakeUp(std::function<void()>&& _func) + { + if(!m_halted) + { + _func(); + return; + } + + { + std::unique_lock lock(m_mutex); + m_wakeUps.emplace(m_wakeUpId++, std::move(_func)); + } + m_blockSem.notify(); + } + void HaltDSP::onInterrupt() { - m_semaphore.wait(); + // signal waiter that we reached halt state + { + std::unique_lock lock(m_mutex); + ++m_irqServedCount; + } + m_cvHalted.notify_one(); + + // halt and wait for resume or a wakeup call + m_blockSem.wait(); + + while(true) + { + std::function<void()> func; + + // check if wakeup call + { + std::unique_lock lock(m_mutex); + const auto it = m_wakeUps.find(m_wakeUpCount); + if(it == m_wakeUps.end()) + break; + + func = std::move(it->second); + m_wakeUps.erase(it); + ++m_wakeUpCount; + } + + // execute wakeup and go back to halt state + func(); + m_blockSem.wait(); + } } } diff --git a/source/hardwareLib/haltDSP.h b/source/hardwareLib/haltDSP.h @@ -1,6 +1,8 @@ #pragma once #include <cstdint> +#include <functional> +#include <unordered_map> #include "baseLib/semaphore.h" @@ -16,9 +18,15 @@ namespace hwLib public: explicit HaltDSP(dsp56k::DSP& _dsp); - void haltDSP(); + void haltDSP(bool _wait = false); bool resumeDSP(); + bool halted() const {return m_halted > 0; } + + dsp56k::DSP& getDSP() const { return m_dsp; } + + void wakeUp(std::function<void()>&& _func); + private: void onInterrupt(); @@ -26,7 +34,18 @@ namespace hwLib uint32_t m_halted = 0; uint32_t m_irq; - baseLib::Semaphore m_semaphore; + baseLib::Semaphore m_blockSem; + + std::mutex m_mutex; + std::condition_variable m_cvHalted; + + uint32_t m_irqServedCount = 0; + uint32_t m_irqRequestCount = 0; + + uint32_t m_wakeUpId = 0; + uint32_t m_wakeUpCount = 0; + + std::unordered_map<uint32_t, std::function<void()>> m_wakeUps; }; class ScopedResumeDSP diff --git a/source/nord/n2x/n2xLib/n2xdsp.cpp b/source/nord/n2x/n2xLib/n2xdsp.cpp @@ -64,6 +64,7 @@ namespace n2x , m_name(_index ? "DSP B" : "DSP A") , m_memory(g_memValidator, g_pMemSize, g_xyMemSize, g_externalMemAddr) , m_dsp(m_memory, &m_periphX, &m_periphNop) + , m_haltDSP(m_dsp) { if(!_hw.isValid()) return; @@ -177,7 +178,7 @@ namespace n2x m_thread->setLogToStdout(false); - m_vbaInterruptDone = dsp().registerInterruptFunc([this] + m_irqInterruptDone = dsp().registerInterruptFunc([this] { m_triggerInterruptDone.notify(); }); @@ -215,14 +216,33 @@ namespace n2x void DSP::hdiSendIrqToDSP(const uint8_t _irq) { -// waitDspRxEmpty(); - dsp().injectExternalInterrupt(_irq); - - dsp().injectExternalInterrupt(m_vbaInterruptDone); - m_hardware.resumeDSPsForFunc([&] + // If applicable, we wait for the halt on our DSP after resuming it + /*if(m_haltDSP.halted()) { + bool done = false; + std::mutex mutex; + std::condition_variable wait; + m_haltDSP.wakeUp([this, _irq, &mutex, &done, &wait] + { + dsp().execInterrupt(_irq); + { + std::unique_lock lock(mutex); + done = true; + } + wait.notify_one(); + }); + std::unique_lock lock(mutex); + wait.wait(lock, [&done]{return done;}); + assert(done); + } + else*/ + { + dsp().injectExternalInterrupt(_irq); + dsp().injectExternalInterrupt(m_irqInterruptDone); + + hwLib::ScopedResumeDSP r(m_hardware.getDSPB().getHaltDSP()); m_triggerInterruptDone.wait(); - }); + } hdiTransferDSPtoUC(); } @@ -265,14 +285,4 @@ namespace n2x } return false; } - - void DSP::waitDspRxEmpty() - { - m_hardware.ucYieldLoop([&]() - { - return (hdi08().hasRXData() && hdi08().rxInterruptEnabled()) || dsp().hasPendingInterrupts(); - }); -// assert(!hdi08().hasRXData()); -// LOG("writeRX wait over"); - } } diff --git a/source/nord/n2x/n2xLib/n2xdsp.h b/source/nord/n2x/n2xLib/n2xdsp.h @@ -7,6 +7,7 @@ #include "dsp56kEmu/dspthread.h" #include "baseLib/semaphore.h" +#include "hardwareLib/haltDSP.h" namespace mc68k { @@ -44,7 +45,8 @@ namespace n2x void advanceSamples(uint32_t _samples, uint32_t _latency); - dsp56k::DSPThread& getDSPThread() const { return *m_thread.get(); } + dsp56k::DSPThread& getDSPThread() const { return *m_thread; } + auto& getHaltDSP() { return m_haltDSP; } private: void onUCRxEmpty(bool _needMoreData); @@ -53,7 +55,6 @@ namespace n2x uint8_t hdiUcReadIsr(uint8_t _isr); void onEsaiCallback(); bool hdiTransferDSPtoUC(); - void waitDspRxEmpty(); Hardware& m_hardware; mc68k::Hdi08& m_hdiUC; @@ -81,6 +82,8 @@ namespace n2x std::mutex m_haltDSPmutex; baseLib::Semaphore m_triggerInterruptDone; - uint32_t m_vbaInterruptDone = 0; + uint32_t m_irqInterruptDone = 0; + + hwLib::HaltDSP m_haltDSP; }; } diff --git a/source/nord/n2x/n2xLib/n2xhardware.cpp b/source/nord/n2x/n2xLib/n2xhardware.cpp @@ -40,9 +40,8 @@ namespace n2x void Hardware::ucYieldLoop(const std::function<bool()>& _continue) { - const auto dspHalted = m_haltDSP; - - resumeDSP(); + hwLib::ScopedResumeDSP rA(m_dspA.getHaltDSP()); + hwLib::ScopedResumeDSP rB(m_dspB.getHaltDSP()); while(_continue()) { @@ -60,9 +59,6 @@ namespace n2x } */ } - - if(dspHalted) - haltDSP(); } void Hardware::processAudio(uint32_t _frames, const uint32_t _latency) @@ -192,9 +188,6 @@ namespace n2x { m_requestedFramesAvailableMutex.unlock(); } - - std::unique_lock uLock(m_haltDSPmutex); - m_haltDSPcv.wait(uLock, [&]{ return m_haltDSP == false; }); } void Hardware::syncUCtoDSP() @@ -208,7 +201,8 @@ namespace n2x if(m_esaiFrameIndex == m_lastEsaiFrameIndex) { - resumeDSP(); + m_dspHalted = false; + resumeDSPs(); std::unique_lock uLock(m_esaiFrameAddedMutex); m_esaiFrameAddedCv.wait(uLock, [this]{return m_esaiFrameIndex > m_lastEsaiFrameIndex;}); } @@ -233,34 +227,30 @@ namespace n2x if(esaiDelta > g_syncHaltDspEsaiThreshold) { - haltDSP(); + if(!m_dspHalted) + { + m_dspHalted = true; + haltDSPs(); + } } - else + else if(m_dspHalted) { - resumeDSP(); + m_dspHalted = false; + resumeDSPs(); } m_lastEsaiFrameIndex = esaiFrameIndex; } - void Hardware::haltDSP() + void Hardware::haltDSPs() { - if(m_haltDSP) - return; - - std::lock_guard uLockHalt(m_haltDSPmutex); - m_haltDSP = true; +// m_dspA.getHaltDSP().haltDSP(); + m_dspB.getHaltDSP().haltDSP(); } - void Hardware::resumeDSP() + void Hardware::resumeDSPs() { - if(!m_haltDSP) - return; - - { - std::lock_guard uLockHalt(m_haltDSPmutex); - m_haltDSP = false; - } - m_haltDSPcv.notify_one(); +// m_dspA.getHaltDSP().resumeDSP(); + m_dspB.getHaltDSP().resumeDSP(); } } diff --git a/source/nord/n2x/n2xLib/n2xhardware.h b/source/nord/n2x/n2xLib/n2xhardware.h @@ -23,25 +23,16 @@ namespace n2x void processAudio(uint32_t _frames, uint32_t _latency); - void resumeDSPsForFunc(const std::function<void()>& _callback) - { - const auto halted = m_haltDSP; - if(halted) - resumeDSP(); - _callback(); - if(halted) - haltDSP(); - } - auto& getDSPA() { return m_dspA; } auto& getDSPB() { return m_dspB; } + void haltDSPs(); + void resumeDSPs(); + private: void ensureBufferSize(uint32_t _frames); void onEsaiCallback(); void syncUCtoDSP(); - void haltDSP(); - void resumeDSP(); Rom m_rom; Microcontroller m_uc; @@ -65,8 +56,6 @@ namespace n2x std::mutex m_requestedFramesAvailableMutex; std::condition_variable m_requestedFramesAvailableCv; size_t m_requestedFrames = 0; - bool m_haltDSP = false; - std::condition_variable m_haltDSPcv; - std::mutex m_haltDSPmutex; + bool m_dspHalted = false; }; }