Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Burst measurement, CLI vcd export #113

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,16 @@ public class CLCaptureOptions

[Value(6, Required = true, HelpText = "Trigger definition in the form of \"TriggerType:(Edge, Fast or Complex),Channel:(base trigger channel),Value:(string containing 1's and 0's indicating each trigger chanel state)\".")]
public CLTrigger? Trigger { get; set; }

[Value(7, Required = true, HelpText = "Name of the output file.")]
public string? OutputFile { get; set; }

[Value(8, Required = false, HelpText = "Format of the output file. Can be csv|vcd or csv:vcd to export both")]
public string? OutputFileFormat { get; set; }

public bool ExportVCD => OutputFileFormat?.Contains("vcd") ?? false;

public bool ExportCSV => string.IsNullOrEmpty(OutputFileFormat) || OutputFileFormat.Contains("csv");
}

public class CLTrigger
Expand Down
124 changes: 104 additions & 20 deletions Software/LogicAnalyzer/CLCapture/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
using CommandLine;
using SharedDriver;
using System.IO.Ports;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;

Expand Down Expand Up @@ -159,6 +157,12 @@ async Task<int> Capture(CLCaptureOptions opts)
break;
}

if (string.IsNullOrWhiteSpace(opts.OutputFile))
{
Console.WriteLine("Output file not specified.");
return -1;
}

Console.WriteLine($"Opening logic analyzer in {opts.AddressPort}...");

try
Expand All @@ -171,7 +175,7 @@ async Task<int> Capture(CLCaptureOptions opts)
return -1;
}

Console.WriteLine($"Conneced to device {driver.DeviceVersion} in port/address {opts.AddressPort}");
Console.WriteLine($"Connected to device {driver.DeviceVersion} in port/address {opts.AddressPort}");

captureCompletedTask = new TaskCompletionSource<CaptureEventArgs>();

Expand All @@ -180,7 +184,7 @@ async Task<int> Capture(CLCaptureOptions opts)
if (opts.Trigger.TriggerType == CLTriggerType.Edge)
{
Console.WriteLine("Starting edge triggered capture...");
var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, opts.LoopCount < 2 ? 0 : opts.LoopCount - 1, nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);
var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, opts.LoopCount < 2 ? 0 : opts.LoopCount - 1, true, nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);

if (resStart != CaptureError.None)
{
Expand Down Expand Up @@ -252,22 +256,108 @@ async Task<int> Capture(CLCaptureOptions opts)
return -1;
}

Console.WriteLine("Capture complete, writting output file...");
Console.WriteLine("Capture complete, writing output file...");

if (opts.ExportVCD)
ExportVCD(nChannels, result.Samples, result.BurstTimestamps, opts);

if (opts.ExportCSV)
ExportCSV(channels, nChannels, result.Samples, opts.OutputFile);

Console.WriteLine("Done.");

return 1;
}

void ExportVCD(int[] channelIndices, UInt128[] samples, uint[] burstTimeStamps, CLCaptureOptions opts)
{
using var vcdFile = File.Create(Path.ChangeExtension(opts.OutputFile, "vcd"));
using var vcdWriter = new StreamWriter(vcdFile);

var file = File.Create(opts.OutputFile);
StreamWriter sw = new StreamWriter(file);
var vcdSb = new StringBuilder();

uint timeStep = 1000000000U / (uint)opts.SamplingFrequency;

var dateNowStr = DateTime.Now.ToString("ddd MMM MM HH:mm:ss yyyy", new System.Globalization.CultureInfo("en-us"));
vcdSb.Append($"$date {dateNowStr} $end\n");
vcdSb.Append($"$timescale {timeStep} ns $end\n");

char channelId = '!';
var channelAliases = new Dictionary<int, char>();
foreach (var channelIdx in channelIndices)
{
vcdSb.Append($"$var wire 1 {channelId} 0 $end\n");
channelAliases[channelIdx] = channelId;

channelId++;
}

vcdSb.Append("$enddefinitions $end\n\n");

char getChannelValue(UInt128 sample, int channelIdx)
{
return (sample & ((UInt128)1 << channelIdx)) == 0 ? '0' : '1';
}

UInt128? prevSample = null;
void appendSampleIfChanged(uint sampleIdx, UInt128 sample)
{
if (sample == prevSample)
return;

vcdSb.Append($"#{sampleIdx}");

foreach (var channelIdx in channelIndices)
{
var currentChannelValue = getChannelValue(sample, channelIdx);
if (!prevSample.HasValue || currentChannelValue != getChannelValue(prevSample.Value, channelIdx))
vcdSb.Append($" {currentChannelValue}{channelAliases[channelIdx]}");
}

vcdSb.Append('\n');
}

uint sampleIdxOffset = 0; // offset for a samples after the first burst, converted to index, since vcd format operates with indices and not time

for (uint sampleIdx = 0; sampleIdx < samples.Length; sampleIdx++)
{
if (sampleIdx >= opts.PreSamples)
{
var burstStartIdx = (sampleIdx - opts.PreSamples);

if (burstStartIdx > 0 && burstStartIdx % opts.PostSamples == 0) // first burst does not have an offset, all consequent - does
{
var burstIdx = burstStartIdx / opts.PostSamples - 1;
sampleIdxOffset += burstTimeStamps[burstIdx] / timeStep;
}
}

UInt128 sample = samples[sampleIdx];
appendSampleIfChanged(sampleIdx + sampleIdxOffset, sample);
prevSample = sample;
}

vcdWriter.Write(vcdSb.ToString());
vcdWriter.Flush();
vcdFile.Flush();
}

void ExportCSV(CLChannel[] channels, int[] channelIndices, UInt128[] samples, string outputFileName)
{
using var file = File.Create(Path.ChangeExtension(outputFileName, "csv"));
using var sw = new StreamWriter(file);

sw.WriteLine(String.Join(',', channels.Select(c => c.ChannelName).ToArray()));

StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();

for (int sample = 0; sample < result.Samples.Length; sample++)
for (int sample = 0; sample < samples.Length; sample++)
{
sb.Clear();

for (int buc = 0; buc < nChannels.Length; buc++)
for (int buc = 0; buc < channelIndices.Length; buc++)
{
if ((result.Samples[sample] & ((UInt128)1 << nChannels[buc])) == 0)
if ((samples[sample] & ((UInt128)1 << channelIndices[buc])) == 0)
sb.Append("0,");
else
sb.Append("1,");
Expand All @@ -276,14 +366,8 @@ async Task<int> Capture(CLCaptureOptions opts)
sw.WriteLine(sb.ToString());
}

sw.Close();
sw.Dispose();
file.Close();
file.Dispose();

Console.WriteLine("Done.");

return 1;
sw.Flush();
file.Flush();
}

int Configure(CLNetworkOptions opts)
Expand Down Expand Up @@ -334,7 +418,7 @@ int Configure(CLNetworkOptions opts)
return -1;
}

Console.WriteLine($"Conneced to device {driver.DeviceVersion} in port {opts.SerialPort}");
Console.WriteLine($"Connected to device {driver.DeviceVersion} in port {opts.SerialPort}");

if (driver.DeviceVersion == null || !driver.DeviceVersion.Contains("WIFI"))
{
Expand Down
49 changes: 49 additions & 0 deletions Software/LogicAnalyzer/LogicAnalyzer/Classes/BurstInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace LogicAnalyzer.Classes
{
public class BurstInfo
{
public int SampleNumber { get; set; }

public uint Nanoseconds { get; set; }

public string GetTime()
{
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler;
if (Nanoseconds < 1000.0)
{
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1);
defaultInterpolatedStringHandler.AppendFormatted<uint>(Nanoseconds);
defaultInterpolatedStringHandler.AppendLiteral(" ns");
return defaultInterpolatedStringHandler.ToStringAndClear();
}
if (Nanoseconds < 1000000.0)
{
double microseconds = Nanoseconds / 1000.0;
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1);
defaultInterpolatedStringHandler.AppendFormatted<double>(microseconds, "F3");
defaultInterpolatedStringHandler.AppendLiteral(" µs");
return defaultInterpolatedStringHandler.ToStringAndClear();
}
if (Nanoseconds < 1000000000.0)
{
double milliseconds = Nanoseconds / 1000000.0;
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1);
defaultInterpolatedStringHandler.AppendFormatted<double>(milliseconds, "F3");
defaultInterpolatedStringHandler.AppendLiteral(" ms");
return defaultInterpolatedStringHandler.ToStringAndClear();
}
double seconds = Nanoseconds / 1000000000.0;
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(2, 1);
defaultInterpolatedStringHandler.AppendFormatted<double>(seconds, "F3");
defaultInterpolatedStringHandler.AppendLiteral(" s");
return defaultInterpolatedStringHandler.ToStringAndClear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class CaptureSettings
public int PreTriggerSamples { get; set; }
public int PostTriggerSamples { get; set; }
public int LoopCount { get; set; }
public bool MeasureBursts { get; set; }
public CaptureChannel[] CaptureChannels { get; set; } = new CaptureChannel[0];
public int TriggerType { get; set; }
public int TriggerChannel { get; set; }
Expand Down
7 changes: 7 additions & 0 deletions Software/LogicAnalyzer/LogicAnalyzer/Classes/SampleRegion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,11 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
}
}
}

public class BurstGapRegion : SampleRegion
{
public int GapSamples { get; init; }

public int BurstDelaySamples { get; init; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ public int VisibleSamples
set { SetValue<IBrush>(BackgroundProperty, value); InvalidateVisual(); }
}

private BurstInfo[] bursts;
public BurstInfo[] Bursts
{
get => bursts;
set
{
bursts = value;
InvalidateVisual();
}
}

public event EventHandler<RegionEventArgs> RegionCreated;
public event EventHandler<RegionEventArgs> RegionDeleted;

Expand All @@ -73,6 +84,10 @@ public int VisibleSamples
bool samplesCopied = false;
bool updating = false;

private Color burstPenColor = Color.FromRgb(224, 175, 29);

private Color burstFillColor = Color.FromArgb(128, 224, 175, 29);

public SampleMarker()
{
InitializeComponent();
Expand Down Expand Up @@ -325,6 +340,32 @@ public override void Render(DrawingContext context)
}
}

if (this.bursts != null)
{
double burstWidth = 16.0;
foreach (var burstInfo in bursts)
{
double x4 = (burstInfo.SampleNumber - FirstSample) * sampleWidth - burstWidth / 2;
double x5 = (burstInfo.SampleNumber - FirstSample) * sampleWidth + burstWidth / 2;
double y5 = 0.0;
double y6 = Bounds.Height;
var container = new PathFigure
{
StartPoint = new(x4, y5)
};
container.Segments.Add(new LineSegment { Point = new Point(x5, y5) });
container.Segments.Add(new LineSegment { Point = new Point(x5, y6 / 2.0) });
container.Segments.Add(new LineSegment { Point = new Point(x4 + (x5 - x4) / 2.0, y6) });
container.Segments.Add(new LineSegment { Point = new Point(x4, y6 / 2.0) });
container.Segments.Add(new LineSegment { Point = new Point(x4, y5 / 2.0) });

container.IsClosed = true;
PathGeometry gContainer = new PathGeometry();
gContainer.Figures.Add(container);
context.DrawGeometry(GraphicObjectsCache.GetBrush(this.burstFillColor), GraphicObjectsCache.GetPen(this.burstPenColor, 1), gContainer);
}
}

base.Render(context);
}
}
Expand All @@ -348,6 +389,30 @@ protected override void OnPointerMoved(PointerEventArgs e)
}
else
{
if (bursts != null)
{
var burst = bursts.FirstOrDefault(b =>
{
double num = (b.SampleNumber - FirstSample) * sampleWidth;
double minX = num - 8.0;
double maxX = num + 8.0;
return pos.Position.X >= minX && pos.Position.X <= maxX;
});
if (burst != null)
{
string burstText = "Burst delay: " + burst.GetTime();
object tip = ToolTip.GetTip(this);
if (((tip != null) ? tip.ToString() : null) != burstText)
{
ToolTip.SetTip(this, burstText);
ToolTip.SetIsOpen(this, false);
ToolTip.SetShowDelay(this, 0);
ToolTip.SetIsOpen(this, true);
}
return;
}
}

if (ToolTip.GetTip(this)?.ToString() != ovrSample.ToString())
{
ToolTip.SetTip(this, ovrSample.ToString());
Expand Down
Loading