From ed429948681adcd37bf7441838847fac74ad3d5a Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Tue, 11 Oct 2016 16:54:56 +0100 Subject: [PATCH] Checkpoint of work in progress --- VM registers.xlsx | Bin 0 -> 35545 bytes scope.py | 6 +- streams.py | 33 ++++-- vm.py | 252 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+), 13 deletions(-) create mode 100644 VM registers.xlsx create mode 100644 vm.py diff --git a/VM registers.xlsx b/VM registers.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9bf3218ea9802230aa79b48918e074f8d4efd48a GIT binary patch literal 35545 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 literal 0 HcmV?d00001 diff --git a/scope.py b/scope.py index d171c94..8d12787 100644 --- a/scope.py +++ b/scope.py @@ -5,9 +5,7 @@ from streams import SerialStream class Scope(object): - def __init__(self, stream=None): - if stream is None: - stream = SerialStream() + def __init__(self, stream): self._stream = stream async def reset(self): @@ -23,7 +21,7 @@ class Scope(object): async def main(): - s = Scope() + s = Scope(SerialStream()) await s.reset() print(await s.get_revision()) diff --git a/streams.py b/streams.py index 9fb4f3a..8f60ec1 100644 --- a/streams.py +++ b/streams.py @@ -1,18 +1,29 @@ import asyncio -import glob +import os import serial +import serial.tools.list_ports -class SerialStream(object): - - def __init__(self, device=None, loop=None, **kwargs): - if device is None: - device = (glob.glob('/dev/tty.usb*') + glob.glob('/dev/ttyUSB*'))[0] - self._connection = serial.Serial(device, timeout=0, write_timeout=0, **kwargs) +class SerialStream: + + @staticmethod + def available_ports(): + return [port.device for port in serial.tools.list_ports.comports()] + + def __init__(self, port=-1, loop=None, **kwargs): + self._device = self.available_ports()[port] + self._connection = serial.Serial(self._device, timeout=0, write_timeout=0, **kwargs) self._loop = loop if loop is not None else asyncio.get_event_loop() self._input_buffer = b'' + def __repr__(self): + return '<{}:{}>'.format(self.__class__.__name__, self._device) + + def close(self): + self._connection.close() + self._connection = None + async def write(self, data): while data: n = await self._write(data) @@ -24,7 +35,9 @@ class SerialStream(object): return future def _feed_data(self, data, future): - future.set_result(self._connection.write(data)) + n = self._connection.write(data) + print(''.format(repr(data[:n]))) + future.set_result(n) self._loop.remove_writer(self._connection) async def read(self, n=None): @@ -62,7 +75,9 @@ class SerialStream(object): return future def _handle_data(self, n, future): - future.set_result(self._connection.read(n if n is not None else self._connection.in_waiting)) + data = self._connection.read(n if n is not None else self._connection.in_waiting) + print(''.format(repr(data))) + future.set_result(data) self._loop.remove_reader(self._connection) diff --git a/vm.py b/vm.py new file mode 100644 index 0000000..3ecec3b --- /dev/null +++ b/vm.py @@ -0,0 +1,252 @@ + + +import asyncio +import numpy as np +import struct + + +class VirtualMachine: + + class Transaction: + def __init__(self, vm): + self._vm = vm + def append(self, cmd): + self._data += cmd + async def __aenter__(self): + self._data = b'' + self._vm._transactions.append(self) + return self + async def __aexit__(self, exc_type, exc_value, traceback): + self._vm._transactions.pop() + if exc_type is None: + await self._vm.issue(self._data) + return False + + Registers = { + "vrTriggerLogic": (1, 0x05, '''Trigger Logic, one bit per channel (0 => Low, 1 => High)''', 'uint'), + "vrTriggerMask": (1, 0x06, '''Trigger Mask, one bit per channel (0 => Don’t Care, 1 => Active)''', 'uint'), + "vrSpockOption": (1, 0x07, '''Spock Option Register (see bit definition table for details)''', 'uint'), + "vrSampleAddress": (3, 0x08, '''Sample address (write) 24 bit''', 'uint'), + "vrSampleCounter": (3, 0x0b, '''Sample address (read) 24 bit''', 'uint'), + "vrTriggerIntro": (2, 0x32, '''Edge trigger intro filter counter (samples/2)''', 'uint'), + "vrTriggerOutro": (2, 0x34, '''Edge trigger outro filter counter (samples/2)''', 'uint'), + "vrTriggerValue": (2, 0x44, '''Digital (comparator) trigger (signed)''', 'int'), + "vrTriggerTime": (4, 0x40, '''Stopwatch trigger time (ticks)''', 'uint'), + "vrClockTicks": (2, 0x2e, '''Master Sample (clock) period (ticks)''', 'uint'), + "vrClockScale": (2, 0x14, '''Clock divide by N (low byte)''', 'uint'), + "vrTraceOption": (1, 0x20, '''Trace Mode Option bits''', 'uint'), + "vrTraceMode": (1, 0x21, '''Trace Mode (see Trace Mode Table)''', 'uint'), + "vrTraceIntro": (2, 0x26, '''Pre-trigger capture count (samples)''', 'uint'), + "vrTraceDelay": (4, 0x22, '''Delay period (uS)''', 'uint'), + "vrTraceOutro": (2, 0x2a, '''Post-trigger capture count (samples)''', 'uint'), + "vrTimeout": (2, 0x2c, '''Auto trace timeout (auto-ticks)''', 'uint'), + "vrPrelude": (2, 0x3a, '''Buffer prefill value''', 'uint'), + "vrBufferMode": (1, 0x31, '''Buffer mode''', 'uint'), + "vrDumpMode": (1, 0x1e, '''Dump mode''', 'uint'), + "vrDumpChan": (1, 0x30, '''Dump (buffer) Channel (0..127,128..254,255)''', 'uint'), + "vrDumpSend": (2, 0x18, '''Dump send (samples)''', 'uint'), + "vrDumpSkip": (2, 0x1a, '''Dump skip (samples)''', 'uint'), + "vrDumpCount": (2, 0x1c, '''Dump size (samples)''', 'uint'), + "vrDumpRepeat": (2, 0x16, '''Dump repeat (iterations)''', 'uint'), + "vrStreamIdent": (1, 0x36, '''Stream data token''', 'uint'), + "vrStampIdent": (1, 0x3c, '''Timestamp token''', 'uint'), + "vrAnalogEnable": (1, 0x37, '''Analog channel enable (bitmap)''', 'uint'), + "vrDigitalEnable": (1, 0x38, '''Digital channel enable (bitmap)''', 'uint'), + "vrSnoopEnable": (1, 0x39, '''Frequency (snoop) channel enable (bitmap)''', 'uint'), + "vpCmd": (1, 0x46, '''Command Vector''', 'uint'), + "vpMode": (1, 0x47, '''Operation Mode (per command)''', 'uint'), + "vpOption": (2, 0x48, '''Command Option (bits fields per command)''', 'uint'), + "vpSize": (2, 0x4a, '''Operation (unit/block) size''', 'uint'), + "vpIndex": (2, 0x4c, '''Operation index (eg, P Memory Page)''', 'uint'), + "vpAddress": (2, 0x4e, '''General purpose address''', 'uint'), + "vpClock": (2, 0x50, '''Sample (clock) period (ticks)''', 'uint'), + "vpModulo": (2, 0x52, '''Modulo Size (generic)''', 'uint'), + "vpLevel": (2, 0x54, '''Output (analog) attenuation (unsigned)''', 'uint'), + "vpOffset": (2, 0x56, '''Output (analog) offset (signed)''', 'int'), + "vpMask": (2, 0x58, '''Translate source modulo mask''', 'uint'), + "vpRatio": (4, 0x5a, '''Translate command ratio (phase step)''', 'uint'), + "vpMark": (2, 0x5e, '''Mark count/phase (ticks/step)''', 'uint'), + "vpSpace": (2, 0x60, '''Space count/phase (ticks/step)''', 'uint'), + "vpRise": (2, 0x82, '''Rising edge clock (channel 1) phase (ticks)''', 'uint'), + "vpFall": (2, 0x84, '''Falling edge clock (channel 1) phase (ticks)''', 'uint'), + "vpControl": (1, 0x86, '''Clock Control Register (channel 1)''', 'uint'), + "vpRise2": (2, 0x88, '''Rising edge clock (channel 2) phase (ticks)''', 'uint'), + "vpFall2": (2, 0x8a, '''Falling edge clock (channel 2) phase (ticks)''', 'uint'), + "vpControl2": (1, 0x8c, '''Clock Control Register (channel 2)''', 'uint'), + "vpRise3": (2, 0x8e, '''Rising edge clock (channel 3) phase (ticks)''', 'uint'), + "vpFall3": (2, 0x90, '''Falling edge clock (channel 3) phase (ticks)''', 'uint'), + "vpControl3": (1, 0x92, '''Clock Control Register (channel 3)''', 'uint'), + "vrEepromData": (1, 0x10, '''EE Data Register''', 'uint'), + "vrEepromAddress": (1, 0x11, '''EE Address Register''', 'uint'), + "vrConverterLo": (2, 0x64, '''VRB ADC Range Bottom (D Trace Mode)''', 'uint'), + "vrConverterHi": (2, 0x66, '''VRB ADC Range Top (D Trace Mode)''', 'uint'), + "vrTriggerLevel": (2, 0x68, '''Trigger Level (comparator, unsigned)''', 'uint'), + "vrLogicControl": (1, 0x74, '''Logic Control''', 'uint'), + "vrRest": (2, 0x78, '''DAC (rest) level''', 'uint'), + "vrKitchenSinkA": (1, 0x7b, '''Kitchen Sink Register A''', 'uint'), + "vrKitchenSinkB": (1, 0x7c, '''Kitchen Sink Register B''', 'uint'), + } + + def __init__(self, stream): + self._stream = stream + self._transactions = [] + + def new_transaction(self): + return self.Transaction(self) + + async def issue(self, cmd): + if isinstance(cmd, str): + cmd = cmd.encode('ascii') + if not self._transactions: + await self._stream.write(cmd) + await self._stream.readuntil(cmd) + else: + self._transactions[-1].append(cmd) + + async def read_reply(self): + return (await self.read_replies(1))[0] + + async def read_replies(self, n): + await self._stream.readuntil(b'\r') + replies = [] + for i in range(n): + replies.append((await self._stream.readuntil(b'\r'))[:-1]) + return replies + + async def reset(self): + await self._stream.write(b'!') + await self._stream.readuntil(b'!') + + async def set_register(self, name, value): + width, base, desc, dtype = self.Registers[name] + bs = struct.pack('') + + async def configure_device_hardware(self): + await self.issue(b'U') + + async def streaming_trace(self): + await self.issue(b'T') + + async def triggered_trace(self): + await self.issue(b'D') + + async def cancel_trace(self): + await self.issue(b'K') + + async def sample_dump_csv(self): + await self.issue(b'S') + + async def analog_dump_binary(self): + await self.issue(b'S') + + async def read_wavetable(self, size=1024, address=0): + async with self.new_transaction(): + await self.set_registers(vpSize=size, vpAddress=address) + await self.issue(b'R') + return await self._stream.readexactly(size) + + async def write_wavetable(self, data, address=0): + async with self.new_transaction(): + await self.set_registers(vpSize=1, vpAddress=address) + for byte in data: + await self.issue('{:02x}W'.format(byte)) + + async def synthesize_wavetable(self, mode='sine', ratio=0.5): + mode = {'sine': 0, 'sawtooth': 1, 'exponential': 2, 'square': 3}[mode.lower()] + async with self.new_transaction(): + await self.set_registers(vpCmd=0, vpMode=mode, vpRatio=int(max(0, min(ratio, 1))*65535)) + await self.issue(b'Y') + + async def translate_wavetable(self, ratio, level=1, offset=0, size=0, index=0, address=0): + async with self.new_transaction(): + await self.set_registers(vpCmd=0, vpMode=0, vpLevel=int(65535*level), vpOffset=int(65535*offset), vpRatio=ratio, + vpSize=size, vpIndex=index, vpAddress=address) + await self.issue(b'X') + + async def stop_waveform_generator(self): + async with self.new_transaction(): + await self.set_registers(vpCmd=1, vpMode=0) + await self.issue(b'Z') + + async def start_waveform_generator(self, clock, modulo, mark, space, rest, option): + async with self.new_transaction(): + await self.set_registers(vpCmd=2, vpMode=0, vpClock=clock, vpModulo=modulo, + vpMark=mark, vpSpace=space, vrRest=rest, vpOption=option) + await self.issue(b'Z') + + async def start_clock_generator(self): + async with self.new_transaction(): + await self.set_registers(vpCmd=3, vpMode=0) + await self.issue(b'Z') + + async def read_eeprom(self, address): + async with self.new_transaction(): + await self.set_registers(vrEepromAddress=address) + await self.issue(b'r') + return int(await self.read_reply(), 16) + + async def write_eeprom(self, address, data): + async with self.new_transaction(): + await self.set_registers(vrEepromAddress=address, vrEepromData=data) + await self.issue(b'w') + return int(await self.read_reply(), 16) + + +async def main(): + from streams import SerialStream + vm = VirtualMachine(SerialStream()) + await vm.reset() + print(await vm.get_revision()) + print(await vm.get_register('vrConverterLo')) + print(await vm.get_register('vrTriggerLevel')) + print(await vm.get_register('vrConverterHi')) + await vm.set_register('vrTriggerLevel', 15000) + print(await vm.get_register('vrTriggerLevel')) + n = await vm.read_eeprom(0) + print(n) + print(await vm.write_eeprom(0, n+1)) + print(await vm.read_eeprom(0)) + async with vm.new_transaction(): + await vm.set_registers(vrKitchenSinkB=0x40) + await vm.configure_device_hardware() + await vm.synthesize_wavetable('sawtooth') + #data = await vm.read_wavetable() + #global array + #array = np.ndarray(buffer=data, shape=(len(data),), dtype='uint8') + #print(array) + await vm.translate_wavetable(671088) + await vm.start_waveform_generator(clock=40, modulo=1000, mark=10, space=1, rest=0x7f00, option=0x8004) + +if __name__ == '__main__': + asyncio.get_event_loop().run_until_complete(main()) +