159 lines
3.9 KiB
Python
159 lines
3.9 KiB
Python
from sys import argv
|
|
import json
|
|
import serial
|
|
from vcd import VCDWriter
|
|
from datetime import datetime
|
|
|
|
def check_config(config):
|
|
"""Check that configuration is okay"""
|
|
assert config["probes"]
|
|
assert config["triggers"]
|
|
assert config["uart"]
|
|
|
|
def setup_serial(ser, config):
|
|
ser.baudrate = config['uart']['baudrate']
|
|
ser.port = config['uart']['port']
|
|
ser.timeout = config['uart']['timeout']
|
|
|
|
# setup number of data bits
|
|
if config['uart']['data'] == 8:
|
|
ser.bytesize = serial.EIGHTBITS
|
|
|
|
elif config['uart']['data'] == 7:
|
|
ser.bytesize = serial.SEVENBITS
|
|
|
|
elif config['uart']['data'] == 6:
|
|
ser.bytesize = serial.SIXBITS
|
|
|
|
elif config['uart']['data'] == 5:
|
|
ser.bytesize = serial.FIVEBITS
|
|
|
|
else:
|
|
raise ValueError("Invalid number of data bits in UART configuration.")
|
|
|
|
# setup number of stop bits
|
|
if config['uart']['stop'] == 1:
|
|
ser.stopbits = serial.STOPBITS_ONE
|
|
|
|
elif config['uart']['stop'] == 1.5:
|
|
ser.stopbits = serial.STOPBITS_ONE_POINT_FIVE
|
|
|
|
elif config['uart']['stop'] == 2:
|
|
ser.stopbits = serial.STOPBITS_TWO
|
|
|
|
else:
|
|
raise ValueError("Invalid number of stop bits in UART configuration.")
|
|
|
|
# setup parity
|
|
if config['uart']['parity'] == 'none':
|
|
ser.parity = serial.PARITY_NONE
|
|
|
|
elif config['uart']['parity'] == 'even':
|
|
ser.parity = serial.PARITY_EVEN
|
|
|
|
elif config['uart']['parity'] == 'odd':
|
|
ser.parity = serial.PARITY_ODD
|
|
|
|
elif config['uart']['parity'] == 'mark':
|
|
ser.parity = serial.PARITY_MARK
|
|
|
|
elif config['uart']['parity'] == 'space':
|
|
ser.parity = serial.PARITY_SPACE
|
|
|
|
else:
|
|
raise ValueError("Invalid parity setting in UART configuration.")
|
|
|
|
def part_select(data, width):
|
|
top, bottom = width
|
|
|
|
assert top >= bottom
|
|
|
|
mask = 2**(top - bottom + 1) - 1
|
|
return (data >> bottom) & mask
|
|
|
|
def make_widths(config):
|
|
# {probe0, probe1, probe2}
|
|
# [12, 1, 3] should produce
|
|
# [ (11,0) , (12, 12), (15,13) ]
|
|
|
|
widths = list(config['probes'].values())
|
|
|
|
parts = []
|
|
for i, width in enumerate(widths):
|
|
if (i == 0):
|
|
parts.append( (width - 1, 0) )
|
|
|
|
else:
|
|
parts.append( ((parts[i-1][1] + width) , (parts[i-1][1] + 1)) )
|
|
|
|
# reversing this list is a little bit of a hack, should fix/document
|
|
return parts[::-1]
|
|
|
|
|
|
## Main Program
|
|
|
|
# parse args
|
|
if len(argv) == 1 or argv[1] == '-h':
|
|
print("""
|
|
run_ila.py: interface with the ILA on the FPGA, setting triggers and downlinking waveform data.
|
|
usage: python3 run_ila.py [config input file] [vcd output file]
|
|
options:
|
|
-h: print this help menu
|
|
-l: list all available serial devices
|
|
|
|
example: python3 run_ila.py ila.json ila.vcd
|
|
""")
|
|
exit()
|
|
|
|
elif argv[1] == '-l':
|
|
import serial.tools.list_ports
|
|
|
|
for info in serial.tools.list_ports.comports():
|
|
print(info)
|
|
|
|
elif len(argv) == 2:
|
|
config_fpath = argv[1]
|
|
vcd_fpath = 'ila.vcd'
|
|
|
|
|
|
elif len(argv) == 3:
|
|
config_fpath = argv[1]
|
|
vcd_fpath = argv[2]
|
|
|
|
else:
|
|
exit()
|
|
|
|
# read config
|
|
with open(config_fpath, 'r') as f:
|
|
config = json.load(f)
|
|
|
|
# obtain bytestream from FPGA
|
|
with serial.Serial() as ser:
|
|
setup_serial(ser, config)
|
|
ser.open()
|
|
ser.flushInput()
|
|
ser.write(b'\x30')
|
|
data = ser.read(4096)
|
|
|
|
# export VCD
|
|
vcd_file = open(vcd_fpath, 'w')
|
|
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
|
|
|
with VCDWriter(vcd_file, timescale='10 ns', date=timestamp, version = 'openILA') as writer:
|
|
|
|
# add probes to vcd file
|
|
vcd_probes = []
|
|
for name, width in config['probes'].items():
|
|
probe = writer.register_var('ila', name, 'wire', size = width)
|
|
vcd_probes.append(probe)
|
|
|
|
# calculate bit widths for part selecting
|
|
widths = make_widths(config)
|
|
|
|
# slice data, and dump to vcd file
|
|
for timestamp, value in enumerate(data):
|
|
for probe_num, probe in enumerate(vcd_probes):
|
|
val = part_select(value, widths[probe_num])
|
|
writer.change(probe, timestamp, val)
|
|
|
|
vcd_file.close() |