175 lines
7.5 KiB
Python
175 lines
7.5 KiB
Python
# Проверка временных характеристик драйвера затвора по цифровым фронтам. Параметры профилей
|
|
# задаются в наносекундах; типичная задержка ACK используется для предупреждения, а
|
|
# нормированная длительность импульса — для PASS/FAIL. Этот адаптер переводит события
|
|
# libsigrokdecode в аннотации DSView; координаты выборок пересчитываются с учётом частоты
|
|
# захвата.
|
|
|
|
"""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))
|