From f1a25460064af42004370b4a994074af251c42e9 Mon Sep 17 00:00:00 2001 From: Pascal Nasahl Date: Tue, 19 Mar 2024 14:26:32 +0000 Subject: [PATCH] [scope] Configure WaveRunner from config file This commit allows the user to configure the channels, timebase, and the trigger for a WaveRunner scope using the configuration files. When providing the config, the scope gets automatically configured allowing for reproducible capture runs. Signed-off-by: Pascal Nasahl --- capture/capture_aes.py | 3 + capture/capture_kmac.py | 3 + capture/capture_otbn.py | 23 ++++---- capture/capture_sha3.py | 3 + capture/scopes/scope.py | 24 +++++++- capture/scopes/waverunner/waverunner.py | 73 +++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 11 deletions(-) diff --git a/capture/capture_aes.py b/capture/capture_aes.py index 3a9a5ac9..6dd8b494 100755 --- a/capture/capture_aes.py +++ b/capture/capture_aes.py @@ -137,6 +137,9 @@ def setup(cfg: dict, project: Path): sparsing = cfg[scope_type].get("sparsing"), scope_gain = cfg[scope_type].get("scope_gain"), pll_frequency = cfg["target"]["pll_frequency"], + channel_configs = cfg[scope_type].get("channel_configs"), + trigger_config = cfg[scope_type].get("trigger_config"), + timebase_config = cfg[scope_type].get("timebase_config") ) scope = Scope(scope_cfg) diff --git a/capture/capture_kmac.py b/capture/capture_kmac.py index 7539f80a..cb14b8e2 100755 --- a/capture/capture_kmac.py +++ b/capture/capture_kmac.py @@ -141,6 +141,9 @@ def setup(cfg: dict, project: Path): sparsing = cfg[scope_type].get("sparsing"), scope_gain = cfg[scope_type].get("scope_gain"), pll_frequency = cfg["target"]["pll_frequency"], + channel_configs = cfg[scope_type].get("channel_configs"), + trigger_config = cfg[scope_type].get("trigger_config"), + timebase_config = cfg[scope_type].get("timebase_config") ) scope = Scope(scope_cfg) diff --git a/capture/capture_otbn.py b/capture/capture_otbn.py index c399d498..f592a63a 100755 --- a/capture/capture_otbn.py +++ b/capture/capture_otbn.py @@ -139,18 +139,21 @@ def setup(cfg: dict, project: Path): # Create scope config & setup scope. scope_cfg = ScopeConfig( - scope_type=scope_type, + scope_type = scope_type, batch_mode = batch, bit = cfg[scope_type].get("bit"), - acqu_channel=cfg[scope_type].get("channel"), - ip=cfg[scope_type].get("waverunner_ip"), - num_samples=cfg[scope_type]["num_samples"], - offset_samples=cfg[scope_type]["offset_samples"], - sampling_rate=cfg[scope_type].get("sampling_rate"), - num_segments=cfg[scope_type]["num_segments"], - sparsing=cfg[scope_type].get("sparsing"), - scope_gain=cfg[scope_type].get("scope_gain"), - pll_frequency=cfg["target"]["pll_frequency"], + acqu_channel = cfg[scope_type].get("channel"), + ip = cfg[scope_type].get("waverunner_ip"), + num_samples = cfg[scope_type]["num_samples"], + offset_samples = cfg[scope_type]["offset_samples"], + sampling_rate = cfg[scope_type].get("sampling_rate"), + num_segments = cfg[scope_type].get("num_segments"), + sparsing = cfg[scope_type].get("sparsing"), + scope_gain = cfg[scope_type].get("scope_gain"), + pll_frequency = cfg["target"]["pll_frequency"], + channel_configs = cfg[scope_type].get("channel_configs"), + trigger_config = cfg[scope_type].get("trigger_config"), + timebase_config = cfg[scope_type].get("timebase_config") ) scope = Scope(scope_cfg) diff --git a/capture/capture_sha3.py b/capture/capture_sha3.py index c7340d1f..175c1297 100755 --- a/capture/capture_sha3.py +++ b/capture/capture_sha3.py @@ -126,6 +126,9 @@ def setup(cfg: dict, project: Path): sparsing = cfg[scope_type].get("sparsing"), scope_gain = cfg[scope_type].get("scope_gain"), pll_frequency = cfg["target"]["pll_frequency"], + channel_configs = cfg[scope_type].get("channel_configs"), + trigger_config = cfg[scope_type].get("trigger_config"), + timebase_config = cfg[scope_type].get("timebase_config") ) scope = Scope(scope_cfg) diff --git a/capture/scopes/scope.py b/capture/scopes/scope.py index a6f86890..34add84a 100644 --- a/capture/scopes/scope.py +++ b/capture/scopes/scope.py @@ -8,7 +8,7 @@ from typing import Optional from scopes.chipwhisperer.husky import Husky -from scopes.waverunner.waverunner import WaveRunner +from scopes.waverunner.waverunner import Channel, Timebase, Trigger, WaveRunner @dataclass @@ -28,6 +28,9 @@ class ScopeConfig: scope_gain: Optional[float] = 1 pll_frequency: Optional[int] = 1 sampling_rate: Optional[int] = 0 + channel_configs: Optional[list] = None + trigger_config: Optional[str] = None + timebase_config: Optional[str] = None class Scope: @@ -90,6 +93,25 @@ def _init_scope(self): first_point = self.scope_cfg.offset_samples, acqu_channel = self.scope_cfg.acqu_channel ) + # If a user defined config is provided, configure the channel, + # timebase, and the trigger. + if self.scope_cfg.channel_configs is not None: + for channel_config in self.scope_cfg.channel_configs: + scope.configure_channel( + Channel(name = channel_config["name"], + trace_enable = channel_config["trace_enable"], + vdiv = channel_config["vdiv"], + offset = channel_config["offset"])) + if self.scope_cfg.timebase_config is not None: + scope.configure_timebase( + Timebase(tdiv = self.scope_cfg.timebase_config["tdiv"], + delay = self.scope_cfg.timebase_config["delay"])) + if self.scope_cfg.trigger_config is not None: + scope.configure_trigger( + Trigger(channel = self.scope_cfg.trigger_config["channel"], + edge = self.scope_cfg.trigger_config["edge"], + coupling = self.scope_cfg.trigger_config["coupling"], + level = self.scope_cfg.trigger_config["level"])) return scope else: raise RuntimeError("Error: No WaveRunner IP provided!") diff --git a/capture/scopes/waverunner/waverunner.py b/capture/scopes/waverunner/waverunner.py index 10416000..cd9d6841 100755 --- a/capture/scopes/waverunner/waverunner.py +++ b/capture/scopes/waverunner/waverunner.py @@ -5,11 +5,40 @@ """Support for capturing traces using LeCroy WaveRunner 9104.""" import re +from dataclasses import dataclass import numpy as np import vxi11 +@dataclass +class Channel: + """ Scope configuration for a channel. + """ + name: str + trace_enable: str + vdiv: str + offset: str + + +@dataclass +class Timebase: + """ Timebase configuration. + """ + tdiv: str + delay: str + + +@dataclass +class Trigger: + """ Trigger configuration. + """ + channel: str + edge: str + coupling: str + level: str + + class _Timeout: """Helper class for setting scoped timeout values.""" @@ -385,6 +414,50 @@ def _parse_waveform(self, data): waves = waves[:, 0:self.num_samples] return waves + def configure_channel(self, channel: Channel): + """Configures a channel on the scope. + """ + commands = [ + # Enable / disable visualization of trace. Disabling increases the + # capture rate. + f"{channel.name}:TRA {channel.trace_enable}", + # Time / division. + f"TDIV {channel.tdiv}", + # Trigger delay. + f"TRDL {channel.delay}", + # Volts / division. + f"{channel.name}:VDIV {channel.vdiv}", + # DC offset. + f"{channel.name}:OFST {channel.offset}", + ] + self._write(";".join(commands)) + + def configure_timebase(self, timebase: Timebase): + """Configures the timebase of the scope. + """ + commands = [ + # Time / division. + f"TDIV {timebase.tdiv}", + # Trigger delay. + f"TRDL {timebase.delay}", + ] + self._write(";".join(commands)) + + def configure_trigger(self, trigger: Trigger): + """Configures the trigger on the scope. + """ + commands = [ + # Select trigger: edge, channel 2, no hold-off. + "TRSE EDGE,SR,C2,HT,OFF", + # Rising edge. + f"{trigger.channel}:TRSL {trigger.edge}", + # DC coupling. + f"{trigger.channel}:TRCP {trigger.coupling}", + # Trigger level. + f"{trigger.channel}:TRLV {trigger.level}", + ] + self._write(";".join(commands)) + def capture_and_transfer_waves(self): """Waits until the acqu is complete and transfers waveforms.