mirror of
https://github.com/jonathanhogg/scopething
synced 2025-07-14 03:02:09 +01:00
Something that looks like it'll do logic capture
This commit is contained in:
107
scope.py
107
scope.py
@ -103,23 +103,30 @@ class Scope(vm.VirtualMachine):
|
|||||||
async def capture(self, channels=['A'], trigger_channel=None, trigger_level=None, trigger_type='rising', hair_trigger=False,
|
async def capture(self, channels=['A'], trigger_channel=None, trigger_level=None, trigger_type='rising', hair_trigger=False,
|
||||||
period=1e-3, nsamples=1000, timeout=None, low=None, high=None, raw=False):
|
period=1e-3, nsamples=1000, timeout=None, low=None, high=None, raw=False):
|
||||||
channels = list(channels)
|
channels = list(channels)
|
||||||
if 'A' in channels and 'B' in channels:
|
analog_channels = [channel for channel in channels if channel in {'A', 'B'}]
|
||||||
nsamples_multiplier = 2
|
logic = 'L' in channels
|
||||||
dual = True
|
ticks = int(period / nsamples / self.capture_clock_period)
|
||||||
else:
|
for capture_mode in vm.CaptureModes:
|
||||||
nsamples_multiplier = 1
|
if capture_mode.analog_channels == len(analog_channels) and capture_mode.logic_channels == logic:
|
||||||
dual = False
|
if ticks in range(capture_mode.clock_low, capture_mode.clock_high + 1):
|
||||||
ticks = int(period / nsamples / nsamples_multiplier / self.capture_clock_period)
|
clock_scale = 1
|
||||||
for clock_mode in vm.ClockModes:
|
break
|
||||||
if clock_mode.dual == dual and ticks in range(clock_mode.clock_low, clock_mode.clock_high + 1):
|
elif capture_mode.clock_divide and ticks > capture_mode.clock_high:
|
||||||
break
|
for clock_scale in range(2, 1<<16):
|
||||||
|
test_ticks = int(period / nsamples / self.capture_clock_period / clock_scale)
|
||||||
|
if test_ticks in range(capture_mode.clock_low, capture_mode.clock_high + 1):
|
||||||
|
ticks = test_ticks
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f"Unsupported clock period: {ticks}")
|
raise RuntimeError(f"Unsupported clock period: {ticks}")
|
||||||
if clock_mode.clock_max is not None and ticks > clock_mode.clock_max:
|
if capture_mode.clock_max is not None and ticks > capture_mode.clock_max:
|
||||||
ticks = clock_mode.clock_max
|
ticks = capture_mode.clock_max
|
||||||
nsamples = int(round(period / ticks / nsamples_multiplier / self.capture_clock_period))
|
nsamples = int(round(period / ticks / self.capture_clock_period / clock_scale))
|
||||||
total_samples = nsamples * nsamples_multiplier
|
total_samples = nsamples*2 if logic else nsamples
|
||||||
buffer_width = self.capture_buffer_size // clock_mode.sample_width
|
buffer_width = self.capture_buffer_size // capture_mode.sample_width
|
||||||
assert total_samples <= buffer_width
|
assert total_samples <= buffer_width
|
||||||
|
|
||||||
if raw:
|
if raw:
|
||||||
@ -144,6 +151,8 @@ class Scope(vm.VirtualMachine):
|
|||||||
elif trigger_channel == 'B':
|
elif trigger_channel == 'B':
|
||||||
kitchen_sink_a = vm.KitchenSinkA.ChannelBComparatorEnable
|
kitchen_sink_a = vm.KitchenSinkA.ChannelBComparatorEnable
|
||||||
spock_option |= vm.SpockOption.TriggerSourceB
|
spock_option |= vm.SpockOption.TriggerSourceB
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"Cannot trigger on channel {trigger_channel}")
|
||||||
kitchen_sink_b = vm.KitchenSinkB.AnalogFilterEnable
|
kitchen_sink_b = vm.KitchenSinkB.AnalogFilterEnable
|
||||||
if self._generator_running:
|
if self._generator_running:
|
||||||
kitchen_sink_b |= vm.KitchenSinkB.WaveformGeneratorEnable
|
kitchen_sink_b |= vm.KitchenSinkB.WaveformGeneratorEnable
|
||||||
@ -157,16 +166,21 @@ class Scope(vm.VirtualMachine):
|
|||||||
analog_enable |= 1
|
analog_enable |= 1
|
||||||
if 'B' in channels:
|
if 'B' in channels:
|
||||||
analog_enable |= 2
|
analog_enable |= 2
|
||||||
|
logic_enable = 0
|
||||||
|
for channel in range(8):
|
||||||
|
if not ((channel == 4 and self._generator_running) or (channel == 6 and 'A' in channels) or (channel == 7 and 'B' in channels)):
|
||||||
|
logic_enable |= 1<<channel
|
||||||
|
|
||||||
async with self.transaction():
|
async with self.transaction():
|
||||||
await self.set_registers(TraceMode=clock_mode.TraceMode, BufferMode=clock_mode.BufferMode,
|
await self.set_registers(TraceMode=capture_mode.TraceMode, BufferMode=capture_mode.BufferMode,
|
||||||
SampleAddress=0, ClockTicks=ticks, ClockScale=1,
|
SampleAddress=0, ClockTicks=ticks, ClockScale=clock_scale,
|
||||||
TraceIntro=total_samples//2, TraceOutro=total_samples//2, TraceDelay=0,
|
TraceIntro=nsamples//2, TraceOutro=nsamples//2, TraceDelay=0,
|
||||||
Timeout=max(1, int(round((period*5 if timeout is None else timeout) / self.trigger_timeout_tick))),
|
Timeout=max(1, int(round((period*5 if timeout is None else timeout) / self.trigger_timeout_tick))),
|
||||||
TriggerMask=0x7f, TriggerLogic=0x80, TriggerLevel=trigger_level, SpockOption=spock_option,
|
TriggerMask=0x7f, TriggerLogic=0x80, TriggerLevel=trigger_level, SpockOption=spock_option,
|
||||||
TriggerIntro=trigger_intro, TriggerOutro=2 if hair_trigger else 4, Prelude=0,
|
TriggerIntro=trigger_intro, TriggerOutro=2 if hair_trigger else 4, Prelude=0,
|
||||||
ConverterLo=lo, ConverterHi=hi,
|
ConverterLo=lo, ConverterHi=hi,
|
||||||
KitchenSinkA=kitchen_sink_a, KitchenSinkB=kitchen_sink_b, AnalogEnable=analog_enable)
|
KitchenSinkA=kitchen_sink_a, KitchenSinkB=kitchen_sink_b,
|
||||||
|
AnalogEnable=analog_enable, DigitalEnable=logic_enable)
|
||||||
await self.issue_program_spock_registers()
|
await self.issue_program_spock_registers()
|
||||||
await self.issue_configure_device_hardware()
|
await self.issue_configure_device_hardware()
|
||||||
await self.issue_triggered_trace()
|
await self.issue_triggered_trace()
|
||||||
@ -174,25 +188,42 @@ class Scope(vm.VirtualMachine):
|
|||||||
code, timestamp = (int(x, 16) for x in await self.read_replies(2))
|
code, timestamp = (int(x, 16) for x in await self.read_replies(2))
|
||||||
if code != 2:
|
if code != 2:
|
||||||
break
|
break
|
||||||
address = int((await self.read_replies(1))[0], 16) // nsamples_multiplier
|
start_timestamp = timestamp - nsamples*ticks*clock_scale
|
||||||
|
if start_timestamp < 0:
|
||||||
|
start_timestamp += 1<<32
|
||||||
|
timestamp += 1<<32
|
||||||
|
address = int((await self.read_replies(1))[0], 16)
|
||||||
traces = DotDict()
|
traces = DotDict()
|
||||||
if 't' in channels:
|
for dump_channel, channel in enumerate(sorted(analog_channels)):
|
||||||
traces.t = [t*self.capture_clock_period for t in range(timestamp-total_samples*ticks, timestamp, ticks*nsamples_multiplier)]
|
asamples = nsamples // len(analog_channels)
|
||||||
channels.remove('t')
|
|
||||||
for dump_channel, channel in enumerate(sorted(channels)):
|
|
||||||
async with self.transaction():
|
async with self.transaction():
|
||||||
await self.set_registers(SampleAddress=(address - nsamples) * nsamples_multiplier % buffer_width,
|
await self.set_registers(SampleAddress=(address - total_samples) % buffer_width,
|
||||||
DumpMode=vm.DumpMode.Native if clock_mode.sample_width == 2 else vm.DumpMode.Raw,
|
DumpMode=vm.DumpMode.Native if capture_mode.sample_width == 2 else vm.DumpMode.Raw,
|
||||||
DumpChan=dump_channel, DumpCount=nsamples, DumpRepeat=1, DumpSend=1, DumpSkip=0)
|
DumpChan=dump_channel, DumpCount=asamples, DumpRepeat=1, DumpSend=1, DumpSkip=0)
|
||||||
await self.issue_program_spock_registers()
|
await self.issue_program_spock_registers()
|
||||||
await self.issue_analog_dump_binary()
|
await self.issue_analog_dump_binary()
|
||||||
data = await self._reader.readexactly(nsamples * clock_mode.sample_width)
|
data = await self._reader.readexactly(asamples * capture_mode.sample_width)
|
||||||
value_multiplier, value_offset = (1, 0) if raw else ((high-low), low+self.analog_offsets[channel])
|
value_multiplier, value_offset = (1, 0) if raw else ((high-low), low+self.analog_offsets[channel])
|
||||||
if clock_mode.sample_width == 2:
|
if capture_mode.sample_width == 2:
|
||||||
data = struct.unpack(f'>{nsamples}h', data)
|
data = struct.unpack(f'>{asamples}h', data)
|
||||||
traces[channel] = [(value/65536+0.5)*value_multiplier + value_offset for value in data]
|
data = [(value/65536+0.5)*value_multiplier + value_offset for value in data]
|
||||||
else:
|
else:
|
||||||
traces[channel] = [(value/256)*value_multiplier + value_offset for value in data]
|
data = [(value/256)*value_multiplier + value_offset for value in data]
|
||||||
|
traces[channel] = {(t+dump_channel*ticks*clock_scale)*self.capture_clock_period: value
|
||||||
|
for (t, value) in zip(range(start_timestamp, timestamp, ticks*clock_scale*len(analog_channels)), data)}
|
||||||
|
|
||||||
|
if logic:
|
||||||
|
async with self.transaction():
|
||||||
|
await self.set_registers(SampleAddress=(address - total_samples) % buffer_width,
|
||||||
|
DumpMode=vm.DumpMode.Raw, DumpChan=128, DumpCount=nsamples, DumpRepeat=1, DumpSend=1, DumpSkip=0)
|
||||||
|
await self.issue_program_spock_registers()
|
||||||
|
await self.issue_analog_dump_binary()
|
||||||
|
data = await self._reader.readexactly(nsamples)
|
||||||
|
ts = [t*self.capture_clock_period for t in range(start_timestamp, timestamp, ticks*clock_scale)]
|
||||||
|
for i in range(8):
|
||||||
|
mask = 1<<i
|
||||||
|
if logic_enable & mask:
|
||||||
|
traces[f'L{i}'] = {t: 1 if value & mask else 0 for (t, value) in zip(ts, data)}
|
||||||
|
|
||||||
return traces
|
return traces
|
||||||
|
|
||||||
@ -273,10 +304,10 @@ class Scope(vm.VirtualMachine):
|
|||||||
await self.start_generator(1000, waveform='square')
|
await self.start_generator(1000, waveform='square')
|
||||||
for low in np.linspace(0.063, 0.4, n):
|
for low in np.linspace(0.063, 0.4, n):
|
||||||
for high in np.linspace(0.877, 0.6, n):
|
for high in np.linspace(0.877, 0.6, n):
|
||||||
data = await self.capture(channels='AB', period=2e-3, trigger_level=0.5, nsamples=1000, low=low, high=high, raw=True)
|
data = await self.capture(channels='AB', period=2e-3, trigger_level=0.5, nsamples=2000, low=low, high=high, raw=True)
|
||||||
A = np.array(data['A'])
|
A = np.fromiter(data['A'].values(), dtype='float')
|
||||||
A.sort()
|
A.sort()
|
||||||
B = np.array(data['B'])
|
B = np.fromiter(data['B'].values(), dtype='float')
|
||||||
B.sort()
|
B.sort()
|
||||||
Azero, A3v3 = A[10:490].mean(), A[510:990].mean()
|
Azero, A3v3 = A[10:490].mean(), A[510:990].mean()
|
||||||
Bzero, B3v3 = B[10:490].mean(), B[510:990].mean()
|
Bzero, B3v3 = B[10:490].mean(), B[510:990].mean()
|
||||||
@ -313,9 +344,9 @@ INFO:scope:Initialised scope, revision: BS000501
|
|||||||
In [2]: generate(2000, 'triangle')
|
In [2]: generate(2000, 'triangle')
|
||||||
Out[2]: 2000.0
|
Out[2]: 2000.0
|
||||||
|
|
||||||
In [3]: traces = capture('tA', low=0, high=3.3)
|
In [3]: traces = capture('A', low=0, high=3.3)
|
||||||
|
|
||||||
In [4]: plot(traces.t, traces.A)
|
In [4]: plot(traces.A.keys(), traces.A.values())
|
||||||
Out[4]: [<matplotlib.lines.Line2D at 0x114009160>]
|
Out[4]: [<matplotlib.lines.Line2D at 0x114009160>]
|
||||||
|
|
||||||
In [5]:
|
In [5]:
|
||||||
@ -345,6 +376,6 @@ def generate(*args, **kwargs):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
logging.basicConfig(level=logging.DEBUG, stream=sys.stderr)
|
||||||
asyncio.get_event_loop().run_until_complete(main())
|
asyncio.get_event_loop().run_until_complete(main())
|
||||||
|
|
||||||
|
31
vm.py
31
vm.py
@ -137,18 +137,27 @@ class KitchenSinkB(IntEnum):
|
|||||||
AnalogFilterEnable = 0x80
|
AnalogFilterEnable = 0x80
|
||||||
WaveformGeneratorEnable = 0x40
|
WaveformGeneratorEnable = 0x40
|
||||||
|
|
||||||
ClockMode = namedtuple('ClockMode', ('clock_low', 'clock_high', 'clock_max', 'dual', 'sample_width',
|
CaptureMode = namedtuple('CaptureMode', ('clock_low', 'clock_high', 'clock_max', 'analog_channels', 'sample_width',
|
||||||
'TraceMode', 'BufferMode'))
|
'logic_channels', 'clock_divide', 'TraceMode', 'BufferMode'))
|
||||||
|
|
||||||
ClockModes = [
|
CaptureModes = [
|
||||||
ClockMode(40, 65536, None, False, 2, TraceMode.Macro, BufferMode.Macro),
|
CaptureMode(40, 65536, None, 1, 2, False, False, TraceMode.Macro, BufferMode.Macro),
|
||||||
ClockMode(40, 65536, None, True, 2, TraceMode.MacroChop, BufferMode.MacroChop),
|
CaptureMode(40, 65536, None, 2, 2, False, False, TraceMode.MacroChop, BufferMode.MacroChop),
|
||||||
ClockMode(15, 40, None, False, 1, TraceMode.Analog, BufferMode.Single),
|
CaptureMode(15, 40, None, 1, 1, False, True, TraceMode.Analog, BufferMode.Single),
|
||||||
ClockMode(13, 40, None, True, 1, TraceMode.AnalogChop, BufferMode.Chop),
|
CaptureMode(13, 40, None, 2, 1, False, True, TraceMode.AnalogChop, BufferMode.Chop),
|
||||||
ClockMode( 8, 14, None, False, 1, TraceMode.AnalogFast, BufferMode.Single),
|
CaptureMode( 8, 14, None, 1, 1, False, False, TraceMode.AnalogFast, BufferMode.Single),
|
||||||
ClockMode( 8, 40, None, True, 1, TraceMode.AnalogFastChop, BufferMode.Chop),
|
CaptureMode( 8, 40, None, 2, 1, False, False, TraceMode.AnalogFastChop, BufferMode.Chop),
|
||||||
ClockMode( 2, 8, 5, False, 1, TraceMode.AnalogShot, BufferMode.Single),
|
CaptureMode( 2, 7, 5, 1, 1, False, False, TraceMode.AnalogShot, BufferMode.Single),
|
||||||
ClockMode( 4, 8, 5, True, 1, TraceMode.AnalogShotChop, BufferMode.Chop),
|
CaptureMode( 4, 7, 5, 2, 1, False, False, TraceMode.AnalogShotChop, BufferMode.Chop),
|
||||||
|
CaptureMode( 5, 16384, None, 0, 1, True, False, TraceMode.Logic, BufferMode.Single),
|
||||||
|
CaptureMode( 4, 4, None, 0, 1, True, False, TraceMode.LogicFast, BufferMode.Single),
|
||||||
|
CaptureMode( 1, 3, None, 0, 1, True, False, TraceMode.LogicShot, BufferMode.Single),
|
||||||
|
CaptureMode(15, 40, None, 1, 1, True, True, TraceMode.Mixed, BufferMode.Dual),
|
||||||
|
CaptureMode(13, 40, None, 2, 1, True, True, TraceMode.MixedChop, BufferMode.ChopDual),
|
||||||
|
CaptureMode( 8, 14, None, 1, 1, True, False, TraceMode.MixedFast, BufferMode.Dual),
|
||||||
|
CaptureMode( 8, 40, None, 2, 1, True, False, TraceMode.MixedFastChop, BufferMode.ChopDual),
|
||||||
|
CaptureMode( 2, 7, 5, 1, 1, True, False, TraceMode.MixedShot, BufferMode.Dual),
|
||||||
|
CaptureMode( 4, 7, 5, 2, 1, True, False, TraceMode.MixedShotChop, BufferMode.ChopDual),
|
||||||
]
|
]
|
||||||
|
|
||||||
def encode(value, dtype):
|
def encode(value, dtype):
|
||||||
|
Reference in New Issue
Block a user