Add DSLogic capture, signal conversion and DSView decoders

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent cc22c803d1
commit 513e79b127
44 changed files with 5982 additions and 36 deletions

View File

@@ -0,0 +1,60 @@
# 1SP0635 / 1SD536F2 timing decoder for DSView
This decoder checks driver control and status responses:
- `Vin1`: optional control sent to the first gate driver (legacy channel ID `vin`);
- `Vstat`: edge acknowledgement and fault feedback returned by the driver.
Built-in data-sheet profiles:
| Profile | ACK delay (typ.) | ACK width (specified) | Long pulse |
|---|---:|---:|---:|
| 1SP0635 | 250 ns | 400...1050 ns | fault above 1.5 us |
| 1SD536F2 | 380 ns | 600...1800 ns | fault above 1.8 us* |
`*` The 1.8 us fault threshold is a project classification choice based on the
data-sheet ACK maximum, not a verified universal manufacturer fault threshold.
The previously stated 1.5 us manual threshold could not be verified.
Correlated pulses within the ACK width limits pass. Uncorrelated pulses up to
the fault threshold are warnings; pulses above it are faults. Width and fault
annotations are emitted only when the pulse ends.
The data sheets specify only a *typical* ACK delay, not minimum and maximum
limits. DSView therefore emits a warning outside the configurable tolerance
(default +/-100 ns). The ACK-width check uses the published min/max values.
## Use in SETGUI
The DSView build recipe packages this adapter together with the canonical
`python/logic_analyzer/decoders/gate_timing.py` core. In the DSView tab,
choose **Анализаторы SET → IGBT · 1SP0635 / 1SD536F2** (or search Decode for
`IGBT 1SP/1SD`). Assign Vstat and, if available, Vin1, choose the driver profile, and set `Vstat acknowledge/fault level`
to match the receiver circuit. The diagrams in the application manual show
the logical event; some optical receiver circuits invert it.
Use at least 50 MS/s; 100 MS/s or more is recommended. Never connect a DSLogic
digital input directly to the IGBT gate (+15/-10 V) or to a power-stage node.
Use the isolated optical receiver logic signals and a common logic ground.
## Separate transistor pair analyzer
This decoder analyzes only Vin1/Vstat driver responses. Vin1 is optional;
Vstat alone retains pulse/fault classification, while Vin1 enables ACK timing
and full-cycle results. Vstat is required. The channel ID `vin` remains stable.
Pair analysis was moved to **Transistor pair** in the **Пользовательские**
section. Add that decoder separately and assign Vin1/Vin2. Old pair settings
on IGBT instances must be recreated in the new decoder. The pair checkbox,
Vin2 assignment and pulse limits are no longer part of the driver decoder.
## Full pulse cycle result
The **Vin1 / ACK full cycle** waveform row displays OK, WARNING or FAULT
across each complete sequence **Vin1 ON -> ACK -> Vin1 OFF -> ACK**.
The annotation starts at Vin1 ON and ends at the end of the OFF acknowledgment.
It is emitted only after both ACKs have been classified. A delay warning makes
that cycle WARNING; missing ACK, invalid width or fault feedback makes it FAULT.
A later normal cycle can be OK independently of previous cycles.
Unfinished cycles are never marked OK. Active-low Vin1 is supported.
The results panel retains its original layout without a large summary banner.
Deadtime and mintime are provided by the separate Transistor pair analyzer.

View File

@@ -0,0 +1,3 @@
"""DSView protocol decoder for SCALE gate-driver timing checks."""
from .pd import Decoder

View File

@@ -0,0 +1,168 @@
"""DSView/libsigrokdecode front end for SCALE gate-driver timing checks."""
import sigrokdecode as srd
from common.setgui_decoders.gate_timing import TimingChecker
class SamplerateError(Exception):
pass
class Ann(object):
CONTROL = 0
DELAY_OK = 1
WIDTH_OK = 2
WARNING = 3
ERROR = 4
FAULT = 5
CYCLE_OK = 6
CYCLE_WARNING = 7
CYCLE_FAULT = 8
ANN_FOR_KIND = {
'control': Ann.CONTROL,
'delay_ok': Ann.DELAY_OK,
'width_ok': Ann.WIDTH_OK,
'delay_warn': Ann.WARNING,
'width_fail': Ann.ERROR,
'missing': Ann.ERROR,
'orphan': Ann.WARNING,
'fault': Ann.FAULT,
'cycle_ok': Ann.CYCLE_OK,
'cycle_warning': Ann.CYCLE_WARNING,
'cycle_fault': Ann.CYCLE_FAULT,
}
class Decoder(srd.Decoder):
api_version = 3
id = 'gate_driver_timing'
name = 'IGBT 1SP/1SD'
longname = 'Power Integrations 1SP0635 / 1SD536F2 timing checker'
desc = 'Checks driver Vin1/Vstat ACK timing, faults and full pulse cycles.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Clock/timing', 'Util']
channels = ()
optional_channels = (
{'id': 'vin', 'name': 'Vin1', 'desc': 'First driver control input'},
{'id': 'vstat', 'name': 'Vstat', 'desc': 'Driver status/acknowledge output'},
)
options = (
{'id': 'profile', 'desc': 'Driver profile', 'default': '1SP0635',
'values': ('1SP0635', '1SD536F2', 'custom')},
{'id': 'vin_active', 'desc': 'Vin1 active level', 'default': 'high',
'values': ('high', 'low')},
{'id': 'vstat_active', 'desc': 'Vstat acknowledge/fault level', 'default': 'high',
'values': ('high', 'low')},
{'id': 'delay_tolerance_ns',
'desc': 'Warning tolerance around typical ACK delay (ns)', 'default': 100},
{'id': 'orphan_min_width_ns',
'desc': 'ORPHAN minimum pulse width (ns, 0 = off)', 'default': 0},
{'id': 'custom_delay_ns', 'desc': 'Custom typical ACK delay (ns)', 'default': 250},
{'id': 'custom_width_min_ns', 'desc': 'Custom ACK width minimum (ns)', 'default': 400},
{'id': 'custom_width_typ_ns', 'desc': 'Custom ACK width typical (ns)', 'default': 700},
{'id': 'custom_width_max_ns', 'desc': 'Custom ACK width maximum (ns)', 'default': 1050},
{'id': 'custom_fault_ns', 'desc': 'Custom fault pulse threshold (ns)', 'default': 1500},
)
annotations = (
('control', 'Vin1 edge'),
('delay-ok', 'ACK delay pass'),
('width-ok', 'ACK width pass'),
('warning', 'Warning'),
('error', 'Timing failure'),
('fault', 'Driver fault'),
('cycle-ok', 'Cycle OK'),
('cycle-warning', 'Cycle WARNING'),
('cycle-fault', 'Cycle FAULT'),
)
annotation_rows = (
('cycle', 'Vin1 / ACK full cycle', (Ann.CYCLE_OK, Ann.CYCLE_WARNING, Ann.CYCLE_FAULT)),
('vin', 'Vin1', (Ann.CONTROL,)),
('measurements', 'Measurements', (Ann.DELAY_OK, Ann.WIDTH_OK)),
('checks', 'Warnings / failures', (Ann.WARNING, Ann.ERROR, Ann.FAULT)),
)
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.samplerate = None
self.checker = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Здесь регистрируются аннотации для дорожек измерений и предупреждений.
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
# Адаптер переводит настройки DSView в параметры общего TimingChecker.
# Пороговые алгоритмы остаются в templates/python/logic_analyzer/decoders.
def _make_checker(self):
custom = {
'ack_delay_typ_ns': float(self.options['custom_delay_ns']),
'ack_width_min_ns': float(self.options['custom_width_min_ns']),
'ack_width_typ_ns': float(self.options['custom_width_typ_ns']),
'ack_width_max_ns': float(self.options['custom_width_max_ns']),
'fault_threshold_ns': float(self.options['custom_fault_ns']),
}
return TimingChecker(
self.samplerate,
profile=self.options['profile'],
delay_tolerance_ns=float(self.options['delay_tolerance_ns']),
vin_active_high=self.options['vin_active'] == 'high',
vstat_active_high=self.options['vstat_active'] == 'high',
orphan_min_width_ns=self.options.get('orphan_min_width_ns', 0),
custom=custom, cycle_results=True)
def _put_events(self, events):
for event in events:
ann = ANN_FOR_KIND[event['kind']]
self.put(event['start'], max(event['start'], event['end']),
self.out_ann, [ann, [event['text'], event['short']]])
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
self.checker = self._make_checker()
has_vin1 = self.has_channel(0)
if not self.has_channel(1):
raise ValueError('Connect Vstat for driver response analysis.')
while True:
conditions = [{0: 'e'}, {1: 'e'}] if has_vin1 else [{1: 'e'}]
timer_bit = 1 << len(conditions)
deadline = self.checker.next_deadline()
if deadline is not None:
conditions.append({'skip': max(1, deadline - self.samplenum)})
# DSView mutates and returns the same native tuple on every wait.
# Release that tuple before waiting again (PyTuple_SetItem requires
# exclusive ownership); tuple(...) would not make a copy.
pins = list(self.wait(conditions))
vin, vstat = pins[:2]
now = self.samplenum
# Both channels may transition at the same sample; preserve both.
if has_vin1 and self.matched & 0b001:
self._put_events(self.checker.on_control_edge(now, vin))
if self.matched & (0b010 if has_vin1 else 0b001):
self._put_events(self.checker.on_status_edge(now, vstat))
if deadline is not None and self.matched & timer_bit:
self._put_events(self.checker.expire(now))
def end(self):
if self.checker is not None:
self._put_events(self.checker.finish_cycles(self.samplenum))