From 3abe847cf8294f3b94141981e331d127ec722bc6 Mon Sep 17 00:00:00 2001 From: jpcrypt Date: Thu, 27 Jul 2023 11:17:10 -0400 Subject: [PATCH] Husky triggering updates. 1. Allow SAD to generate triggers when not armed (i.e. after the ADC capture is done). 2. Retrieve timestamped trigger times (when using multiple triggers). --- .../scopes/cwhardware/ChipWhispererExtra.py | 51 +++++++++++++++++++ .../cwhardware/ChipWhispererHuskyMisc.py | 4 +- .../scopes/cwhardware/ChipWhispererSAD.py | 21 +++++++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererExtra.py b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererExtra.py index c88d0e5a2..73bbee4c5 100644 --- a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererExtra.py +++ b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererExtra.py @@ -46,6 +46,8 @@ ADDR_IOREAD = 59 ADDR_EDGE_TRIGGER = 113 ADDR_SOFTPOWER_CONTROL = 115 +ADDR_NUM_TRIGGERS_STAT = 117 +ADDR_NUM_TRIGGERS_DATA = 118 # API aliases for the TIO settings _tio_alias = { @@ -1158,6 +1160,55 @@ def edges_seen(self): """ return int.from_bytes(self.cwe.oa.sendMessage(CODE_READ, ADDR_EDGE_TRIGGER, Validate=False, maxResp=2), byteorder='little') + def get_trigger_times(self): + """Retrieve the timestamped trigger times. + + When multiple triggers occur, the number of ADC clock cycles between + successive triggers is recorded. Up to 1024 triggers can be + timestamped. The counter for each timestamp is 32-bits wide; overflows + are noted. Recorded triggers are automatically reset when the scope is + armed. + + """ + if self._trigger_times_empty(): + return None + else: + if self._trigger_times_overflow(): + scope_logger.warning('Trigger times FIFO overflowed (too many triggers occured).') + self.cwe.oa.sendMessage(CODE_WRITE, ADDR_NUM_TRIGGERS_STAT, [1]) # clears the overflow flag + times = [] + while not self._trigger_times_empty(): + self.cwe.oa.sendMessage(CODE_WRITE, ADDR_NUM_TRIGGERS_DATA, [1]) + raw = int.from_bytes(self.cwe.oa.sendMessage(CODE_READ, ADDR_NUM_TRIGGERS_DATA, Validate=False, maxResp=4), byteorder='little') + if raw == 2**32-1 and len(times): # don't care about overflow on first entry + scope_logger.error('Trigger times counter overflowed (more than 2**32 cycles between successive triggers).') + else: + times.append(raw) + if self._trigger_times_underflow(): + scope_logger.error('Internal error: trigger times FIFO underflowed.') + return times[1:] + + + def _trigger_times_empty(self): + if self.cwe.oa.sendMessage(CODE_READ, ADDR_NUM_TRIGGERS_STAT, Validate=False, maxResp=1)[0] & 1: + return True + else: + return False + + def _trigger_times_overflow(self): + if self.cwe.oa.sendMessage(CODE_READ, ADDR_NUM_TRIGGERS_STAT, Validate=False, maxResp=1)[0] & 2: + return True + else: + return False + + def _trigger_times_underflow(self): + if self.cwe.oa.sendMessage(CODE_READ, ADDR_NUM_TRIGGERS_STAT, Validate=False, maxResp=1)[0] & 4: + return True + else: + return False + + + class SADTrigger(util.DisableNewAttr): diff --git a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererHuskyMisc.py b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererHuskyMisc.py index 16c5dd922..928e12c07 100644 --- a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererHuskyMisc.py +++ b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererHuskyMisc.py @@ -419,8 +419,8 @@ def fpga_mode(self): @fpga_mode.setter def fpga_mode(self, setting): - if not setting in range(0, 11): - raise ValueError("Must be integer in [0, 10]") + if not setting in range(0, 12): + raise ValueError("Must be integer in [0, 11]") else: self.oa.sendMessage(CODE_WRITE, ADDR_USERIO_DEBUG_SELECT, [setting]) diff --git a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererSAD.py b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererSAD.py index e3a253758..847b04dc4 100644 --- a/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererSAD.py +++ b/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererSAD.py @@ -45,6 +45,7 @@ ADDR_SAD_SHORT = 107 ADDR_SAD_REF_BASE = 108 ADDR_SAD_VERSION = 114 +ADDR_SAD_ALWAYS_ARMED = 116 CODE_READ = 0x80 CODE_WRITE = 0xC0 @@ -258,6 +259,7 @@ def _dict_repr(self): rtn['half_pattern'] = self.half_pattern rtn['multiple_triggers'] = self.multiple_triggers rtn['num_triggers_seen'] = self.num_triggers_seen + rtn['always_armed'] = self.always_armed return rtn def __repr__(self): @@ -468,8 +470,7 @@ def triggered(self, val): @property def num_triggers_seen(self): - """SAD module status. Intended for debugging. - Indicates how many triggers were generated by the SAD module; + """ Indicates how many triggers were generated by the SAD module; gets cleared by setting this (or triggered) to any value or by re-arming. """ @@ -480,3 +481,19 @@ def num_triggers_seen(self): def num_triggers_seen(self, val): self.triggered = val + @property + def always_armed(self): + """ Control whether the SAD module can trigger without the scope being + armed. The intended use is to allow the SAD module to trigger after + the ADC capture has completed, which can be very useful when + calibrating the SAD threshold when using multiple SAD triggers. + """ + if self.oa.sendMessage(CODE_READ, ADDR_SAD_ALWAYS_ARMED, Validate=False, maxResp=1)[0]: + return True + else: + return False + + @always_armed.setter + def always_armed(self, val): + self.oa.sendMessage(CODE_WRITE, ADDR_SAD_ALWAYS_ARMED, [val]) +