From: Florent Kermarrec Date: Wed, 18 Feb 2015 14:32:34 +0000 (+0100) Subject: split host files since we now have more drivers/dumps supported X-Git-Tag: 24jan2021_ls180~2575^2~19 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6bfd5ce1d86621db0590bc3b9ae7517b49031ebc;p=litex.git split host files since we now have more drivers/dumps supported --- diff --git a/README b/README index 216f4a04..6804b3ee 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ florent@enjoy-digital.fr A small footprint and configurable embedded FPGA - logic analyzer core developed by EnjoyDigital + logic analyzer core powered by Migen [> Doc --------- diff --git a/litescope/host/driver.py b/litescope/host/driver.py deleted file mode 100644 index 0a49fe49..00000000 --- a/litescope/host/driver.py +++ /dev/null @@ -1,316 +0,0 @@ -import csv -import time -import sys -import string -import serial -import socket -from struct import * -from migen.fhdl.structure import * -from litescope.host.reg import * -from litescope.host.dump import * -from litescope.host.truthtable import * - -# XXX FIXME -try: - from liteeth.test.model.etherbone import * -except: - pass - -def write_b(uart, data): - uart.write(pack('B',data)) - -class LiteScopeUART2WBDriver: - cmds = { - "write" : 0x01, - "read" : 0x02 - } - def __init__(self, port, baudrate=115200, addrmap=None, busword=8, debug=False): - self.port = port - self.baudrate = str(baudrate) - self.debug = debug - self.uart = serial.Serial(port, baudrate, timeout=0.25) - self.regs = build_map(addrmap, busword, self.read, self.write) - - def open(self): - self.uart.flushOutput() - self.uart.close() - self.uart.open() - self.uart.flushInput() - try: - self.regs.uart2wb_sel.write(1) - except: - pass - - def close(self): - try: - self.regs.uart2wb_sel.write(0) - except: - pass - self.uart.flushOutput() - self.uart.close() - - def read(self, addr, burst_length=1): - self.uart.flushInput() - write_b(self.uart, self.cmds["read"]) - write_b(self.uart, burst_length) - addr = addr//4 - write_b(self.uart, (addr & 0xff000000) >> 24) - write_b(self.uart, (addr & 0x00ff0000) >> 16) - write_b(self.uart, (addr & 0x0000ff00) >> 8) - write_b(self.uart, (addr & 0x000000ff)) - values = [] - for i in range(burst_length): - val = 0 - for j in range(4): - val = val << 8 - val |= ord(self.uart.read()) - if self.debug: - print("RD %08X @ %08X" %(val, (addr+i)*4)) - values.append(val) - if burst_length == 1: - return values[0] - else: - return values - - def write(self, addr, data): - if isinstance(data, list): - burst_length = len(data) - else: - burst_length = 1 - write_b(self.uart, self.cmds["write"]) - write_b(self.uart, burst_length) - addr = addr//4 - write_b(self.uart, (addr & 0xff000000) >> 24) - write_b(self.uart, (addr & 0x00ff0000) >> 16) - write_b(self.uart, (addr & 0x0000ff00) >> 8) - write_b(self.uart, (addr & 0x000000ff)) - if isinstance(data, list): - for i in range(len(data)): - dat = data[i] - for j in range(4): - write_b(self.uart, (dat & 0xff000000) >> 24) - dat = dat << 8 - if self.debug: - print("WR %08X @ %08X" %(data[i], (addr + i)*4)) - else: - dat = data - for j in range(4): - write_b(self.uart, (dat & 0xff000000) >> 24) - dat = dat << 8 - if self.debug: - print("WR %08X @ %08X" %(data, (addr * 4))) - -class LiteScopeEtherboneDriver: - def __init__(self, ip_address, udp_port=20000, addrmap=None, busword=8, debug=False): - self.ip_address = ip_address - self.udp_port = udp_port - self.debug = debug - - self.tx_sock = None - self.rx_sock = None - self.regs = build_map(addrmap, busword, self.read, self.write) - - def open(self): - self.tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.rx_sock.bind(("", self.udp_port)) - - def close(self): - pass - - def read(self, addr, burst_length=1): - reads_addrs = [addr+4*j for j in range(burst_length)] - reads = EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) - record = EtherboneRecord() - record.writes = None - record.reads = reads - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = 0 - record.rcount = len(reads_addrs) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.tx_sock.sendto(bytes(packet), (self.ip_address, self.udp_port)) - - datas, addrs = self.rx_sock.recvfrom(8192) - packet = EtherbonePacket(datas) - packet.decode() - values = packet.records.pop().writes.get_datas() - if self.debug: - for i, val in enumerate(values): - print("RD %08X @ %08X" %(val, addr + 4*i)) - if burst_length == 1: - return values[0] - else: - return values - - def write(self, addr, datas): - if not isinstance(datas, list): - datas = [datas] - writes_datas = [d for d in datas] - writes = EtherboneWrites(base_addr=addr, datas=writes_datas) - record = EtherboneRecord() - record.writes = writes - record.reads = None - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = len(writes_datas) - record.rcount = 0 - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.tx_sock.sendto(bytes(packet), (self.ip_address, self.udp_port)) - - if self.debug: - for i, data in enumerate(datas): - print("WR %08X @ %08X" %(data, addr + 4*i)) - -class LiteScopeIODriver(): - def __init__(self, regs, name): - self.regs = regs - self.name = name - self.build() - - def build(self): - for key, value in self.regs.d.items(): - if self.name in key: - key = key.replace(self.name +"_", "") - setattr(self, key, value) - - def write(self, value): - self.o.write(value) - - def read(self): - return self.i.read() - -class LiteScopeLADriver(): - def __init__(self, regs, name, config_csv=None, use_rle=False, debug=False): - self.regs = regs - self.name = name - self.use_rle = use_rle - self.debug = debug - if config_csv is None: - self.config_csv = name + ".csv" - self.get_config() - self.get_layout() - self.build() - self.dat = Dat(self.dw) - - def get_config(self): - csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#') - for item in csv_reader: - t, n, v = item - if t == "config": - setattr(self, n, int(v)) - - def get_layout(self): - self.layout = [] - csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#') - for item in csv_reader: - t, n, v = item - if t == "layout": - self.layout.append((n, int(v))) - - def build(self): - for key, value in self.regs.d.items(): - if self.name == key[:len(self.name)]: - key = key.replace(self.name + "_", "") - setattr(self, key, value) - value = 1 - for name, length in self.layout: - setattr(self, name + "_o", value) - value = value*(2**length) - value = 0 - for name, length in self.layout: - setattr(self, name + "_m", (2**length-1) << value) - value += length - - def configure_term(self, port, trigger=0, mask=0, cond=None): - if cond is not None: - for k, v in cond.items(): - trigger |= getattr(self, k + "_o")*v - mask |= getattr(self, k + "_m") - t = getattr(self, "trigger_port{d}_trig".format(d=int(port))) - m = getattr(self, "trigger_port{d}_mask".format(d=int(port))) - t.write(trigger) - m.write(mask) - - def configure_range_detector(self, port, low, high): - l = getattr(self, "trigger_port{d}_low".format(d=int(port))) - h = getattr(self, "trigger_port{d}_high".format(d=int(port))) - l.write(low) - h.write(high) - - def configure_edge_detector(self, port, rising_mask, falling_mask, both_mask): - rm = getattr(self, "trigger_port{d}_rising_mask".format(d=int(port))) - fm = getattr(self, "trigger_port{d}_falling_mask".format(d=int(port))) - bm = getattr(self, "trigger_port{d}_both_mask".format(d=int(port))) - rm.write(rising_mask) - fm.write(falling_mask) - bm.write(both_mask) - - def configure_sum(self, equation): - datas = gen_truth_table(equation) - for adr, dat in enumerate(datas): - self.trigger_sum_prog_adr.write(adr) - self.trigger_sum_prog_dat.write(dat) - self.trigger_sum_prog_we.write(1) - - def configure_subsampler(self, n): - self.subsampler_value.write(n-1) - - def configure_qualifier(self, v): - self.recorder_qualifier.write(v) - - def configure_rle(self, v): - self.rle_enable.write(v) - - def done(self): - return self.recorder_done.read() - - def run(self, offset, length): - if self.debug: - print("run") - if self.with_rle: - self.config_rle(self.use_rle) - self.recorder_offset.write(offset) - self.recorder_length.write(length) - self.recorder_trigger.write(1) - - def upload(self): - if self.debug: - print("upload") - while self.recorder_source_stb.read(): - self.dat.append(self.recorder_source_data.read()) - self.recorder_source_ack.write(1) - if self.with_rle: - if self.use_rle: - self.dat = self.dat.decode_rle() - return self.dat - - def save(self, filename): - if self.debug: - print("save to " + filename) - dump = Dump() - dump.add_from_layout(self.layout, self.dat) - if ".vcd" in filename: - VCDExport(dump).write(filename) - elif ".csv" in filename: - CSVExport(dump).write(filename) - elif ".py" in filename: - PYExport(dump).write(filename) - else: - raise NotImplementedError diff --git a/litescope/host/driver/__init__.py b/litescope/host/driver/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litescope/host/driver/etherbone.py b/litescope/host/driver/etherbone.py new file mode 100644 index 00000000..02297dde --- /dev/null +++ b/litescope/host/driver/etherbone.py @@ -0,0 +1,82 @@ +import socket +from litescope.host.driver.reg import * + +from liteeth.test.model.etherbone import * + +class LiteScopeEtherboneDriver: + def __init__(self, ip_address, udp_port=20000, addrmap=None, busword=8, debug=False): + self.ip_address = ip_address + self.udp_port = udp_port + self.debug = debug + + self.tx_sock = None + self.rx_sock = None + self.regs = build_map(addrmap, busword, self.read, self.write) + + def open(self): + self.tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.rx_sock.bind(("", self.udp_port)) + + def close(self): + pass + + def read(self, addr, burst_length=1): + reads_addrs = [addr+4*j for j in range(burst_length)] + reads = EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) + record = EtherboneRecord() + record.writes = None + record.reads = reads + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = 0 + record.rcount = len(reads_addrs) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.tx_sock.sendto(bytes(packet), (self.ip_address, self.udp_port)) + + datas, addrs = self.rx_sock.recvfrom(8192) + packet = EtherbonePacket(datas) + packet.decode() + values = packet.records.pop().writes.get_datas() + if self.debug: + for i, val in enumerate(values): + print("RD %08X @ %08X" %(val, addr + 4*i)) + if burst_length == 1: + return values[0] + else: + return values + + def write(self, addr, datas): + if not isinstance(datas, list): + datas = [datas] + writes_datas = [d for d in datas] + writes = EtherboneWrites(base_addr=addr, datas=writes_datas) + record = EtherboneRecord() + record.writes = writes + record.reads = None + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = len(writes_datas) + record.rcount = 0 + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.tx_sock.sendto(bytes(packet), (self.ip_address, self.udp_port)) + + if self.debug: + for i, data in enumerate(datas): + print("WR %08X @ %08X" %(data, addr + 4*i)) diff --git a/litescope/host/driver/io.py b/litescope/host/driver/io.py new file mode 100644 index 00000000..dbee829a --- /dev/null +++ b/litescope/host/driver/io.py @@ -0,0 +1,17 @@ +class LiteScopeIODriver(): + def __init__(self, regs, name): + self.regs = regs + self.name = name + self.build() + + def build(self): + for key, value in self.regs.d.items(): + if self.name in key: + key = key.replace(self.name +"_", "") + setattr(self, key, value) + + def write(self, value): + self.o.write(value) + + def read(self): + return self.i.read() diff --git a/litescope/host/driver/la.py b/litescope/host/driver/la.py new file mode 100644 index 00000000..9f6a6445 --- /dev/null +++ b/litescope/host/driver/la.py @@ -0,0 +1,131 @@ +import csv +from struct import * +from migen.fhdl.structure import * +from litescope.host.dump import * +from litescope.host.driver.truthtable import * + +class LiteScopeLADriver(): + def __init__(self, regs, name, config_csv=None, use_rle=False, debug=False): + self.regs = regs + self.name = name + self.use_rle = use_rle + self.debug = debug + if config_csv is None: + self.config_csv = name + ".csv" + self.get_config() + self.get_layout() + self.build() + self.dat = Dat(self.dw) + + def get_config(self): + csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#') + for item in csv_reader: + t, n, v = item + if t == "config": + setattr(self, n, int(v)) + + def get_layout(self): + self.layout = [] + csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#') + for item in csv_reader: + t, n, v = item + if t == "layout": + self.layout.append((n, int(v))) + + def build(self): + for key, value in self.regs.d.items(): + if self.name == key[:len(self.name)]: + key = key.replace(self.name + "_", "") + setattr(self, key, value) + value = 1 + for name, length in self.layout: + setattr(self, name + "_o", value) + value = value*(2**length) + value = 0 + for name, length in self.layout: + setattr(self, name + "_m", (2**length-1) << value) + value += length + + def configure_term(self, port, trigger=0, mask=0, cond=None): + if cond is not None: + for k, v in cond.items(): + trigger |= getattr(self, k + "_o")*v + mask |= getattr(self, k + "_m") + t = getattr(self, "trigger_port{d}_trig".format(d=int(port))) + m = getattr(self, "trigger_port{d}_mask".format(d=int(port))) + t.write(trigger) + m.write(mask) + + def configure_range_detector(self, port, low, high): + l = getattr(self, "trigger_port{d}_low".format(d=int(port))) + h = getattr(self, "trigger_port{d}_high".format(d=int(port))) + l.write(low) + h.write(high) + + def configure_edge_detector(self, port, rising_mask, falling_mask, both_mask): + rm = getattr(self, "trigger_port{d}_rising_mask".format(d=int(port))) + fm = getattr(self, "trigger_port{d}_falling_mask".format(d=int(port))) + bm = getattr(self, "trigger_port{d}_both_mask".format(d=int(port))) + rm.write(rising_mask) + fm.write(falling_mask) + bm.write(both_mask) + + def configure_sum(self, equation): + datas = gen_truth_table(equation) + for adr, dat in enumerate(datas): + self.trigger_sum_prog_adr.write(adr) + self.trigger_sum_prog_dat.write(dat) + self.trigger_sum_prog_we.write(1) + + def configure_subsampler(self, n): + self.subsampler_value.write(n-1) + + def configure_qualifier(self, v): + self.recorder_qualifier.write(v) + + def configure_rle(self, v): + self.rle_enable.write(v) + + def done(self): + return self.recorder_done.read() + + def run(self, offset, length): + if self.debug: + print("running") + if self.with_rle: + self.config_rle(self.use_rle) + self.recorder_offset.write(offset) + self.recorder_length.write(length) + self.recorder_trigger.write(1) + + def upload(self): + if self.debug: + print("uploading") + while self.recorder_source_stb.read(): + self.dat.append(self.recorder_source_data.read()) + self.recorder_source_ack.write(1) + if self.with_rle: + if self.use_rle: + self.dat = self.dat.decode_rle() + return self.dat + + def save(self, filename): + if self.debug: + print("saving to " + filename) + name, ext = os.path.splitext(filename) + if ext == ".vcd": + from litescope.host.dump.vcd import VCDDump + dump = VCDDump() + elif ext == ".csv": + from litescope.host.dump.csv import CSVDump + dump = CSVDump() + elif ext == ".py": + from litescope.host.dump.python import PythonDump + dump = PythonDump() + elif ext == ".sr": + from litescope.host.dump.sigrok import SigrokDump + dump = SigrokDump() + else: + raise NotImplementedError + dump.add_from_layout(self.layout, self.dat) + dump.write(filename) diff --git a/litescope/host/driver/reg.py b/litescope/host/driver/reg.py new file mode 100644 index 00000000..16a98d91 --- /dev/null +++ b/litescope/host/driver/reg.py @@ -0,0 +1,48 @@ +import csv + +class MappedReg: + def __init__(self, readfn, writefn, name, addr, length, busword, mode): + self.readfn = readfn + self.writefn = writefn + self.addr = addr + self.length = length + self.busword = busword + self.mode = mode + + def read(self): + if self.mode not in ["rw", "ro"]: + raise KeyError(name + "register not readable") + r = 0 + for i in range(self.length): + r |= self.readfn(self.addr + 4*i) + if i != (self.length-1): + r <<= self.busword + return r + + def write(self, value): + if self.mode not in ["rw", "wo"]: + raise KeyError(name + "register not writable") + for i in range(self.length): + dat = (value >> ((self.length-1-i)*self.busword)) & (2**self.busword-1) + self.writefn(self.addr + 4*i, dat) + +class MappedRegs: + def __init__(self, d): + self.d = d + + def __getattr__(self, attr): + try: + return self.__dict__['d'][attr] + except KeyError: + pass + raise KeyError("No such register " + attr) + +def build_map(addrmap, busword, readfn, writefn): + csv_reader = csv.reader(open(addrmap), delimiter=',', quotechar='#') + d = {} + for item in csv_reader: + name, addr, length, mode = item + addr = int(addr.replace("0x", ""), 16) + length = int(length) + d[name] = MappedReg(readfn, writefn, name, addr, length, busword, mode) + return MappedRegs(d) \ No newline at end of file diff --git a/litescope/host/driver/truthtable.py b/litescope/host/driver/truthtable.py new file mode 100644 index 00000000..3ed6e18c --- /dev/null +++ b/litescope/host/driver/truthtable.py @@ -0,0 +1,47 @@ +import os +import re +import sys + +def is_number(x): + try: + _ = float(x) + except ValueError: + return False + return True + +def remove_numbers(seq): + return [x for x in seq if not is_number(x)] + +def remove_duplicates(seq): + seen = set() + seen_add = seen.add + return [x for x in seq if x not in seen and not seen_add(x)] + +def get_operands(s): + operands = re.findall("[A-z0-9_]+", s) + operands = remove_duplicates(operands) + operands = remove_numbers(operands) + return sorted(operands) + +def gen_truth_table(s): + operands = get_operands(s) + width = len(operands) + stim = [] + for i in range(width): + stim_op = [] + for j in range(2**width): + stim_op.append((int(j/(2**i)))%2) + stim.append(stim_op) + + truth_table = [] + for i in range(2**width): + for j in range(width): + exec("%s = stim[j][i]" %operands[j]) + truth_table.append(eval(s) != 0) + return truth_table + +def main(): + print(gen_truth_table("(A&B&C)|D")) + +if __name__ == '__main__': + main() diff --git a/litescope/host/driver/uart.py b/litescope/host/driver/uart.py new file mode 100644 index 00000000..4bd8f713 --- /dev/null +++ b/litescope/host/driver/uart.py @@ -0,0 +1,87 @@ +import serial +from struct import * +from litescope.host.driver.reg import * + +def write_b(uart, data): + uart.write(pack('B',data)) + +class LiteScopeUARTDriver: + cmds = { + "write" : 0x01, + "read" : 0x02 + } + def __init__(self, port, baudrate=115200, addrmap=None, busword=8, debug=False): + self.port = port + self.baudrate = str(baudrate) + self.debug = debug + self.uart = serial.Serial(port, baudrate, timeout=0.25) + self.regs = build_map(addrmap, busword, self.read, self.write) + + def open(self): + self.uart.flushOutput() + self.uart.close() + self.uart.open() + self.uart.flushInput() + try: + self.regs.uart2wb_sel.write(1) + except: + pass + + def close(self): + try: + self.regs.uart2wb_sel.write(0) + except: + pass + self.uart.flushOutput() + self.uart.close() + + def read(self, addr, burst_length=1): + self.uart.flushInput() + write_b(self.uart, self.cmds["read"]) + write_b(self.uart, burst_length) + addr = addr//4 + write_b(self.uart, (addr & 0xff000000) >> 24) + write_b(self.uart, (addr & 0x00ff0000) >> 16) + write_b(self.uart, (addr & 0x0000ff00) >> 8) + write_b(self.uart, (addr & 0x000000ff)) + values = [] + for i in range(burst_length): + val = 0 + for j in range(4): + val = val << 8 + val |= ord(self.uart.read()) + if self.debug: + print("RD %08X @ %08X" %(val, (addr+i)*4)) + values.append(val) + if burst_length == 1: + return values[0] + else: + return values + + def write(self, addr, data): + if isinstance(data, list): + burst_length = len(data) + else: + burst_length = 1 + write_b(self.uart, self.cmds["write"]) + write_b(self.uart, burst_length) + addr = addr//4 + write_b(self.uart, (addr & 0xff000000) >> 24) + write_b(self.uart, (addr & 0x00ff0000) >> 16) + write_b(self.uart, (addr & 0x0000ff00) >> 8) + write_b(self.uart, (addr & 0x000000ff)) + if isinstance(data, list): + for i in range(len(data)): + dat = data[i] + for j in range(4): + write_b(self.uart, (dat & 0xff000000) >> 24) + dat = dat << 8 + if self.debug: + print("WR %08X @ %08X" %(data[i], (addr + i)*4)) + else: + dat = data + for j in range(4): + write_b(self.uart, (dat & 0xff000000) >> 24) + dat = dat << 8 + if self.debug: + print("WR %08X @ %08X" %(data, (addr * 4))) diff --git a/litescope/host/dump.py b/litescope/host/dump.py deleted file mode 100644 index 9c4f3068..00000000 --- a/litescope/host/dump.py +++ /dev/null @@ -1,451 +0,0 @@ -import sys -import os -import math -import shutil -import datetime -import zipfile -import re -from collections import OrderedDict - -def dec2bin(d, nb=0): - if d=="x": - return "x"*nb - elif d==0: - b="0" - else: - b="" - while d!=0: - b="01"[d&1]+b - d=d>>1 - return b.zfill(nb) - -def get_bits(values, width, low, high=None): - r = [] - for val in values: - t = dec2bin(val, width)[::-1] - if high == None: - t = t[low] - else: - t = t[low:high] - t = t[::-1] - t = int(t,2) - r.append(t) - return r - -class Dat(list): - def __init__(self, width): - self.width = width - - def __getitem__(self, key): - if isinstance(key, int): - return get_bits(self, self.width, key) - elif isinstance(key, slice): - if key.start != None: - start = key.start - else: - start = 0 - if key.stop != None: - stop = key.stop - else: - stop = self.width - if stop > self.width: - stop = self.width - if key.step != None: - raise KeyError - return get_bits(self, self.width, start, stop) - else: - raise KeyError - - def decode_rle(self): - rle_bit = self[-1] - rle_dat = self[:self.width-1] - - dat = Dat(self.width) - i=0 - last = 0 - for d in self: - if rle_bit[i]: - if len(dat) >= 1: - # FIX ME... why is rle_dat in reverse order... - for j in range(int(dec2bin(rle_dat[i])[::-1],2)): - dat.append(last) - else: - dat.append(d) - last = d - i +=1 - return dat - -class Var: - def __init__(self, name, width, values=[], type="wire", default="x"): - self.type = type - self.width = width - self.name = name - self.val = default - self.values = values - self.vcd_id = None - - def set_vcd_id(self, s): - self.vcd_id = s - - def __len__(self): - return len(self.values) - - def change(self, cnt): - r = "" - try : - if self.values[cnt+1] != self.val: - r += "b" - r += dec2bin(self.values[cnt+1], self.width) - r += " " - r += self.vcd_id - r += "\n" - return r - except : - return r - return r - -class Dump: - def __init__(self): - self.vars = [] - self.vcd_id = "!" - - def add(self, var): - var.set_vcd_id(self.vcd_id) - self.vcd_id = chr(ord(self.vcd_id)+1) - self.vars.append(var) - - def add_from_layout(self, layout, var): - i=0 - for s, n in layout: - self.add(Var(s, n, var[i:i+n])) - i += n - - def __len__(self): - l = 0 - for var in self.vars: - l = max(len(var),l) - return l - -class VCDExport(): - def __init__(self, dump, timescale="1ps", comment=""): - self.dump = dump - self.timescale = timescale - self.comment = comment - self.cnt = -1 - - def change(self): - r = "" - c = "" - for var in self.dump.vars: - c += var.change(self.cnt) - if c != "": - r += "#" - r += str(self.cnt+1) - r += "\n" - r += c - return r - - def p_date(self): - now = datetime.datetime.now() - r = "$date\n" - r += "\t" - r += now.strftime("%Y-%m-%d %H:%M") - r += "\n" - r += "$end\n" - return r - - def p_version(self): - r = "$version\n" - r += "\tmiscope VCD dump\n" - r += "$end\n" - return r - - def p_comment(self): - r = "$comment\n" - r += self.comment - r += "\n$end\n" - return r - - def p_timescale(self): - r = "$timescale " - r += self.timescale - r += " $end\n" - return r - - def p_scope(self): - r = "$scope " - r += self.timescale - r += " $end\n" - return r - - def p_vars(self): - r = "" - for var in self.dump.vars: - r += "$var " - r += var.type - r += " " - r += str(var.width) - r += " " - r += var.vcd_id - r += " " - r += var.name - r += " $end\n" - return r - - def p_unscope(self): - r = "$unscope " - r += " $end\n" - return r - - def p_enddefinitions(self): - r = "$enddefinitions " - r += " $end\n" - return r - - def p_dumpvars(self): - r = "$dumpvars\n" - for var in self.dump.vars: - r += "b" - r += dec2bin(var.val, var.width) - r += " " - r += var.vcd_id - r+= "\n" - r += "$end\n" - return r - - def p_valuechange(self): - r = "" - for i in range(len(self.dump)): - r += self.change() - self.cnt += 1 - return r - - def __repr__(self): - r = "" - r += self.p_date() - r += self.p_version() - r += self.p_comment() - r += self.p_timescale() - r += self.p_scope() - r += self.p_vars() - r += self.p_unscope() - r += self.p_enddefinitions() - r += self.p_dumpvars() - r += self.p_valuechange() - return r - - def write(self, filename): - f = open(filename, "w") - f.write(str(self)) - f.close() - -class CSVExport(): - def __init__(self, dump): - self.dump = dump - - def p_vars(self): - r = "" - for var in self.dump.vars: - r += var.name - r += "," - r += "\n" - for var in self.dump.vars: - r += str(var.width) - r += "," - r += "\n" - return r - - def p_dumpvars(self): - r = "" - for i in range(len(self.dump)): - for var in self.dump.vars: - try: - var.val = var.values[i] - except: - pass - if var.val == "x": - r += "x" - else: - r += dec2bin(var.val, var.width) - r += ", " - r+= "\n" - return r - - def __repr__(self): - r = "" - r += self.p_vars() - r += self.p_dumpvars() - return r - - def write(self, filename): - f = open(filename, "w") - f.write(str(self)) - f.close() - -class PYExport(): - def __init__(self, dump): - self.dump = dump - - def __repr__(self): - r = "dump = {\n" - for var in self.dump.vars: - r += "\"" + var.name + "\"" - r += " : " - r += str(var.values) - r += ",\n" - r += "}" - return r - - def write(self, filename): - f = open(filename, "w") - f.write(str(self)) - f.close() - -class SRExport(): - def __init__(self, dump): - self.dump = dump - - def write_version(self): - f = open("version", "w") - f.write("1") - f.close() - - def write_metadata(self, name): - f = open("metadata", "w") - r = """ -[global] -sigrok version = 0.2.0 -[device 1] -driver = litescope -capturefile = {} -unitsize = 1 -total probes = {} -samplerate = {} MHz -""".format( - name, - len(self.dump.vars), - 50, # XXX add parameter - ) - for i, var in enumerate(self.dump.vars): - r += "probe{} = {}\n".format(i, var.name) - f.write(r) - f.close() - - def write_data(self, name): - # XXX are probes limited to 1 bit? - data_bits = math.ceil(len(self.dump.vars)/8)*8 - data_len = 0 - for var in self.dump.vars: - data_len = max(data_len, len(var)) - datas = [] - for i in range(data_len): - data = 0 - for j, var in enumerate(reversed(self.dump.vars)): - data = data << 1 - try: - data |= var.values[i] %2 - except: - pass - datas.append(data) - - f = open(name, "wb") - for data in datas: - f.write(data.to_bytes(data_bits//8, "big")) - f.close() - - def zip(self, name): - f = zipfile.ZipFile(name + ".sr", "w") - os.chdir(name) - f.write("version") - f.write("metadata") - f.write(name) - os.chdir("..") - f.close() - - def write(self, filename): - name, ext = os.path.splitext(filename) - if os.path.exists(name): - shutil.rmtree(name) - os.makedirs(name) - os.chdir(name) - self.write_version() - self.write_metadata(name) - self.write_data(name) - os.chdir("..") - self.zip(name) - shutil.rmtree(name) - -class SRImport(): - def __init__(self, filename): - name, ext = os.path.splitext(filename) - self.unzip(filename, name) - os.chdir(name) - probes = self.read_metadata() - total_probes = len(probes.keys()) - datas = self.read_data(name, total_probes) - os.chdir("..") - shutil.rmtree(name) - self.dump = self.generate_dump(probes, datas) - - # XXX we can maybe avoid this - def unzip(self, filename, name): - f = open(filename, "rb") - z = zipfile.ZipFile(f) - if os.path.exists(name): - shutil.rmtree(name) - os.makedirs(name) - for file in z.namelist(): - z.extract(file, name) - f.close() - - def read_metadata(self): - probes = OrderedDict() - f = open("metadata", "r") - for l in f: - m = re.search("probe([0-9]+) = (\w+)", l, re.I) - if m is not None: - index = int(m.group(1)) - name = m.group(2) - probes[name] = index - f.close() - return probes - - def read_data(self, name, total_probes): - datas = [] - f = open(name, "rb") - while True: - data = f.read(math.ceil(total_probes/8)) - if data == bytes('', "utf-8"): - break - data = int.from_bytes(data, "big") - datas.append(data) - f.close() - return datas - - def generate_dump(self, probes, datas): - dump = Dump() - for k, v in probes.items(): - probe_data = [] - for data in datas: - probe_data.append((data >> v) & 0x1) - dump.add(Var(k, 1, probe_data)) - return dump - -def main(): - dump = Dump() - dump.add(Var("foo1", 1, [0,1,0,1,0,1])) - dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0])) - ramp = [i%128 for i in range(1024)] - dump.add(Var("ramp", 16, ramp)) - - VCDExport(dump).write("mydump.vcd") - CSVExport(dump).write("mydump.csv") - PYExport(dump).write("mydump.py") - SRExport(dump).write("dump.sr") - dump = SRImport("dump.sr").dump - VCDExport(dump).write("dump.vcd") - - -if __name__ == '__main__': - main() - diff --git a/litescope/host/dump/__init__.py b/litescope/host/dump/__init__.py new file mode 100644 index 00000000..8e67c9f2 --- /dev/null +++ b/litescope/host/dump/__init__.py @@ -0,0 +1,118 @@ +def dec2bin(d, nb=0): + if d=="x": + return "x"*nb + elif d==0: + b="0" + else: + b="" + while d!=0: + b="01"[d&1]+b + d=d>>1 + return b.zfill(nb) + +def get_bits(values, width, low, high=None): + r = [] + for val in values: + t = dec2bin(val, width)[::-1] + if high == None: + t = t[low] + else: + t = t[low:high] + t = t[::-1] + t = int(t,2) + r.append(t) + return r + +class Dat(list): + def __init__(self, width): + self.width = width + + def __getitem__(self, key): + if isinstance(key, int): + return get_bits(self, self.width, key) + elif isinstance(key, slice): + if key.start != None: + start = key.start + else: + start = 0 + if key.stop != None: + stop = key.stop + else: + stop = self.width + if stop > self.width: + stop = self.width + if key.step != None: + raise KeyError + return get_bits(self, self.width, start, stop) + else: + raise KeyError + + def decode_rle(self): + rle_bit = self[-1] + rle_dat = self[:self.width-1] + + dat = Dat(self.width) + i=0 + last = 0 + for d in self: + if rle_bit[i]: + if len(dat) >= 1: + # FIX ME... why is rle_dat in reverse order... + for j in range(int(dec2bin(rle_dat[i])[::-1],2)): + dat.append(last) + else: + dat.append(d) + last = d + i +=1 + return dat + +class Var: + def __init__(self, name, width, values=[], type="wire", default="x"): + self.type = type + self.width = width + self.name = name + self.val = default + self.values = values + self.vcd_id = None + + def set_vcd_id(self, s): + self.vcd_id = s + + def __len__(self): + return len(self.values) + + def change(self, cnt): + r = "" + try : + if self.values[cnt+1] != self.val: + r += "b" + r += dec2bin(self.values[cnt+1], self.width) + r += " " + r += self.vcd_id + r += "\n" + return r + except : + return r + return r + +class Dump: + def __init__(self): + self.vars = [] + self.vcd_id = "!" + + def add(self, var): + var.set_vcd_id(self.vcd_id) + self.vcd_id = chr(ord(self.vcd_id)+1) + self.vars.append(var) + + def add_from_layout(self, layout, var): + i=0 + for s, n in layout: + self.add(Var(s, n, var[i:i+n])) + i += n + + def __len__(self): + l = 0 + for var in self.vars: + l = max(len(var),l) + return l diff --git a/litescope/host/dump/csv.py b/litescope/host/dump/csv.py new file mode 100644 index 00000000..8face7f8 --- /dev/null +++ b/litescope/host/dump/csv.py @@ -0,0 +1,52 @@ +from litescope.host.dump import * + +class CSVDump(Dump): + def __init__(self, init_dump=None): + Dump.__init__(self) + if init_dump: + self.vars = init_dump.vars + + def generate_vars(self): + r = "" + for var in self.vars: + r += var.name + r += "," + r += "\n" + for var in self.vars: + r += str(var.width) + r += "," + r += "\n" + return r + + def generate_dumpvars(self): + r = "" + for i in range(len(self)): + for var in self.vars: + try: + var.val = var.values[i] + except: + pass + if var.val == "x": + r += "x" + else: + r += dec2bin(var.val, var.width) + r += ", " + r+= "\n" + return r + + def write(self, filename): + f = open(filename, "w") + f.write(self.generate_vars()) + f.write(self.generate_dumpvars()) + f.close() + + def read(self, filename): + raise NotImplementedError("CSV files can not (yet) be read, please contribute!") + +if __name__ == '__main__': + dump = CSVDump() + dump.add(Var("foo1", 1, [0,1,0,1,0,1])) + dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0])) + ramp = [i%128 for i in range(1024)] + dump.add(Var("ramp", 16, ramp)) + dump.write("dump.csv") diff --git a/litescope/host/dump/python.py b/litescope/host/dump/python.py new file mode 100644 index 00000000..0dd9cdd1 --- /dev/null +++ b/litescope/host/dump/python.py @@ -0,0 +1,33 @@ +from litescope.host.dump import * + +class PythonDump(Dump): + def __init__(self, init_dump=None): + Dump.__init__(self) + if init_dump: + self.vars = init_dump.vars + + def generate_data(self): + r = "dump = {\n" + for var in self.vars: + r += "\"" + var.name + "\"" + r += " : " + r += str(var.values) + r += ",\n" + r += "}" + return r + + def write(self, filename): + f = open(filename, "w") + f.write(self.generate_data()) + f.close() + + def read(self, filename): + raise NotImplementedError("Python files can not (yet) be read, please contribute!") + +if __name__ == '__main__': + dump = PythonDump() + dump.add(Var("foo1", 1, [0,1,0,1,0,1])) + dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0])) + ramp = [i%128 for i in range(1024)] + dump.add(Var("ramp", 16, ramp)) + dump.write("dump.py") diff --git a/litescope/host/dump/sigrok.py b/litescope/host/dump/sigrok.py new file mode 100644 index 00000000..0d664cec --- /dev/null +++ b/litescope/host/dump/sigrok.py @@ -0,0 +1,143 @@ +import os +import math +import shutil +import zipfile +import re +from collections import OrderedDict + +from litescope.host.dump import * + +class SigrokDump(Dump): + def __init__(self, init_dump=None): + Dump.__init__(self) + if init_dump: + self.vars = init_dump.vars + + def write_version(self): + f = open("version", "w") + f.write("1") + f.close() + + def write_metadata(self, name): + f = open("metadata", "w") + r = """ +[global] +sigrok version = 0.2.0 +[device 1] +driver = litescope +capturefile = {} +unitsize = 1 +total probes = {} +samplerate = {} MHz +""".format( + name, + len(self.vars), + 50, # XXX add parameter + ) + for i, var in enumerate(self.vars): + r += "probe{} = {}\n".format(i, var.name) + f.write(r) + f.close() + + def write_data(self, name): + # XXX are probes limited to 1 bit? + data_bits = math.ceil(len(self.vars)/8)*8 + data_len = 0 + for var in self.vars: + data_len = max(data_len, len(var)) + datas = [] + for i in range(data_len): + data = 0 + for j, var in enumerate(reversed(self.vars)): + data = data << 1 + try: + data |= var.values[i] %2 + except: + pass + datas.append(data) + f = open(name, "wb") + for data in datas: + f.write(data.to_bytes(data_bits//8, "big")) + f.close() + + def zip(self, name): + f = zipfile.ZipFile(name + ".sr", "w") + os.chdir(name) + f.write("version") + f.write("metadata") + f.write(name) + os.chdir("..") + f.close() + + def write(self, filename): + name, ext = os.path.splitext(filename) + if os.path.exists(name): + shutil.rmtree(name) + os.makedirs(name) + os.chdir(name) + self.write_version() + self.write_metadata(name) + self.write_data(name) + os.chdir("..") + self.zip(name) + shutil.rmtree(name) + + def unzip(self, filename, name): + f = open(filename, "rb") + z = zipfile.ZipFile(f) + if os.path.exists(name): + shutil.rmtree(name) + os.makedirs(name) + for file in z.namelist(): + z.extract(file, name) + f.close() + + def read_metadata(self): + probes = OrderedDict() + f = open("metadata", "r") + for l in f: + m = re.search("probe([0-9]+) = (\w+)", l, re.I) + if m is not None: + index = int(m.group(1)) + name = m.group(2) + probes[name] = index + f.close() + return probes + + def read_data(self, name, total_probes): + datas = [] + f = open(name, "rb") + while True: + data = f.read(math.ceil(total_probes/8)) + if data == bytes('', "utf-8"): + break + data = int.from_bytes(data, "big") + datas.append(data) + f.close() + return datas + + def read(self, filename): + self.vars = [] + name, ext = os.path.splitext(filename) + self.unzip(filename, name) + os.chdir(name) + probes = self.read_metadata() + datas = self.read_data(name, len(probes.keys())) + os.chdir("..") + shutil.rmtree(name) + + for k, v in probes.items(): + probe_data = [] + for data in datas: + probe_data.append((data >> v) & 0x1) + self.add(Var(k, 1, probe_data)) + +if __name__ == '__main__': + dump = SigrokDump() + dump.add(Var("foo1", 1, [0,1,0,1,0,1])) + dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0])) + ramp = [i%128 for i in range(1024)] + dump.add(Var("ramp", 16, ramp)) + dump.write("dump.sr") + dump.read("dump.sr") + dump.write("dump_copy.sr") diff --git a/litescope/host/dump/vcd.py b/litescope/host/dump/vcd.py new file mode 100644 index 00000000..1646eb9f --- /dev/null +++ b/litescope/host/dump/vcd.py @@ -0,0 +1,127 @@ +import datetime +from litescope.host.dump import * + +class VCDDump(Dump): + def __init__(self, init_dump=None, timescale="1ps", comment=""): + Dump.__init__(self) + if init_dump: + self.vars = init_dump.vars + self.timescale = timescale + self.comment = comment + self.cnt = -1 + + def change(self): + r = "" + c = "" + for var in self.vars: + c += var.change(self.cnt) + if c != "": + r += "#" + r += str(self.cnt+1) + r += "\n" + r += c + return r + + def generate_date(self): + now = datetime.datetime.now() + r = "$date\n" + r += "\t" + r += now.strftime("%Y-%m-%d %H:%M") + r += "\n" + r += "$end\n" + return r + + def generate_version(self): + r = "$version\n" + r += "\tmiscope VCD dump\n" + r += "$end\n" + return r + + def generate_comment(self): + r = "$comment\n" + r += self.comment + r += "\n$end\n" + return r + + def generate_timescale(self): + r = "$timescale " + r += self.timescale + r += " $end\n" + return r + + def generate_scope(self): + r = "$scope " + r += self.timescale + r += " $end\n" + return r + + def generate_vars(self): + r = "" + for var in self.vars: + r += "$var " + r += var.type + r += " " + r += str(var.width) + r += " " + r += var.vcd_id + r += " " + r += var.name + r += " $end\n" + return r + + def generate_unscope(self): + r = "$unscope " + r += " $end\n" + return r + + def generate_enddefinitions(self): + r = "$enddefinitions " + r += " $end\n" + return r + + def generate_dumpvars(self): + r = "$dumpvars\n" + for var in self.vars: + r += "b" + r += dec2bin(var.val, var.width) + r += " " + r += var.vcd_id + r+= "\n" + r += "$end\n" + return r + + def generate_valuechange(self): + r = "" + for i in range(len(self)): + r += self.change() + self.cnt += 1 + return r + + def __repr__(self): + r = "" + + return r + + def write(self, filename): + f = open(filename, "w") + f.write(self.generate_date()) + f.write(self.generate_comment()) + f.write(self.generate_timescale()) + f.write(self.generate_scope()) + f.write(self.generate_vars()) + f.write(self.generate_unscope()) + f.write(self.generate_enddefinitions()) + f.write(self.generate_dumpvars()) + f.write(self.generate_valuechange()) + f.close() + + def read(self, filename): + raise NotImplementedError("VCD files can not (yet) be read, please contribute!") + +if __name__ == '__main__': + dump = VCDDump() + dump.add(Var("foo1", 1, [0,1,0,1,0,1])) + dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0])) + ramp = [i%128 for i in range(1024)] + dump.add(Var("ramp", 16, ramp)) + dump.write("dump.vcd") diff --git a/litescope/host/reg.py b/litescope/host/reg.py deleted file mode 100644 index 16a98d91..00000000 --- a/litescope/host/reg.py +++ /dev/null @@ -1,48 +0,0 @@ -import csv - -class MappedReg: - def __init__(self, readfn, writefn, name, addr, length, busword, mode): - self.readfn = readfn - self.writefn = writefn - self.addr = addr - self.length = length - self.busword = busword - self.mode = mode - - def read(self): - if self.mode not in ["rw", "ro"]: - raise KeyError(name + "register not readable") - r = 0 - for i in range(self.length): - r |= self.readfn(self.addr + 4*i) - if i != (self.length-1): - r <<= self.busword - return r - - def write(self, value): - if self.mode not in ["rw", "wo"]: - raise KeyError(name + "register not writable") - for i in range(self.length): - dat = (value >> ((self.length-1-i)*self.busword)) & (2**self.busword-1) - self.writefn(self.addr + 4*i, dat) - -class MappedRegs: - def __init__(self, d): - self.d = d - - def __getattr__(self, attr): - try: - return self.__dict__['d'][attr] - except KeyError: - pass - raise KeyError("No such register " + attr) - -def build_map(addrmap, busword, readfn, writefn): - csv_reader = csv.reader(open(addrmap), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - name, addr, length, mode = item - addr = int(addr.replace("0x", ""), 16) - length = int(length) - d[name] = MappedReg(readfn, writefn, name, addr, length, busword, mode) - return MappedRegs(d) \ No newline at end of file diff --git a/litescope/host/truthtable.py b/litescope/host/truthtable.py deleted file mode 100644 index 3ed6e18c..00000000 --- a/litescope/host/truthtable.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -import re -import sys - -def is_number(x): - try: - _ = float(x) - except ValueError: - return False - return True - -def remove_numbers(seq): - return [x for x in seq if not is_number(x)] - -def remove_duplicates(seq): - seen = set() - seen_add = seen.add - return [x for x in seq if x not in seen and not seen_add(x)] - -def get_operands(s): - operands = re.findall("[A-z0-9_]+", s) - operands = remove_duplicates(operands) - operands = remove_numbers(operands) - return sorted(operands) - -def gen_truth_table(s): - operands = get_operands(s) - width = len(operands) - stim = [] - for i in range(width): - stim_op = [] - for j in range(2**width): - stim_op.append((int(j/(2**i)))%2) - stim.append(stim_op) - - truth_table = [] - for i in range(2**width): - for j in range(width): - exec("%s = stim[j][i]" %operands[j]) - truth_table.append(eval(s) != 0) - return truth_table - -def main(): - print(gen_truth_table("(A&B&C)|D")) - -if __name__ == '__main__': - main() diff --git a/make.py b/make.py index f6f84f5c..f3f6304c 100644 --- a/make.py +++ b/make.py @@ -90,7 +90,7 @@ if __name__ == "__main__": /_/ A small footprint and configurable embedded FPGA - based in Migen/MiSoC + logic analyzer core powered by Migen ====== Building parameters: ====== LiscopeIO diff --git a/test/config.py b/test/config.py index 7fc57068..fb98ad1d 100644 --- a/test/config.py +++ b/test/config.py @@ -1,4 +1,4 @@ -from litescope.host.driver import LiteScopeUART2WBDriver +from litescope.host.driver.uart import LiteScopeUARTDriver csr_csv_file = "./csr.csv" busword = 32 @@ -6,4 +6,4 @@ debug_wb = False com = 3 baud = 115200 -wb = LiteScopeUART2WBDriver(com, baud, csr_csv_file, busword, debug_wb) \ No newline at end of file +wb = LiteScopeUARTDriver(com, baud, csr_csv_file, busword, debug_wb) \ No newline at end of file diff --git a/test/test_io.py b/test/test_io.py index 8f3f2e0c..431c6cb6 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -1,7 +1,7 @@ import time from config import * -from litescope.host.driver import LiteScopeIODriver +from litescope.host.driver.io import LiteScopeIODriver def led_anim0(io): for i in range(10): diff --git a/test/test_la.py b/test/test_la.py index 48b7a9ba..aa29b46b 100644 --- a/test/test_la.py +++ b/test/test_la.py @@ -1,5 +1,5 @@ from config import * -from litescope.host.driver import LiteScopeLADriver +from litescope.host.driver.la import LiteScopeLADriver wb.open() ### @@ -19,5 +19,6 @@ la.upload() la.save("dump.vcd") la.save("dump.csv") la.save("dump.py") +la.save("dump.sr") ### wb.close()