From b6e750867c77a5831fc740f3513322ebe8ad89d8 Mon Sep 17 00:00:00 2001 From: fredzo Date: Sun, 1 Feb 2026 22:55:43 +0100 Subject: [PATCH 1/4] First step tp digital channels in DemoOscilloscope. --- scopehal/CMakeLists.txt | 1 + scopehal/DemoOscilloscope.cpp | 101 +++++++- scopehal/DemoOscilloscope.h | 10 + scopehal/TestDigitalWaveformSource.cpp | 308 +++++++++++++++++++++++++ scopehal/TestDigitalWaveformSource.h | 85 +++++++ 5 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 scopehal/TestDigitalWaveformSource.cpp create mode 100644 scopehal/TestDigitalWaveformSource.h diff --git a/scopehal/CMakeLists.txt b/scopehal/CMakeLists.txt index b7e758e1..b03b61fd 100644 --- a/scopehal/CMakeLists.txt +++ b/scopehal/CMakeLists.txt @@ -224,6 +224,7 @@ set(SCOPEHAL_SOURCES SParameterFilter.cpp TestWaveformSource.cpp + TestDigitalWaveformSource.cpp ComputePipeline.cpp FilterGraphExecutor.cpp diff --git a/scopehal/DemoOscilloscope.cpp b/scopehal/DemoOscilloscope.cpp index 1a74700b..a6aa0e06 100644 --- a/scopehal/DemoOscilloscope.cpp +++ b/scopehal/DemoOscilloscope.cpp @@ -62,6 +62,8 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport) m_source[i] = new TestWaveformSource(*m_rng[i]); } + m_digitalSource = new TestDigitalWaveformSource(); + m_model = "Oscilloscope Simulator"; m_vendor = "Antikernel Labs"; m_serial = "12345"; @@ -93,6 +95,22 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport) m_channelModes[i] = CHANNEL_MODE_NOISE_LPF; } + char chn[32]; + for(size_t i=0; i<16; i++) + { + snprintf(chn, sizeof(chn), "D%zu", i); + auto chan = new OscilloscopeChannel( + this, + chn, + GetDefaultChannelColor(m_channels.size()), + Unit(Unit::UNIT_FS), + Unit(Unit::UNIT_COUNTS), + Stream::STREAM_TYPE_DIGITAL, + m_channels.size()); + m_channels.push_back(chan); + m_digitalChannels.push_back(chan); + } + m_sweepFreq = 1e9; //Default sampling configuration @@ -104,6 +122,22 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport) m_channels[2]->SetDisplayName("PRBS31"); m_channels[3]->SetDisplayName("8B10B"); + m_channels[4]->SetDisplayName("SPI-CS"); + m_channels[5]->SetDisplayName("SPI-SCLK"); + m_channels[6]->SetDisplayName("SPI-MOSI"); + m_channels[7]->SetDisplayName("UART-0"); + m_channels[8]->SetDisplayName("UART-0-Clk"); + m_channels[9]->SetDisplayName("UART-1"); + m_channels[10]->SetDisplayName("UART-1-Clk"); + m_channels[11]->SetDisplayName("UART-2"); + m_channels[12]->SetDisplayName("UART-2-Clk"); + m_channels[13]->SetDisplayName("UART-3"); + m_channels[14]->SetDisplayName("UART-3-Clk"); + m_channels[15]->SetDisplayName("UART-4"); + m_channels[16]->SetDisplayName("UART-4-Clk"); + m_channels[17]->SetDisplayName("UART-5"); + m_channels[18]->SetDisplayName("UART-5-Clk"); + //Create Vulkan objects for the waveform conversion for(int i=0; i<4; i++) { @@ -137,6 +171,24 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport) } } +vector DemoOscilloscope::GetDigitalBanks() +{ + vector banks; + + for(size_t n = 0; n < 2; n++) + { + DigitalBank bank; + + for(size_t i = 0; i < 8; i++) + bank.push_back(m_digitalChannels[i + n * 8]); + + banks.push_back(bank); + } + + return banks; +} + + DemoOscilloscope::~DemoOscilloscope() { LogTrace("Shutting down demo scope\n"); @@ -146,6 +198,7 @@ DemoOscilloscope::~DemoOscilloscope() delete m_source[i]; delete m_rng[i]; } + delete m_digitalSource; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -538,7 +591,7 @@ bool DemoOscilloscope::AcquireData() auto depth = GetSampleDepth(); int64_t sampleperiod = FS_PER_SECOND / m_rate; - WaveformBase* waveforms[4] = {nullptr}; + WaveformBase* waveforms[4+16] = {nullptr}; for(int i=0; i<4; i++) { if(!m_channelsEnabled[i]) @@ -591,8 +644,52 @@ bool DemoOscilloscope::AcquireData() ChannelsDownloadStatusUpdate(i, InstrumentChannel::DownloadState::DOWNLOAD_FINISHED, 1.0); } + // Prepare SPI data + auto cs = new SparseDigitalWaveform("CS"); + auto sclk = new SparseDigitalWaveform("SCLK"); + auto mosi = new SparseDigitalWaveform("MOSI"); + + m_digitalSource->GenerateSPI(cs,sclk,mosi,sampleperiod, depth); + + for(int i=4; i<(4+16); i++) + { + if(!m_channelsEnabled[i]) + continue; + + // Lambda passed to generate waveform methods to update "download" percentage + ChannelsDownloadStatusUpdate(i, InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS, 0.0); + switch(i) + { + case 4: + waveforms[i] = cs; + break; + case 5: + waveforms[i] = sclk; + break; + case 6: + waveforms[i] = mosi; + break; + default: + if(i%2 == 1) + { + auto wfm = new SparseDigitalWaveform("UART"); + waveforms[i] = wfm; + m_digitalSource->GenerateUART(wfm, sampleperiod, depth); + } + else + { + auto wfm = new SparseDigitalWaveform("UART-Clk"); + waveforms[i] = wfm; + m_digitalSource->GenerateUARTClock(wfm, sampleperiod, depth); + } + break; + } + + ChannelsDownloadStatusUpdate(i, InstrumentChannel::DownloadState::DOWNLOAD_FINISHED, 1.0); + } + SequenceSet s; - for(int i=0; i<4; i++) + for(int i=0; i<(4+16); i++) { s[GetOscilloscopeChannel(i)] = waveforms[i]; } diff --git a/scopehal/DemoOscilloscope.h b/scopehal/DemoOscilloscope.h index 04c686e3..dfa94c62 100644 --- a/scopehal/DemoOscilloscope.h +++ b/scopehal/DemoOscilloscope.h @@ -38,6 +38,7 @@ #define DemoOscilloscope_h #include "TestWaveformSource.h" +#include "TestDigitalWaveformSource.h" #include /** @@ -119,6 +120,9 @@ class DemoOscilloscope : public virtual SCPIOscilloscope virtual unsigned int GetInstrumentTypes() const override; virtual void LoadConfiguration(int version, const YAML::Node& node, IDTable& idmap) override; + virtual std::vector GetDigitalBanks() override; + + protected: ///@brief External trigger @@ -145,6 +149,10 @@ class DemoOscilloscope : public virtual SCPIOscilloscope ///@brief Map of channel ID to ADC mode std::map m_channelModes; + ///@brief Digital channels + std::vector m_digitalChannels; + + ///@brief ADC mode selectors (used to select the simulated channel) enum ChannelModes { @@ -186,6 +194,8 @@ class DemoOscilloscope : public virtual SCPIOscilloscope */ TestWaveformSource* m_source[4]; + TestDigitalWaveformSource* m_digitalSource; + ///@brief Vulkan queue for ISI channel std::shared_ptr m_queue[4]; diff --git a/scopehal/TestDigitalWaveformSource.cpp b/scopehal/TestDigitalWaveformSource.cpp new file mode 100644 index 00000000..dd25a160 --- /dev/null +++ b/scopehal/TestDigitalWaveformSource.cpp @@ -0,0 +1,308 @@ +/*********************************************************************************************************************** +* * +* libscopehal * +* * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * +* All rights reserved. * +* * +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * +* following conditions are met: * +* * +* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products * +* derived from this software without specific prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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. * +* * +***********************************************************************************************************************/ + +/** + @file + @author Frederic BORRY + @brief Implementation of TestDigitalWaveformSource + + @ingroup core + */ +#include "scopehal.h" +#include "TestDigitalWaveformSource.h" +#include + +using namespace std; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Construction / destruction + +/** + @brief Initializes a TestDigitalWaveformSource + */ +TestDigitalWaveformSource::TestDigitalWaveformSource() +{ +} + +TestDigitalWaveformSource::~TestDigitalWaveformSource() +{ +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Signal generation + +/** + @brief Generates a UART waveform + + @param wfm Waveform to fill + @param sampleperiod Interval between samples, in femtoseconds + @param depth Total number of samples to generate + @param baudrate The baudrate of the UART link + */ +void TestDigitalWaveformSource::GenerateUART( + SparseDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate) +{ + wfm->m_triggerPhase = 0; + wfm->m_timescale = sampleperiod; + + int64_t timeWindow = depth * sampleperiod; + + const int64_t bitPeriodFs = FS_PER_SECOND / baudrate; + const int64_t bitPeriod = bitPeriodFs/sampleperiod; + + int64_t numBits = timeWindow / bitPeriodFs; + + wfm->Resize(numBits); + + int64_t currentTime = 0; + + int64_t bitCount = 0; + + auto push = [&bitCount,bitPeriod,numBits,depth] (SparseDigitalWaveform* w, int64_t time, bool v) -> bool + { + w->m_offsets.push_back(time); + w->m_durations.push_back(time + bitPeriod < (int64_t)depth ? bitPeriod : depth - time); + w->m_samples.push_back(v); + bitCount++; + return bitCount > numBits+1; + }; + + // Idle initial + push(wfm, currentTime, true); + currentTime += bitPeriod; + + string msg = "Hello World from ngscopeclient UART !"; + + while(bitCount < numBits) + { + for(uint8_t c : msg) + { + // Start bit + if(push(wfm, currentTime, false)) return; + currentTime += bitPeriod; + + // Data bits (LSB first) + for(int i = 0; i < 8; i++) + { + bool bit = (c >> i) & 1; + if(push(wfm, currentTime, bit)) return; + currentTime += bitPeriod; + } + + // Stop bit + if(push(wfm, currentTime, true)) return; + currentTime += bitPeriod; + } + } + + // Idle final + currentTime += bitPeriod; + push(wfm, currentTime, true); +} + +/** + @brief Generates a UART waveform using UniformDigitalWaveform + + @param wfm Waveform to fill + @param sampleperiod Interval between samples, in femtoseconds + @param depth Total number of samples to generate + @param baudrate The baudrate of the UART link + */ +void TestDigitalWaveformSource::GenerateUART( + UniformDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate) +{ + wfm->m_triggerPhase = 0; + wfm->m_timescale = sampleperiod; + + int64_t timeWindow = depth * sampleperiod; + + const int64_t bitPeriodFs = FS_PER_SECOND / baudrate; + + int64_t numBits = timeWindow / bitPeriodFs; + + wfm->Resize(numBits); + + string msg = "Hello World from ngscopeclient UART uniform waveform !"; + + const int64_t samplesPerBit = bitPeriodFs / sampleperiod; + + // Reqired bits + const size_t totalSamples = depth; + + wfm->Resize(totalSamples); + + size_t sample = 0; + + auto emitBit = [&](bool level) + { + for(int64_t i = 0; (i < samplesPerBit && sample < totalSamples); i++) + wfm->m_samples[sample++] = level; + }; + + // Idle initial + emitBit(true); + + while(sample < totalSamples) + { + for(uint8_t c : msg) + { + // Start bit + emitBit(false); + + // Data bits (LSB first) + for(int i = 0; i < 8; i++) + emitBit((c >> i) & 1); + + // Stop bit + emitBit(true); + } + } + + // Idle final + emitBit(true); +} + +/** + @brief Generates a UART clock waveform + + @param wfm Waveform to fill + @param sampleperiod Interval between samples, in femtoseconds + @param depth Total number of samples to generate + @param baudrate The baudrate of the UART link +*/ +void TestDigitalWaveformSource::GenerateUARTClock( + SparseDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate) +{ + wfm->m_triggerPhase = 0; + wfm->m_timescale = sampleperiod; + + int64_t timeWindow = depth * sampleperiod; + + const int64_t bitPeriodFs = (FS_PER_SECOND / baudrate)/2; + const int64_t bitPeriod = bitPeriodFs/sampleperiod; + + int64_t numBits = timeWindow / bitPeriodFs; + + wfm->Resize(numBits); + + int64_t t = 0; + + int64_t bitCount = 0; + + auto push = [&bitCount,bitPeriod,depth](SparseDigitalWaveform* w, int64_t time, bool v) + { + w->m_offsets.push_back(time); + w->m_durations.push_back(time + bitPeriod < (int64_t)depth ? bitPeriod : 0); + w->m_samples.push_back(v); + bitCount++; + }; + + while(bitCount < numBits) + { + push(wfm, t, false); + t += bitPeriod; + push(wfm, t, true); + t += bitPeriod; + } + + // Idle final + t += bitPeriod; + push(wfm, t, true); +} + +void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDigitalWaveform* sclk, SparseDigitalWaveform* mosi, int64_t sampleperiod, size_t depth) +{ + cs->m_triggerPhase = 0; + cs->m_timescale = sampleperiod; + sclk->m_triggerPhase = 0; + sclk->m_timescale = sampleperiod; + mosi->m_triggerPhase = 0; + mosi->m_timescale = sampleperiod; + + string msg = "Hello ngscopeclient from SPI !"; + + int64_t t = 0; + int64_t numBits = msg.size()*8 + 2; + const int64_t bitPeriod = depth/numBits; + const int64_t half = bitPeriod / 2; + + cs->Resize(numBits); + sclk->Resize(numBits); + mosi->Resize(numBits); + + auto push = [](SparseDigitalWaveform* w, int64_t time, int64_t duration, bool v) + { + w->m_offsets.push_back(time); + w->m_durations.push_back(duration); + w->m_samples.push_back(v); + }; + + // Idle state + push(cs, t, bitPeriod, true); + push(sclk, t, bitPeriod, false); + push(mosi, t, bitPeriod, false); + + t += bitPeriod; + + // Assert CS + push(cs, t, bitPeriod, false); + + for(uint8_t c : msg) + { + for(int i = 7; i >= 0; i--) + { + bool bit = (c >> i) & 1; + + // Data setup + push(mosi, t, bitPeriod, bit); + push(sclk, t, half, false); + + t += half; + + // Clock high (sampling edge) + push(sclk, t, half, true); + + t += half; + } + } + + // Deassert CS + t += bitPeriod; + push(cs, t, bitPeriod, true); + +} diff --git a/scopehal/TestDigitalWaveformSource.h b/scopehal/TestDigitalWaveformSource.h new file mode 100644 index 00000000..71455ef4 --- /dev/null +++ b/scopehal/TestDigitalWaveformSource.h @@ -0,0 +1,85 @@ +/*********************************************************************************************************************** +* * +* libscopehal * +* * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * +* All rights reserved. * +* * +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * +* following conditions are met: * +* * +* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products * +* derived from this software without specific prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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. * +* * +***********************************************************************************************************************/ + +/** + @file + @author Frederic BORRY + @brief Declaration of TestDigitalWaveformSource + @ingroup core + */ + +#ifndef TestDigitalWaveformSource_h +#define TestDigitalWaveformSource_h + +/** + @brief Helper class for generating test digital waveforms + + Used by DemoOscilloscope + + @ingroup core + */ +class TestDigitalWaveformSource +{ +public: + TestDigitalWaveformSource(); + virtual ~TestDigitalWaveformSource(); + + TestDigitalWaveformSource(const TestDigitalWaveformSource&) =delete; + TestDigitalWaveformSource& operator=(const TestDigitalWaveformSource&) =delete; + + void GenerateUART( + SparseDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate = 115200); + + void GenerateUART( + UniformDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate = 115200); + + void GenerateUARTClock( + SparseDigitalWaveform* wfm, + int64_t sampleperiod, + size_t depth, + int64_t baudrate = 115200); + + void GenerateSPI( + SparseDigitalWaveform* cs, + SparseDigitalWaveform* sclk, + SparseDigitalWaveform* mosi, + int64_t sampleperiod, + size_t depth); + +protected: + +}; + +#endif From 0bddf667871e37975705ce40381442c1c52ab8a6 Mon Sep 17 00:00:00 2001 From: fredzo Date: Tue, 3 Feb 2026 00:16:23 +0100 Subject: [PATCH 2/4] Added parallel bus to demo oscilloscope. --- scopehal/DemoOscilloscope.cpp | 96 ++++++++++++++++++++++---- scopehal/TestDigitalWaveformSource.cpp | 84 ++++++++++++++++++---- scopehal/TestDigitalWaveformSource.h | 4 ++ 3 files changed, 160 insertions(+), 24 deletions(-) diff --git a/scopehal/DemoOscilloscope.cpp b/scopehal/DemoOscilloscope.cpp index a6aa0e06..f530ea74 100644 --- a/scopehal/DemoOscilloscope.cpp +++ b/scopehal/DemoOscilloscope.cpp @@ -129,14 +129,16 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport) m_channels[8]->SetDisplayName("UART-0-Clk"); m_channels[9]->SetDisplayName("UART-1"); m_channels[10]->SetDisplayName("UART-1-Clk"); - m_channels[11]->SetDisplayName("UART-2"); - m_channels[12]->SetDisplayName("UART-2-Clk"); - m_channels[13]->SetDisplayName("UART-3"); - m_channels[14]->SetDisplayName("UART-3-Clk"); - m_channels[15]->SetDisplayName("UART-4"); - m_channels[16]->SetDisplayName("UART-4-Clk"); - m_channels[17]->SetDisplayName("UART-5"); - m_channels[18]->SetDisplayName("UART-5-Clk"); + m_channels[11]->SetDisplayName("Parallel-Clk"); + + m_channels[12]->SetDisplayName("Parallel-0"); + m_channels[13]->SetDisplayName("Parallel-1"); + m_channels[14]->SetDisplayName("Parallel-2"); + m_channels[15]->SetDisplayName("Parallel-3"); + m_channels[16]->SetDisplayName("Parallel-4"); + m_channels[17]->SetDisplayName("Parallel-5"); + m_channels[18]->SetDisplayName("Parallel-6"); + m_channels[19]->SetDisplayName("Parallel-7"); //Create Vulkan objects for the waveform conversion for(int i=0; i<4; i++) @@ -644,17 +646,75 @@ bool DemoOscilloscope::AcquireData() ChannelsDownloadStatusUpdate(i, InstrumentChannel::DownloadState::DOWNLOAD_FINISHED, 1.0); } + bool spiEnabled = m_channelsEnabled[4]||m_channelsEnabled[5]||m_channelsEnabled[6]; + bool parallelEnabled = false; + for(int i=11; i<=19; i++) + { + if(m_channelsEnabled[i]) + { + parallelEnabled = true; + break; + } + } + + // Prepare SPI data - auto cs = new SparseDigitalWaveform("CS"); - auto sclk = new SparseDigitalWaveform("SCLK"); - auto mosi = new SparseDigitalWaveform("MOSI"); + SparseDigitalWaveform* cs = nullptr; + SparseDigitalWaveform* sclk = nullptr; + SparseDigitalWaveform* mosi = nullptr; + if(spiEnabled) + { + cs = new SparseDigitalWaveform("CS"); + sclk = new SparseDigitalWaveform("SCLK"); + mosi = new SparseDigitalWaveform("MOSI"); + m_digitalSource->GenerateSPI(cs,sclk,mosi,sampleperiod, depth); + } - m_digitalSource->GenerateSPI(cs,sclk,mosi,sampleperiod, depth); + std::vector parallelWfms; + if(parallelEnabled) + { // Prepare Parallel bus data + auto wfClk = new SparseDigitalWaveform("Parallel-Clk"); + parallelWfms.push_back(wfClk); + // Parallel lines waveforms + for(int i = 0 ; i < 8 ; i++) + { + auto wf = new SparseDigitalWaveform("Parallel-"+to_string(i)); + parallelWfms.push_back(wf); + } + m_digitalSource->GenerateParallel(parallelWfms,sampleperiod,depth); + } for(int i=4; i<(4+16); i++) { if(!m_channelsEnabled[i]) + { + switch(i) + { + case 4: + if(spiEnabled) delete cs; + break; + case 5: + if(spiEnabled) delete sclk; + break; + case 6: + if(spiEnabled) delete mosi; + break; + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + // Paralle bus + if(parallelEnabled) delete parallelWfms[i-11]; + break; + } + continue; + } // Lambda passed to generate waveform methods to update "download" percentage ChannelsDownloadStatusUpdate(i, InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS, 0.0); @@ -669,6 +729,18 @@ bool DemoOscilloscope::AcquireData() case 6: waveforms[i] = mosi; break; + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + // Paralle bus + waveforms[i] = parallelWfms[i-11]; + break; default: if(i%2 == 1) { diff --git a/scopehal/TestDigitalWaveformSource.cpp b/scopehal/TestDigitalWaveformSource.cpp index dd25a160..767c6d89 100644 --- a/scopehal/TestDigitalWaveformSource.cpp +++ b/scopehal/TestDigitalWaveformSource.cpp @@ -81,6 +81,7 @@ void TestDigitalWaveformSource::GenerateUART( int64_t numBits = timeWindow / bitPeriodFs; + wfm->clear(); wfm->Resize(numBits); int64_t currentTime = 0; @@ -100,7 +101,7 @@ void TestDigitalWaveformSource::GenerateUART( push(wfm, currentTime, true); currentTime += bitPeriod; - string msg = "Hello World from ngscopeclient UART !"; + string msg = "Hello World from ngscopeclient UART !\n"; while(bitCount < numBits) { @@ -146,21 +147,16 @@ void TestDigitalWaveformSource::GenerateUART( wfm->m_triggerPhase = 0; wfm->m_timescale = sampleperiod; - int64_t timeWindow = depth * sampleperiod; - const int64_t bitPeriodFs = FS_PER_SECOND / baudrate; - int64_t numBits = timeWindow / bitPeriodFs; - - wfm->Resize(numBits); - - string msg = "Hello World from ngscopeclient UART uniform waveform !"; + string msg = "Hello World from ngscopeclient UART uniform waveform !\n"; const int64_t samplesPerBit = bitPeriodFs / sampleperiod; // Reqired bits const size_t totalSamples = depth; + wfm->clear(); wfm->Resize(totalSamples); size_t sample = 0; @@ -218,6 +214,7 @@ void TestDigitalWaveformSource::GenerateUARTClock( int64_t numBits = timeWindow / bitPeriodFs; + wfm->clear(); wfm->Resize(numBits); int64_t t = 0; @@ -254,15 +251,18 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig mosi->m_triggerPhase = 0; mosi->m_timescale = sampleperiod; - string msg = "Hello ngscopeclient from SPI !"; + string msg = "Hello ngscopeclient from SPI !\n"; int64_t t = 0; - int64_t numBits = msg.size()*8 + 2; + int64_t numBits = msg.size()*8 + 3; const int64_t bitPeriod = depth/numBits; const int64_t half = bitPeriod / 2; + cs->clear(); cs->Resize(numBits); - sclk->Resize(numBits); + sclk->clear(); + sclk->Resize(numBits*2); + mosi->clear(); mosi->Resize(numBits); auto push = [](SparseDigitalWaveform* w, int64_t time, int64_t duration, bool v) @@ -280,7 +280,7 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig t += bitPeriod; // Assert CS - push(cs, t, bitPeriod, false); + push(cs, t, bitPeriod*msg.size(), false); for(uint8_t c : msg) { @@ -302,7 +302,67 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig } // Deassert CS + push(cs, t, bitPeriod, true); + push(sclk, t, bitPeriod, false); + push(mosi, t, bitPeriod, false); t += bitPeriod; push(cs, t, bitPeriod, true); + push(sclk, t, bitPeriod, true); + push(mosi, t, bitPeriod, true); +} +void TestDigitalWaveformSource::GenerateParallel(std::vector &waveforms, int64_t sampleperiod, size_t depth) +{ + if(waveforms.size() != 9) + { + LogError("Invalid waveforms size: exected 9, found %zu.\n",waveforms.size()); + return; + } + string msg = "\x01\x02\x04\x08\x10\x20\x40\x80\xFFHello ngscopeclient from UART !"; + + int64_t t = 0; + int64_t numBits = msg.size(); + const int64_t bitPeriod = depth/numBits; + const int64_t half = bitPeriod / 2; + + // Clock WF + auto wfClk = waveforms[0]; + wfClk->m_triggerPhase = 0; + wfClk->m_timescale = sampleperiod; + wfClk->clear(); + wfClk->Resize(2*numBits); + // Parallel lines waveforms + for(int i = 0 ; i < 8 ; i++) + { + auto wf = waveforms[i+1]; + wf->m_triggerPhase = 0; + wf->m_timescale = sampleperiod; + wf->clear(); + wf->Resize(numBits); + } + + auto push = [](SparseDigitalWaveform* w, int64_t time, int64_t duration, bool v) + { + w->m_offsets.push_back(time); + w->m_durations.push_back(duration); + w->m_samples.push_back(v); + }; + + for(uint8_t c : msg) + { + for(int i = 0; i < 8; i++) + { + bool bit = (c >> i) & 1; + // Push data lines + push(waveforms[i+1], t, bitPeriod, bit); + } + push(wfClk, t, half, false); + + t += half; + + // Clock high (sampling edge) + push(wfClk, t, half, true); + + t += half; + } } diff --git a/scopehal/TestDigitalWaveformSource.h b/scopehal/TestDigitalWaveformSource.h index 71455ef4..32708e70 100644 --- a/scopehal/TestDigitalWaveformSource.h +++ b/scopehal/TestDigitalWaveformSource.h @@ -78,6 +78,10 @@ class TestDigitalWaveformSource int64_t sampleperiod, size_t depth); + void GenerateParallel(std::vector &waveforms, + int64_t sampleperiod, + size_t depth); + protected: }; From 0cd1714cc799993b55b2a5567cf832d03771dda5 Mon Sep 17 00:00:00 2001 From: fredzo Date: Tue, 3 Feb 2026 15:00:43 +0100 Subject: [PATCH 3/4] Code cleanup. --- scopehal/DemoOscilloscope.cpp | 14 +- scopehal/TestDigitalWaveformSource.cpp | 177 ++++++++++--------------- scopehal/TestDigitalWaveformSource.h | 6 - 3 files changed, 76 insertions(+), 121 deletions(-) diff --git a/scopehal/DemoOscilloscope.cpp b/scopehal/DemoOscilloscope.cpp index f530ea74..d4f1cb13 100644 --- a/scopehal/DemoOscilloscope.cpp +++ b/scopehal/DemoOscilloscope.cpp @@ -664,21 +664,21 @@ bool DemoOscilloscope::AcquireData() SparseDigitalWaveform* mosi = nullptr; if(spiEnabled) { - cs = new SparseDigitalWaveform("CS"); - sclk = new SparseDigitalWaveform("SCLK"); - mosi = new SparseDigitalWaveform("MOSI"); + cs = AllocateDigitalWaveform("CS"); + sclk = AllocateDigitalWaveform("SCLK"); + mosi = AllocateDigitalWaveform("MOSI"); m_digitalSource->GenerateSPI(cs,sclk,mosi,sampleperiod, depth); } std::vector parallelWfms; if(parallelEnabled) { // Prepare Parallel bus data - auto wfClk = new SparseDigitalWaveform("Parallel-Clk"); + auto wfClk = AllocateDigitalWaveform("Parallel-Clk"); parallelWfms.push_back(wfClk); // Parallel lines waveforms for(int i = 0 ; i < 8 ; i++) { - auto wf = new SparseDigitalWaveform("Parallel-"+to_string(i)); + auto wf = AllocateDigitalWaveform("Parallel-"+to_string(i)); parallelWfms.push_back(wf); } m_digitalSource->GenerateParallel(parallelWfms,sampleperiod,depth); @@ -744,13 +744,13 @@ bool DemoOscilloscope::AcquireData() default: if(i%2 == 1) { - auto wfm = new SparseDigitalWaveform("UART"); + auto wfm = AllocateDigitalWaveform("UART"); waveforms[i] = wfm; m_digitalSource->GenerateUART(wfm, sampleperiod, depth); } else { - auto wfm = new SparseDigitalWaveform("UART-Clk"); + auto wfm = AllocateDigitalWaveform("UART-Clk"); waveforms[i] = wfm; m_digitalSource->GenerateUARTClock(wfm, sampleperiod, depth); } diff --git a/scopehal/TestDigitalWaveformSource.cpp b/scopehal/TestDigitalWaveformSource.cpp index 767c6d89..f808dea7 100644 --- a/scopehal/TestDigitalWaveformSource.cpp +++ b/scopehal/TestDigitalWaveformSource.cpp @@ -71,7 +71,7 @@ void TestDigitalWaveformSource::GenerateUART( size_t depth, int64_t baudrate) { - wfm->m_triggerPhase = 0; + wfm->PrepareForCpuAccess(); wfm->m_timescale = sampleperiod; int64_t timeWindow = depth * sampleperiod; @@ -81,25 +81,25 @@ void TestDigitalWaveformSource::GenerateUART( int64_t numBits = timeWindow / bitPeriodFs; - wfm->clear(); - wfm->Resize(numBits); + wfm->Resize(numBits+1); int64_t currentTime = 0; int64_t bitCount = 0; - auto push = [&bitCount,bitPeriod,numBits,depth] (SparseDigitalWaveform* w, int64_t time, bool v) -> bool + auto push = [&bitCount,bitPeriod,numBits,depth,¤tTime] (SparseDigitalWaveform* w, bool v) -> bool { - w->m_offsets.push_back(time); - w->m_durations.push_back(time + bitPeriod < (int64_t)depth ? bitPeriod : depth - time); + w->m_offsets.push_back(currentTime); + int64_t duration = currentTime + bitPeriod < (int64_t)depth ? bitPeriod : depth - currentTime; + w->m_durations.push_back(duration); w->m_samples.push_back(v); + currentTime += duration; bitCount++; return bitCount > numBits+1; }; // Idle initial - push(wfm, currentTime, true); - currentTime += bitPeriod; + push(wfm, true); string msg = "Hello World from ngscopeclient UART !\n"; @@ -108,86 +108,27 @@ void TestDigitalWaveformSource::GenerateUART( for(uint8_t c : msg) { // Start bit - if(push(wfm, currentTime, false)) return; - currentTime += bitPeriod; + if(push(wfm, false)) break; // Data bits (LSB first) for(int i = 0; i < 8; i++) { bool bit = (c >> i) & 1; - if(push(wfm, currentTime, bit)) return; - currentTime += bitPeriod; + if(push(wfm, bit)) break; } // Stop bit - if(push(wfm, currentTime, true)) return; - currentTime += bitPeriod; + if(push(wfm, true)) break; } } // Idle final - currentTime += bitPeriod; - push(wfm, currentTime, true); -} - -/** - @brief Generates a UART waveform using UniformDigitalWaveform - - @param wfm Waveform to fill - @param sampleperiod Interval between samples, in femtoseconds - @param depth Total number of samples to generate - @param baudrate The baudrate of the UART link - */ -void TestDigitalWaveformSource::GenerateUART( - UniformDigitalWaveform* wfm, - int64_t sampleperiod, - size_t depth, - int64_t baudrate) -{ - wfm->m_triggerPhase = 0; - wfm->m_timescale = sampleperiod; - - const int64_t bitPeriodFs = FS_PER_SECOND / baudrate; - - string msg = "Hello World from ngscopeclient UART uniform waveform !\n"; - - const int64_t samplesPerBit = bitPeriodFs / sampleperiod; - - // Reqired bits - const size_t totalSamples = depth; - - wfm->clear(); - wfm->Resize(totalSamples); - - size_t sample = 0; - - auto emitBit = [&](bool level) - { - for(int64_t i = 0; (i < samplesPerBit && sample < totalSamples); i++) - wfm->m_samples[sample++] = level; - }; - - // Idle initial - emitBit(true); - - while(sample < totalSamples) - { - for(uint8_t c : msg) - { - // Start bit - emitBit(false); - - // Data bits (LSB first) - for(int i = 0; i < 8; i++) - emitBit((c >> i) & 1); + wfm->m_offsets.push_back(currentTime); + wfm->m_durations.push_back(1); + wfm->m_samples.push_back(true); - // Stop bit - emitBit(true); - } - } - - // Idle final - emitBit(true); + wfm->MarkSamplesModifiedFromCpu(); + wfm->MarkTimestampsModifiedFromCpu(); } /** @@ -204,7 +145,7 @@ void TestDigitalWaveformSource::GenerateUARTClock( size_t depth, int64_t baudrate) { - wfm->m_triggerPhase = 0; + wfm->PrepareForCpuAccess(); wfm->m_timescale = sampleperiod; int64_t timeWindow = depth * sampleperiod; @@ -214,56 +155,56 @@ void TestDigitalWaveformSource::GenerateUARTClock( int64_t numBits = timeWindow / bitPeriodFs; - wfm->clear(); - wfm->Resize(numBits); + wfm->Resize(numBits+1); - int64_t t = 0; + int64_t currentTime = 0; int64_t bitCount = 0; - auto push = [&bitCount,bitPeriod,depth](SparseDigitalWaveform* w, int64_t time, bool v) + auto push = [&bitCount,bitPeriod,depth,¤tTime](SparseDigitalWaveform* w, bool v) { - w->m_offsets.push_back(time); - w->m_durations.push_back(time + bitPeriod < (int64_t)depth ? bitPeriod : 0); + w->m_offsets.push_back(currentTime); + int64_t duration = currentTime + bitPeriod < (int64_t)depth ? bitPeriod : depth - currentTime; + w->m_durations.push_back(duration); w->m_samples.push_back(v); + currentTime+=duration; bitCount++; }; while(bitCount < numBits) { - push(wfm, t, false); - t += bitPeriod; - push(wfm, t, true); - t += bitPeriod; + push(wfm, false); + push(wfm, true); } // Idle final - t += bitPeriod; - push(wfm, t, true); + wfm->m_offsets.push_back(depth); + wfm->m_durations.push_back(1); + wfm->m_samples.push_back(true); + + wfm->MarkSamplesModifiedFromCpu(); + wfm->MarkTimestampsModifiedFromCpu(); } void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDigitalWaveform* sclk, SparseDigitalWaveform* mosi, int64_t sampleperiod, size_t depth) { - cs->m_triggerPhase = 0; + cs->PrepareForCpuAccess(); cs->m_timescale = sampleperiod; - sclk->m_triggerPhase = 0; + sclk->PrepareForCpuAccess(); sclk->m_timescale = sampleperiod; - mosi->m_triggerPhase = 0; + mosi->PrepareForCpuAccess(); mosi->m_timescale = sampleperiod; string msg = "Hello ngscopeclient from SPI !\n"; int64_t t = 0; - int64_t numBits = msg.size()*8 + 3; + int64_t numBits = msg.size()*8 + 4; const int64_t bitPeriod = depth/numBits; const int64_t half = bitPeriod / 2; - cs->clear(); - cs->Resize(numBits); - sclk->clear(); - sclk->Resize(numBits*2); - mosi->clear(); - mosi->Resize(numBits); + cs->Resize(numBits+1); + sclk->Resize(numBits*2+1); + mosi->Resize(numBits+1); auto push = [](SparseDigitalWaveform* w, int64_t time, int64_t duration, bool v) { @@ -277,7 +218,7 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig push(sclk, t, bitPeriod, false); push(mosi, t, bitPeriod, false); - t += bitPeriod; + t += (3*bitPeriod); // Assert CS push(cs, t, bitPeriod*msg.size(), false); @@ -306,9 +247,17 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig push(sclk, t, bitPeriod, false); push(mosi, t, bitPeriod, false); t += bitPeriod; - push(cs, t, bitPeriod, true); - push(sclk, t, bitPeriod, true); - push(mosi, t, bitPeriod, true); + // Finale Sample + push(cs, t, 1, true); + push(sclk, t, 1, false); + push(mosi, t, 1, false); + + cs->MarkSamplesModifiedFromCpu(); + cs->MarkTimestampsModifiedFromCpu(); + sclk->MarkSamplesModifiedFromCpu(); + sclk->MarkTimestampsModifiedFromCpu(); + mosi->MarkSamplesModifiedFromCpu(); + mosi->MarkTimestampsModifiedFromCpu(); } void TestDigitalWaveformSource::GenerateParallel(std::vector &waveforms, int64_t sampleperiod, size_t depth) @@ -327,18 +276,16 @@ void TestDigitalWaveformSource::GenerateParallel(std::vectorm_triggerPhase = 0; + wfClk->PrepareForCpuAccess(); wfClk->m_timescale = sampleperiod; - wfClk->clear(); - wfClk->Resize(2*numBits); + wfClk->Resize(2*numBits+1); // Parallel lines waveforms for(int i = 0 ; i < 8 ; i++) { auto wf = waveforms[i+1]; - wf->m_triggerPhase = 0; + wf->PrepareForCpuAccess(); wf->m_timescale = sampleperiod; - wf->clear(); - wf->Resize(numBits); + wf->Resize(numBits+1); } auto push = [](SparseDigitalWaveform* w, int64_t time, int64_t duration, bool v) @@ -365,4 +312,18 @@ void TestDigitalWaveformSource::GenerateParallel(std::vectorMarkSamplesModifiedFromCpu(); + wf->MarkTimestampsModifiedFromCpu(); + } } diff --git a/scopehal/TestDigitalWaveformSource.h b/scopehal/TestDigitalWaveformSource.h index 32708e70..4de8ff8d 100644 --- a/scopehal/TestDigitalWaveformSource.h +++ b/scopehal/TestDigitalWaveformSource.h @@ -59,12 +59,6 @@ class TestDigitalWaveformSource size_t depth, int64_t baudrate = 115200); - void GenerateUART( - UniformDigitalWaveform* wfm, - int64_t sampleperiod, - size_t depth, - int64_t baudrate = 115200); - void GenerateUARTClock( SparseDigitalWaveform* wfm, int64_t sampleperiod, From e2a3c7139a3306611ae28fb001b8ee5547636efa Mon Sep 17 00:00:00 2001 From: fredzo Date: Tue, 3 Feb 2026 22:26:36 +0100 Subject: [PATCH 4/4] Fixed SPI start bit. --- scopehal/TestDigitalWaveformSource.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scopehal/TestDigitalWaveformSource.cpp b/scopehal/TestDigitalWaveformSource.cpp index f808dea7..c589f247 100644 --- a/scopehal/TestDigitalWaveformSource.cpp +++ b/scopehal/TestDigitalWaveformSource.cpp @@ -214,9 +214,9 @@ void TestDigitalWaveformSource::GenerateSPI(SparseDigitalWaveform* cs, SparseDig }; // Idle state - push(cs, t, bitPeriod, true); - push(sclk, t, bitPeriod, false); - push(mosi, t, bitPeriod, false); + push(cs, t, 3*bitPeriod, true); + push(sclk, t, 3*bitPeriod, false); + push(mosi, t, 3*bitPeriod, false); t += (3*bitPeriod);