From e95e8b03b7dd00e8b6cbe687b8e1f03a37940527 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 22 Feb 2013 14:28:05 +0100 Subject: [PATCH] - reworking WIP --- README | 23 +- doc/{migScope.rst => miscope.rst} | 0 migScope/migLa.py | 36 -- migScope/recorder.py | 245 -------------- migScope/trigger.py | 387 ---------------------- {migScope => miscope}/__init__.py | 0 migScope/migIo.py => miscope/miio.py | 13 +- miscope/mila.py | 38 +++ miscope/recorder.py | 284 ++++++++++++++++ {migScope => miscope}/tools/__init__.py | 0 {migScope => miscope}/tools/conv.py | 0 {migScope => miscope}/tools/truthtable.py | 0 {migScope => miscope}/tools/vcd.py | 0 miscope/trigger.py | 309 +++++++++++++++++ setup.py | 6 +- sim/{tb_Migscope.py => tb_migcope.py} | 0 16 files changed, 654 insertions(+), 687 deletions(-) rename doc/{migScope.rst => miscope.rst} (100%) delete mode 100644 migScope/migLa.py delete mode 100644 migScope/recorder.py delete mode 100644 migScope/trigger.py rename {migScope => miscope}/__init__.py (100%) rename migScope/migIo.py => miscope/miio.py (86%) create mode 100644 miscope/mila.py create mode 100644 miscope/recorder.py rename {migScope => miscope}/tools/__init__.py (100%) rename {migScope => miscope}/tools/conv.py (100%) rename {migScope => miscope}/tools/truthtable.py (100%) rename {migScope => miscope}/tools/vcd.py (100%) create mode 100644 miscope/trigger.py rename sim/{tb_Migscope.py => tb_migcope.py} (100%) diff --git a/README b/README index 3f61f70a..222df2b8 100644 --- a/README +++ b/README @@ -6,32 +6,31 @@ Copyright 2012 / Florent Kermarrec / florent@enjoy-digital.fr - migScope + miscope -------------------------------------------------------------------------------- -[> migScope +[> miscope ------------ -migScope is a small logic analyzer to be embedded in an FPGA. +miscope is a small logic analyzer to be embedded in an FPGA. While free vendor toolchains are generally used by beginners or for prototyping -(situations where having a logic analyser in the design is generally very -helpful) free toolchains are always provided without the proprietary logic +(situations where having a logic analyser in the design is generally very +helpful) free toolchains are always provided without the proprietary logic analyzer solution... :( -Based on Migen, migScope aims to provide a free, portable and flexible +Based on Migen, miscope aims to provide a free, portable and flexible alternative to vendor's solutions! [> Specification: -migScope provides Migen cores to be embedded in the design and Python drivers to -control the logic analyzer from the Host. migScope automatically interconnects -all cores tothe CSR bus. When using Python on the Host, no needs to worry about -cores register mapping, importing migScope project gives you direct access to +miscope provides Migen cores to be embedded in the design and Python drivers to +control the logic analyzer from the Host. miscope automatically interconnects +all cores to the CSR bus. When using Python on the Host, no needs to worry about +cores register mapping, importing miscope project gives you direct access to all the cores! -migScope produces.vcd output files to be analyzed in your favorite waveform -viewer. +miscope produces.vcd output files to be analyzed in your favorite waveform viewer. [> Status: Complete flow tested on board with a classic Term. RangeDetector, EdgeDetector diff --git a/doc/migScope.rst b/doc/miscope.rst similarity index 100% rename from doc/migScope.rst rename to doc/miscope.rst diff --git a/migScope/migLa.py b/migScope/migLa.py deleted file mode 100644 index 781ccb35..00000000 --- a/migScope/migLa.py +++ /dev/null @@ -1,36 +0,0 @@ -from migen.fhdl.structure import * -from migen.bus import csr -from migen.bank import description, csrgen -from migen.bank.description import * - -import sys -sys.path.append("../") - -from migScope import trigger, recorder - -class MigLa: - def __init__(self,address, trig, rec, interface=None): - self.address = address - self.trig = trig - self.rec = rec - self.interface = interface - - self.in_trig = Signal(self.trig.trig_width) - self.in_dat = Signal(self.trig.trig_width) - - self.trig.set_address(self.address) - self.rec.set_address(self.address + 0x0200) - - self.trig.set_interface(self.interface) - self.rec.set_interface(self.interface) - - def get_fragment(self): - comb = [] - comb += [ - self.trig.in_trig.eq(self.in_trig), - ] - comb += [ - self.rec.trig_dat.eq(self.in_dat), - self.rec.trig_hit.eq(self.trig.hit) - ] - return Fragment(comb=comb) \ No newline at end of file diff --git a/migScope/recorder.py b/migScope/recorder.py deleted file mode 100644 index ebfc9d43..00000000 --- a/migScope/recorder.py +++ /dev/null @@ -1,245 +0,0 @@ -from migen.fhdl.structure import * -from migen.bus import csr -from migen.bank import description, csrgen -from migen.bank.description import * -from migen.corelogic.misc import optree - -class Storage: - # - # Definition - # - def __init__(self, width, depth): - self.width = width - self.depth = depth - self.depth_width = bits_for(self.depth) - #Control - self.rst = Signal() - self.start = Signal() - self.offset = Signal(self.depth_width) - self.size = Signal(self.depth_width) - self.done = Signal() - self.run = Signal() - - #Others - self._mem = Memory(self.width, self.depth) - - #Write Path - self.put = Signal() - self.put_dat = Signal(self.width) - self._put_ptr = Signal(self.depth_width) - self._put_ptr_stop = Signal(self.depth_width) - self._put_port = self._mem.get_port(write_capable=True) - - #Read Path - self.get = Signal() - self.get_dat = Signal(self.width) - self._get_ptr = Signal(self.depth_width) - self._get_port = self._mem.get_port(has_re=True) - - - def get_fragment(self): - comb = [] - sync = [] - memories = [self._mem] - comb += [ - self._get_port.adr.eq(self._get_ptr), - self._get_port.re.eq(self.get), - self.get_dat.eq(self._get_port.dat_r), - - self._put_port.adr.eq(self._put_ptr), - self._put_port.we.eq(self.put), - self._put_port.dat_w.eq(self.put_dat) - ] - - size_minus_offset = Signal(self.depth_width) - comb += [size_minus_offset.eq(self.size-self.offset)] - - #Control - sync += [ - If(self.rst, - self.run.eq(0), - self._put_ptr.eq(0) - ).Elif(self.start & ~self.run, - self.run.eq(1), - self._put_ptr_stop.eq(self._put_ptr + self.size - self.offset) - ).Elif(self.done, - self.run.eq(0) - ), - - If(self.put & ~self.done, - self._put_ptr.eq(self._put_ptr+1) - ), - - If(self.rst, - self.done.eq(0) - ).Elif((self._put_ptr == self._put_ptr_stop) & self.run, - self.done.eq(1) - ), - - If(self.rst, - self._get_ptr.eq(0) - ).Elif(self.start & ~self.run, - self._get_ptr.eq(self._put_ptr-self.offset-1) - ).Elif(self.get, - self._get_ptr.eq(self._get_ptr+1) - ) - ] - return Fragment(comb=comb, sync=sync, memories=memories) - -class Sequencer: - # - # Definition - # - def __init__(self,depth): - self.depth = depth - self.depth_width = bits_for(self.depth) - # Controller interface - self.ctl_rst = Signal() - self.ctl_offset = Signal(self.depth_width) - self.ctl_size = Signal(self.depth_width) - self.ctl_arm = Signal() - self.ctl_done = Signal() - self._ctl_arm_d = Signal() - # Triggers interface - self.trig_hit = Signal() - self._trig_hit_d = Signal() - # Recorder interface - self.rec_offset = Signal(self.depth_width) - self.rec_size = Signal(self.depth_width) - self.rec_start = Signal() - self.rec_done = Signal() - # Others - self.enable = Signal() - - def get_fragment(self): - comb = [] - sync = [] - #Control - sync += [ - If(self.ctl_rst, - self.enable.eq(0) - ).Elif(self.ctl_arm & ~self._ctl_arm_d, - self.enable.eq(1) - ).Elif(self.rec_done, - self.enable.eq(0) - ), - self._ctl_arm_d.eq(self.ctl_arm) - ] - sync += [self._trig_hit_d.eq(self.trig_hit)] - comb += [ - self.rec_offset.eq(self.ctl_offset), - self.rec_size.eq(self.ctl_size), - self.rec_start.eq(self.enable & (self.trig_hit & ~self._trig_hit_d)), - self.ctl_done.eq(~self.enable) - ] - return Fragment(comb=comb, sync=sync) - -class Recorder: - # - # Definition - # - def __init__(self, width, depth, address = 0x0000, interface = None): - self.address = address - self.width = width - self.depth = depth - self.depth_width = bits_for(self.depth) - self.interface = interface - - self.storage = Storage(self.width, self.depth) - self.sequencer = Sequencer(self.depth) - - # Csr interface - self._rst = RegisterField("rst", reset=1) - self._arm = RegisterField("arm", reset=0) - self._done = RegisterField("done", reset=0, access_bus=READ_ONLY, access_dev=WRITE_ONLY) - - self._size = RegisterField("size", self.depth_width, reset=1) - self._offset = RegisterField("offset", self.depth_width, reset=1) - - self._get = RegisterField("get", reset=0) - self._get_dat = RegisterField("get_dat", self.width, reset=1, access_bus=READ_ONLY, access_dev=WRITE_ONLY) - - self.regs = [self._rst, self._arm, self._done, - self._size, self._offset, - self._get, self._get_dat] - - self.bank = csrgen.Bank(self.regs,address=self.address) - - # Trigger Interface - self.trig_hit = Signal() - self.trig_dat = Signal(self.width) - - def set_address(self, address): - self.address = address - self.bank = csrgen.Bank(self.regs,address=self.address) - - def set_interface(self, interface): - self.interface = interface - - def get_fragment(self): - comb = [] - sync = [] - - _get_d = Signal() - _get_rising = Signal() - - sync += [ - _get_d.eq(self._get.field.r), - _get_rising.eq(self._get.field.r & ~_get_d) - ] - - #Bank <--> Storage / Sequencer - comb += [ - self.sequencer.ctl_rst.eq(self._rst.field.r), - self.storage.rst.eq(self._rst.field.r), - self.sequencer.ctl_offset.eq(self._offset.field.r), - self.sequencer.ctl_size.eq(self._size.field.r), - self.sequencer.ctl_arm.eq(self._arm.field.r), - self._done.field.w.eq(self.sequencer.ctl_done), - self.storage.get.eq(_get_rising), - self._get_dat.field.w.eq(self.storage.get_dat) - ] - - #Storage <--> Sequencer <--> Trigger - comb += [ - self.storage.offset.eq(self.sequencer.rec_offset), - self.storage.size.eq(self.sequencer.rec_size), - self.storage.start.eq(self.sequencer.rec_start), - self.sequencer.rec_done.eq(self.storage.done), - self.sequencer.trig_hit.eq(self.trig_hit), - self.storage.put.eq(self.sequencer.enable), - self.storage.put_dat.eq(self.trig_dat) - - ] - - return self.bank.get_fragment()+\ - self.storage.get_fragment()+self.sequencer.get_fragment()+\ - Fragment(comb=comb, sync=sync) - - # - #Driver - # - def reset(self): - self.interface.write(self.address + 0x00, 1) - self.interface.write(self.address + 0x00, 0) - - def arm(self): - self.interface.write(self.address + 0x01, 1) - self.interface.write(self.address + 0x01, 0) - - def is_done(self): - return self.interface.read(self.address + 0x02) == 1 - - def size(self, dat): - self.interface.write_n(self.address + 0x03, dat, 16) - - def offset(self, dat): - self.interface.write_n(self.address + 0x05, dat, 16) - - def read(self, size): - r = [] - for i in range(size): - self.interface.write(self.address+7, 1) - self.interface.write(self.address+7, 0) - r.append(self.interface.read_n(self.address+8,self.width)) - return r diff --git a/migScope/trigger.py b/migScope/trigger.py deleted file mode 100644 index cde9890d..00000000 --- a/migScope/trigger.py +++ /dev/null @@ -1,387 +0,0 @@ -from migen.fhdl.structure import * -from migen.bus import csr -from migen.bank import description, csrgen -from migen.bank.description import * -from migen.corelogic.misc import optree - -class Term: - # - # Definition - # - def __init__(self, width, pipe=False): - self.width = width - self.pipe = pipe - self.interface = None - - self.reg_name = "term_reg" - self.reg_base = 0 - self.reg_size = 2*width - self.words = int(2**bits_for(width-1)/8) - - self.i = Signal(self.width) - self.t = Signal(self.width) - self.m = Signal(self.width) - self.o = Signal() - - def get_fragment(self): - frag = [ - self.o.eq((self.m & self.i) == self.t) - ] - if self.pipe: - return Fragment(sync=frag) - else: - return Fragment(comb=frag) - - def connect_to_reg(self, reg): - comb = [] - comb += [self.t.eq(reg.field.r[0*self.width:1*self.width])] - comb += [self.m.eq(reg.field.r[1*self.width:2*self.width])] - return comb - # - #Driver - # - def write(self, dat, mask = None): - if mask == None: - mask = (2**self.width)-1 - self.interface.write_n(self.reg_base + self.words, dat ,self.width) - self.interface.write_n(self.reg_base, mask ,self.width) - -class RangeDetector: - # - # Definition - # - def __init__(self, width, pipe=False): - self.width = width - self.pipe = pipe - self.interface = None - - self.reg_name = "range_reg" - self.reg_base = 0 - self.reg_size = 2*width - self.words = int(2**bits_for(width-1)/8) - - self.i = Signal(self.width) - self.low = Signal(self.width) - self.high = Signal(self.width) - self.o = Signal() - - def get_fragment(self): - frag = [ - self.o.eq((self.i >= self.low) & ((self.i <= self.high))) - ] - if self.pipe: - return Fragment(sync=frag) - else: - return Fragment(comb=frag) - - def connect_to_reg(self, reg): - comb = [] - comb += [self.low.eq(reg.field.r[0*self.width:1*self.width])] - comb += [self.low.eq(reg.field.r[1*self.width:2*self.width])] - return comb - # - #Driver - # - def write_low(self, dat): - self.interface.write_n(self.reg_base, dat ,self.width) - - def write_high(self, dat): - self.interface.write_n(self.reg_base + self.words, dat ,self.width) - -class EdgeDetector: - # - # Definition - # - def __init__(self, width, pipe=False, mode = "RFB"): - self.width = width - self.pipe = pipe - self.mode = mode - self.interface = None - - self.reg_name = "edge_reg" - self.reg_base = 0 - self.reg_size = len(self.mode)*width - - self.i = Signal(self.width) - self.i_d = Signal(self.width) - if "R" in self.mode: - self.r_mask = Signal(self.width) - self.ro = Signal() - if "F" in self.mode: - self.f_mask = Signal(self.width) - self.fo = Signal() - if "B" in self.mode: - self.b_mask = Signal(self.width) - self.bo = Signal() - self.o = Signal() - - def get_fragment(self): - comb = [] - sync = [] - sync += [self.i_d.eq(self.i)] - # Rising Edge - if "R" in self.mode: - r_eq = [self.ro.eq(self.r_mask & self.i & (~self.i_d))] - if self.pipe: - sync += r_eq - else: - comb += r_eq - else: - comb += [self.ro.eq(0)] - # Falling Edge - if "F" in self.mode: - f_eq = [self.fo.eq(self.f_mask & (~ self.i) & self.i_d)] - if self.pipe: - sync += f_eq - else: - comb += f_eq - else: - comb += [self.fo.eq(0)] - # Both - if "B" in self.mode: - b_eq = [self.bo.eq(self.b_mask & self.i != self.i_d)] - if self.pipe: - sync += b_eq - else: - comb += b_eq - else: - comb += [self.bo.eq(0)] - #Output - comb += [self.o.eq(self.ro | self.fo | self.bo)] - - return Fragment(comb, sync) - - def connect_to_reg(self, reg): - comb = [] - i = 0 - if "R" in self.mode: - comb += [self.r_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] - i += 1 - if "F" in self.mode: - comb += [self.f_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] - i += 1 - if "B" in self.mode: - comb += [self.b_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] - i += 1 - return comb - - # - #Driver - # - - def get_offset(self, type): - if type == "R": - r = 0 - r = r+self.words if "F" in self.mode else r - r = r+self.words if "B" in self.mode else r - return r - elif type == "F": - r = 0 - r = r+self.words if "B" in self.mode else r - return r - elif type == "B": - r = 0 - return r - return 0 - - def write_r(self, dat): - self.interface.write_n(self.reg_base + self.get_offset("R"), dat ,self.width) - - def write_f(self, dat): - self.interface.write_n(self.reg_base + self.get_offset("F"), dat ,self.width) - - def write_b(self, dat): - self.interface.write_n(self.reg_base + self.get_offset("B"), dat ,self.width) - -class Timer: - # - # Definition - # - def __init__(self, width): - self.width = width - self.interface = None - - self.start = Signal() - self.stop = Signal() - self.clear = Signal() - - self.enable = Signal() - self.cnt = Signal(self.width) - self.cnt_max = Signal(self.width) - - self.o = Signal() - - def get_fragment(self): - comb = [] - sync = [] - sync += [ - If(self.stop, - self.enable.eq(0), - self.cnt.eq(0), - self.o.eq(0) - ).Elif(self.clear, - self.cnt.eq(0), - self.o.eq(0) - ).Elif(self.start, - self.enable.eq(1) - ).Elif(self.enable, - If(self.cnt <= self.cnt_max, - self.cnt.eq(self.cnt+1) - ).Else( - self.o.eq(1) - ) - ), - If(self.enable, - self.enable.eq(0), - self.cnt.eq(0) - ).Elif(self.clear, - self.cnt.eq(0) - ).Elif(self.start, - self.enable.eq(1) - ) - - ] - - return Fragment(comb, sync) - -class Sum: - # - # Definition - # - def __init__(self,width=4,pipe=False): - self.width = width - self.pipe = pipe - self.interface = None - - self._mem = Memory(1, 2**self.width) - - self.i = Signal(self.width) - self._o = Signal() - self.o = Signal() - self._lut_port = self._mem.get_port() - - self.reg_name = "sum_reg" - self.reg_base = 0 - self.reg_size = 32 - - self.prog = Signal() - self.prog_adr = Signal(width) - self.prog_dat = Signal() - self._prog_port = self._mem.get_port(write_capable=True) - - - def get_fragment(self): - comb = [] - sync = [] - memories = [self._mem] - comb += [ - self._lut_port.adr.eq(self.i), - self._o.eq(self._lut_port.dat_r), - - self._prog_port.adr.eq(self.prog_adr), - self._prog_port.we.eq(self.prog), - self._prog_port.dat_w.eq(self.prog_dat) - ] - - - if self.pipe: - sync += [self.o.eq(self._o)] - else: - comb += [self.o.eq(self._o)] - return Fragment(comb=comb, sync=sync, memories=memories) - - def connect_to_reg(self, reg): - comb = [] - comb += [ - self.prog_adr.eq(reg.field.r[0:16]), - self.prog_dat.eq(reg.field.r[16]), - self.prog.eq(reg.field.r[17]) - ] - return comb - - # - #Driver - # - def write(self, truth_table): - for i in range(len(truth_table)): - val = truth_table[i] - we = 1<<17 - dat = val<<16 - addr = i - self.interface.write_n(self.reg_base, we + dat + addr,self.reg_size) - self.interface.write_n(self.reg_base, dat + addr, self.reg_size) - -class Trigger: - # - # Definition - # - def __init__(self, trig_width, ports, address = 0x0000, interface = None): - self.address = address - self.trig_width = trig_width - self.ports = ports - self.interface = interface - self.sum = Sum(len(self.ports)) - - self.in_trig = Signal(self.trig_width) - - self.hit = Signal() - - # Update port reg_name - for i in range(len(self.ports)): - self.ports[i].reg_name += "_%d"%i - - # Csr interface - for port in self.ports: - setattr(self,port.reg_name,RegisterField(port.reg_name, port.reg_size, reset=0, - access_bus=WRITE_ONLY, access_dev=READ_ONLY)) - self.sum_reg = RegisterField(self.sum.reg_name, self.sum.reg_size, reset=0, access_bus=WRITE_ONLY, access_dev=READ_ONLY) - - self.regs = [] - objects = self.__dict__ - for object in sorted(objects): - if "_reg" in object: - self.regs.append(objects[object]) - self.bank = csrgen.Bank(self.regs,address=self.address) - - # Update base addr - self.set_address(self.address) - - # Update interface - self.set_interface(self.interface) - - def set_address(self, address): - self.address = address - self.bank = csrgen.Bank(self.regs,address=self.address) - for port in self.ports: - port.reg_base = self.bank.get_base(port.reg_name) - self.sum.reg_base = self.bank.get_base(self.sum.reg_name) - - def set_interface(self, interface): - self.interface = interface - for port in self.ports: - port.interface = self.interface - self.sum.interface = self.interface - - def get_fragment(self): - comb = [] - sync = [] - # Connect in_trig to input of trig elements - comb+= [port.i.eq(self.in_trig) for port in self.ports] - - # Connect output of trig elements to sum - comb+= [self.sum.i[j].eq(self.ports[j].o) for j in range(len(self.ports))] - - # Connect sum ouput to hit - comb+= [self.hit.eq(self.sum.o)] - - # Add ports & sum to frag - frag = self.bank.get_fragment() - frag += self.sum.get_fragment() - for port in self.ports: - frag += port.get_fragment() - - #Connect Registers - for port in self.ports: - comb += port.connect_to_reg(getattr(self, port.reg_name)) - comb += self.sum.connect_to_reg(self.sum_reg) - return frag + Fragment(comb=comb, sync=sync) diff --git a/migScope/__init__.py b/miscope/__init__.py similarity index 100% rename from migScope/__init__.py rename to miscope/__init__.py diff --git a/migScope/migIo.py b/miscope/miio.py similarity index 86% rename from migScope/migIo.py rename to miscope/miio.py index 5e4ff4fa..a65f6fb6 100644 --- a/migScope/migIo.py +++ b/miscope/miio.py @@ -3,34 +3,39 @@ from migen.bus import csr from migen.bank import description, csrgen from migen.bank.description import * - -class MigIo: +class MiIo: # # Definition # - def __init__(self,address, width, mode = "IO", interface=None): + def __init__(self, address, width, mode = "IO", interface=None): self.address = address self.width = width self.mode = mode self.interface = interface self.words = int(2**bits_for(width-1)/8) + if "I" in self.mode: self.i = Signal(self.width) self.ireg = description.RegisterField("i", self.width, READ_ONLY, WRITE_ONLY) self.ireg.field.w.name_override = "inputs" + if "O" in self.mode: self.o = Signal(self.width) self.oreg = description.RegisterField("o", self.width) self.oreg.field.r.name_override = "ouptuts" + self.bank = csrgen.Bank([self.oreg, self.ireg], address=self.address) def get_fragment(self): comb = [] + if "I" in self.mode: comb += [self.ireg.field.w.eq(self.i)] + if "O" in self.mode: comb += [self.o.eq(self.oreg.field.r)] - return Fragment(comb=comb) + self.bank.get_fragment() + + return Fragment(comb) + self.bank.get_fragment() # #Driver # diff --git a/miscope/mila.py b/miscope/mila.py new file mode 100644 index 00000000..e49887ba --- /dev/null +++ b/miscope/mila.py @@ -0,0 +1,38 @@ +from migen.fhdl.structure import * +from migen.bus import csr +from migen.bank import description, csrgen +from migen.bank.description import * + +from miscope import trigger, recorder + +class MiLa: + def __init__(self, address, trigger, recorder, interface=None): + + self.trigger = trigger + self.recorder = recorder + self.interface = interface + + self.trig = Signal(self.trigger.trig_w) + self.dat = Signal(self.trigger.trig_w) + + self.set_address(address) + self.set_interface(interface) + + def set_interface(self, i): + self.interface = i + self.trigger.set_interface(i) + self.recorder.set_interface(i) + + def set_address(self, i): + self.address = address + self.trigger.set_address(self.address) + self.recorder.set_address(self.address + 0x0200) + + def get_fragment(self): + comb =[ + self.trigger.trig.eq(self.trig), + + self.recorder.dat.eq(self.dat), + self.recorder.hit.eq(self.trigger.hit) + ] + return Fragment(comb) \ No newline at end of file diff --git a/miscope/recorder.py b/miscope/recorder.py new file mode 100644 index 00000000..022596d2 --- /dev/null +++ b/miscope/recorder.py @@ -0,0 +1,284 @@ +from migen.fhdl.structure import * +from migen.bus import csr +from migen.bank import description, csrgen +from migen.bank.description import * +from migen.corelogic.misc import optree + +class Storage: + # + # Definition + # + def __init__(self, width, depth): + self.width = width + self.depth = depth + self.depth_width = bits_for(self.depth) + + # Control + self.rst = Signal() + self.start = Signal() + self.offset = Signal(self.depth_width) + self.size = Signal(self.depth_width) + self.done = Signal() + + # Push Path + self.push_stb = Signal() + self.push_dat = Signal(self.width) + self._push_ptr = Signal(self.depth_width) + self._push_ptr_stop = Signal(self.depth_width) + + # Pull Path + self.pull_stb = Signal() + self.pull_dat = Signal(self.width) + self._pull_ptr = Signal(self.depth_width) + + # Memory + self._mem = Memory(self.width, self.depth) + self._push_port = self._mem.get_port(write_capable=True) + self._pull_port = self._mem.get_port(has_re=True) + + def get_fragment(self): + comb = [ + self._push_port.adr.eq(self._push_ptr), + self._push_port.we.eq(self.push), + self._push_port.dat_w.eq(self.push_dat), + + self._pull_port.adr.eq(self._pull_ptr), + self._pull_port.re.eq(self.pull_stb), + self.pull_dat.eq(self._pull_port.dat_r) + ] + + size_minus_offset = Signal(self.depth_width) + comb += [size_minus_offset.eq(self.size-self.offset)] + + idle_rising = Signal() + idle_ongoing = Signal() + active_rising = Signal() + active_ongoing = Signal() + + # FSM + fsm = FSM("IDLE", "ACTIVE") + + # Idle + fsm.act(fsm.IDLE, + If(self.start, + fsm.next_state(fsm.PUSH), + active_rising.eq(1) + ), + idle_ongoing.eq(1) + ) + + # Active + fsm.act(fsm.ACTIVE, + If(self.done | self.rst, + fsm.next_state(fsm.IDLE), + idle_rising.eq(1) + ), + active_ongoing.eq(1) + ) + + sync +=[ + If(active_rising, + self._push_ptr_stop.eq(self._push_ptr + self.size - self.offset), + self._pull_ptr.eq(self._push_ptr-self.offset-1) + ).Else( + If(self.pull_stb, self._pull_ptr.eq(self._pull_ptr+1)) + ), + If(self.push_stb, self._push_ptr.eq(self._push_ptr+1)), + ] + comb +=[self.done.eq((self._put_ptr == self._put_ptr_stop) & active_ongoing)] + + return Fragment(comb, sync, memories=self._mem) + +class Sequencer: + # + # Definition + # + def __init__(self,depth): + self.depth = depth + self.depth_width = bits_for(self.depth) + + # Controller interface + self.ctl_rst = Signal() + self.ctl_offset = Signal(self.depth_width) + self.ctl_size = Signal(self.depth_width) + self.ctl_arm = Signal() + self.ctl_done = Signal() + self._ctl_arm_d = Signal() + + # Trigger interface + self.hit = Signal() + + # Recorder interface + self.rec_offset = Signal(self.depth_width) + self.rec_size = Signal(self.depth_width) + self.rec_start = Signal() + self.rec_done = Signal() + + # Others + self.enable = Signal() + + def get_fragment(self): + + idle_rising = Signal() + idle_ongoing = Signal() + active_rising = Signal() + active_ongoing = Signal() + + # FSM + fsm = FSM("IDLE", "ACTIVE") + + # Idle + fsm.act(fsm.IDLE, + If(self.ctl_arm, + fsm.next_state(fsm.PUSH), + active_rising.eq(1) + ), + idle_ongoing.eq(1) + ) + + # Active + fsm.act(fsm.ACTIVE, + If(self.rec_done | self.rst, + fsm.next_state(fsm.IDLE), + idle_rising.eq(1) + ), + active_ongoing.eq(1) + ) + comb +=[self.enable.eq(active_ongoing)] + + # trig_hit rising_edge + _hit_d = Signal() + _hit_rising = Signal() + sync +=[_hit_d.eq(self.hit)] + comb +=[_hit_rising.eq(self.hit & ~_hit_d] + + # connexion + comb = [ + self.rec_offset.eq(self.ctl_offset), + self.rec_size.eq(self.ctl_size), + self.rec_start.eq(self.enable & _hit_rising), + self.ctl_done.eq(~self.enable) + ] + return Fragment(comb, sync) + + +REC_RST_BASE = 0x00 +REC_ARM_BASE = 0x01 +REC_DONE_BASE = 0x02 +REC_SIZE_BASE = 0x03 +REC_OFFSET_BASE = 0x05 +REC_READ_BASE = 0x07 +REC_READ_DATA_BASE = 0x09 + +class Recorder: + # + # Definition + # + def __init__(self, width, depth, address = 0x0000, interface = None): + self.width = width + self.depth = depth + self.depth_width = bits_for(self.depth) + + self.storage = Storage(self.width, self.depth) + self.sequencer = Sequencer(self.depth) + + # csr interface + self._rst = RegisterField("rst", reset=1) + self._arm = RegisterField("arm", reset=0) + self._done = RegisterField("done", reset=0, access_bus=READ_ONLY, + access_dev=WRITE_ONLY) + + self._size = RegisterField("size", self.depth_width, reset=1) + self._offset = RegisterField("offset", self.depth_width, reset=1) + + self._pull_stb = RegisterField("pull_stb", reset=0) + self._pull_dat = RegisterField("pull_dat", self.width, reset=1, + access_bus=READ_ONLY, access_dev=WRITE_ONLY) + + self.regs = [self._rst, self._arm, self._done, self._size, self._offset, + self._get, self._get_dat] + + self.bank = csrgen.Bank(self.regs, address=address) + + # set address / interface + self.set_address(address) + self.set_interface(interface) + + # trigger Interface + self.hit = Signal() + self.dat = Signal(self.width) + + def set_address(self, address): + self.address = address + self.bank = csrgen.Bank(self.regs,address=self.address) + + def set_interface(self, interface): + self.interface = interface + + def get_fragment(self): + _pull_d = Signal() + _pull_rising = Signal() + + sync = [ + _pull_d.eq(self._pull.field.r), + _pull_rising.eq(self._pull.field.r & ~_pull_d) + ] + + # Bank <--> Storage / Sequencer + comb = [ + self.sequencer.ctl_rst.eq(self._rst.field.r), + self.storage.rst.eq(self._rst.field.r), + + self.sequencer.ctl_offset.eq(self._offset.field.r), + self.sequencer.ctl_size.eq(self._size.field.r), + self.sequencer.ctl_arm.eq(self._arm.field.r), + + self._done.field.w.eq(self.sequencer.ctl_done), + + self.storage.pull_stb.eq(_pull_rising), + self._pull_dat.field.w.eq(self.storage.pull_dat) + ] + + # Storage <--> Sequencer <--> Trigger + comb += [ + self.storage.offset.eq(self.sequencer.rec_offset), + self.storage.size.eq(self.sequencer.rec_size), + self.storage.start.eq(self.sequencer.rec_start), + + self.sequencer.rec_done.eq(self.storage.done), + self.sequencer.hit.eq(self.hit), + + self.storage.put_stb.eq(self.sequencer.enable), + self.storage.put_dat.eq(self.dat) + ] + + return self.bank.get_fragment() + Fragment(comb, sync) +\ + self.storage.get_fragment() + self.sequencer.get_fragment() + + # + #Driver + # + def reset(self): + self.interface.write(self.address + REC_RST_BASE, 1) + self.interface.write(self.address + REC_RST_BASE, 0) + + def arm(self): + self.interface.write(self.address + REC_ARM_BASE, 1) + self.interface.write(self.address + REC_ARM_BASE, 0) + + def is_done(self): + return self.interface.read(self.address + REC_DONE_BASE) == 1 + + def size(self, dat): + self.interface.write_n(self.address + REC_SIZE_BASE, dat, 16) + + def offset(self, dat): + self.interface.write_n(self.address + REC_OFFSET_BASE, dat, 16) + + def read(self, size): + r = [] + for i in range(size): + self.interface.write(self.address + REC_READ_BASE, 1) + self.interface.write(self.address + REC_READ_BASE, 0) + r.append(self.interface.read_n(self.address + REC_READ_DATA_BASE, self.width)) + return r diff --git a/migScope/tools/__init__.py b/miscope/tools/__init__.py similarity index 100% rename from migScope/tools/__init__.py rename to miscope/tools/__init__.py diff --git a/migScope/tools/conv.py b/miscope/tools/conv.py similarity index 100% rename from migScope/tools/conv.py rename to miscope/tools/conv.py diff --git a/migScope/tools/truthtable.py b/miscope/tools/truthtable.py similarity index 100% rename from migScope/tools/truthtable.py rename to miscope/tools/truthtable.py diff --git a/migScope/tools/vcd.py b/miscope/tools/vcd.py similarity index 100% rename from migScope/tools/vcd.py rename to miscope/tools/vcd.py diff --git a/miscope/trigger.py b/miscope/trigger.py new file mode 100644 index 00000000..f6dcddb9 --- /dev/null +++ b/miscope/trigger.py @@ -0,0 +1,309 @@ +from migen.fhdl.structure import * +from migen.bus import csr +from migen.bank import description, csrgen +from migen.bank.description import * +from migen.corelogic.misc import optree + + +class RegParams: + def __init__(self, name, base, width, nb): + self.name = name + self.base = base + self.width = width + self.nb = nb + + self.size = nb*width + self.words = int(2**bits_for(self.width-1)/8) + +def list_regs(objects): + r = [] + for object in objects: + if "_reg" in object: + r.append(objects[object]) + return r + +class Term: + # + # Definition + # + def __init__(self, width): + self.width = width + self.interface = None + + self.i = Signal(width) + self.t = Signal(width) + self.m = Signal(width) + self.o = Signal() + + self.reg_p = RegParams("term_reg", 0, width, 2) + + def get_registers(self, reg): + comb = [self.t.eq(reg.field.r[0*self.width:1*self.width])] + comb += [self.m.eq(reg.field.r[1*self.width:2*self.width])] + return comb + + def get_fragment(self, reg): + comb = [self.o.eq((self.m & self.i) == self.t)] + comb += self.get_registers(reg) + return Fragment(comb) + + # + # Driver + # + def write(self, dat, mask=None): + if mask is None: + mask = (2**self.width)-1 + self.interface.write_n(self.reg_p.base + self.reg_p.words, dat, self.width) + self.interface.write_n(self.reg_p.base, mask, self.width) + +class RangeDetector: + # + # Definition + # + def __init__(self, width): + self.width = width + self.pipe = pipe + self.interface = None + + self.reg_p = RegParams("range_reg", 0, width, 2) + + self.i = Signal(width) + self.low = Signal(width) + self.high = Signal(width) + self.o = Signal() + + def get_registers(self, reg): + comb = [self.low.eq(reg.field.r[0*self.width:1*self.width])] + comb += [self.low.eq(reg.field.r[1*self.width:2*self.width])] + return comb + + def get_fragment(self, reg): + comb = [self.o.eq((self.i >= self.low) & (self.i <= self.high))] + comb += self.get_registers(reg) + return Fragment(comb) + # + # Driver + # + def write_low(self, dat): + self.interface.write_n(self.reg_p.base, dat ,self.width) + + def write_high(self, dat): + self.interface.write_n(self.reg_p.base + self.reg_p.words, dat ,self.width) + +class EdgeDetector: + # + # Definition + # + def __init__(self, width, mode = "RFB"): + self.width = width + self.mode = mode + self.interface = None + + self.reg_p = RegParams("edge_reg", 0, width, len(self.mode) + + self.i = Signal(self.width) + self.i_d = Signal(self.width) + if "R" in self.mode: + self.r_mask = Signal(self.width) + self.ro = Signal() + if "F" in self.mode: + self.f_mask = Signal(self.width) + self.fo = Signal() + if "B" in self.mode: + self.b_mask = Signal(self.width) + self.bo = Signal() + self.o = Signal() + + def get_registers(self, reg): + comb = [] + i = 0 + if "R" in self.mode: + comb += [self.r_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] + i += 1 + if "F" in self.mode: + comb += [self.f_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] + i += 1 + if "B" in self.mode: + comb += [self.b_mask.eq(reg.field.r[i*self.width:(i+1)*self.width])] + i += 1 + return comb + + def get_fragment(self, reg): + comb = [] + sync = [self.i_d.eq(self.i)] + + # Rising Edge + if "R" in self.mode: + comb += [self.ro.eq(self.r_mask & self.i & (~self.i_d))] + else: + comb += [self.ro.eq(0)] + + # Falling Edge + if "F" in self.mode: + comb += [self.fo.eq(self.f_mask & (~ self.i) & self.i_d)] + else: + comb += [self.fo.eq(0)] + + # Both + if "B" in self.mode: + comb += [self.bo.eq(self.b_mask & self.i != self.i_d)] + else: + comb += [self.bo.eq(0)] + + # Output + comb += [self.o.eq(self.ro | self.fo | self.bo)] + + # Registers + comb += self.get_registers(reg) + + return Fragment(comb, sync) + + # + # Driver + # + def get_offset(self, type): + if type == "R": + r = 0 + r = r + self.words if "F" in self.mode else r + r = r + self.words if "B" in self.mode else r + return r + elif type == "F": + r = 0 + r = r + self.words if "B" in self.mode else r + return r + elif type == "B": + r = 0 + return r + return 0 + + def write_r(self, dat): + self.interface.write_n(self.reg_p.base + self.get_offset("R"), dat ,self.width) + + def write_f(self, dat): + self.interface.write_n(self.reg_p.base + self.get_offset("F"), dat ,self.width) + + def write_b(self, dat): + self.interface.write_n(self.reg_p.base + self.get_offset("B"), dat ,self.width) + +class Sum: + # + # Definition + # + def __init__(self, width=4): + self.width = width + self.interface = None + + + + self.i = Signal(self.width) + self._o = Signal() + self.o = Signal() + + self.reg_p = RegParams("sum_reg", 0, 8, 4) + + self.prog_stb = Signal() + self.prog_adr = Signal(width) + self.prog_dat = Signal() + + self._mem = Memory(1, 2**self.width) + self._lut_port = self._mem.get_port() + self._prog_port = self._mem.get_port(write_capable=True) + + def get_registers(self, reg): + comb = [ + self.prog_adr.eq(reg.field.r[0:16]), + self.prog_dat.eq(reg.field.r[16]), + self.prog_stb.eq(reg.field.r[17]) + ] + return comb + + def get_fragment(self, reg): + comb = [ + self._lut_port.adr.eq(self.i), + self._o.eq(self._lut_port.dat_r), + + self._prog_port.adr.eq(self.prog_adr), + self._prog_port.we.eq(self.prog_stb), + self._prog_port.dat_w.eq(self.prog_dat) + + self.o.eq(self._o) + ] + comb += get_registers(reg) + return Fragment(comb, sync, memories=self._mem) + + # + #Driver + # + def write(self, truth_table): + for i in range(len(truth_table)): + val = truth_table[i] + we = 1<<17 + dat = val<<16 + addr = i + self.interface.write_n(self.reg_p.base, we + dat + addr, self.reg_size) + self.interface.write_n(self.reg_p.base, dat + addr, self.reg_size) + +class Trigger: + # + # Definition + # + def __init__(self, trig_w, ports, address=0x0000, interface=None): + self.trig_w = trig_w + self.ports = ports + + self.sum = Sum(len(ports)) + self.trig = Signal(self.trig_w) + self.hit = Signal() + + # insert port number in port reg name + for i in range(len(self.ports)): + self.ports[i].reg_p.name += "_%d"%i + + # generate ports csr registers fields + for port in self.ports: + rf = RegisterField(port.reg_p.name, port.reg_p.size, reset=0, + access_bus=WRITE_ONLY, access_dev=READ_ONLY) + setattr(self, port.reg_name, rf) + + # generate sum csr registers fields + self.sum_reg = RegisterField(self.sum.reg_p.name, self.sum.reg_p.size, reset=0, + access_bus=WRITE_ONLY, access_dev=READ_ONLY) + + # generate registers + self.regs = list_regs(self.__dict__) + self.bank = csrgen.Bank(self.regs, address=address) + + # update base addr & interface + self.set_address(self.address) + self.set_interface(self.interface) + + def set_address(self, address): + self.address = address + self.bank = csrgen.Bank(self.regs,address=self.address) + for port in self.ports: + port.reg_p.base = self.bank.get_base(port.reg_p.name) + self.sum.reg_p.base = self.bank.get_base(self.sum.reg_p.name) + + def set_interface(self, interface): + self.interface = interface + for port in self.ports: + port.interface = self.interface + self.sum.interface = self.interface + + def get_fragment(self): + # connect trig to input of each trig element + comb = [port.i.eq(self.in_trig) for port in self.ports] + + # connect output of trig elements to sum + comb += [self.sum.i[j].eq(self.ports[j].o) for j in range(len(self.ports))] + + # connect sum ouput to hit + comb += [self.hit.eq(self.sum.o)] + + # add ports & sum to frag + frag = self.bank.get_fragment() + frag += self.sum.get_fragment(self.sum_reg) + for port in self.ports: + frag += port.get_fragment(getattr(self, port.reg_name)) + + return frag + Fragment(comb) diff --git a/setup.py b/setup.py index ab62935c..de34491d 100644 --- a/setup.py +++ b/setup.py @@ -9,18 +9,18 @@ README = open(os.path.join(here, "README")).read() required_version = (3, 1) if sys.version_info < required_version: - raise SystemExit("MigScope requires python {0} or greater".format( + raise SystemExit("Migscope requires python {0} or greater".format( ".".join(map(str, required_version)))) setup( - name="migscope", + name="miscope", version="unknown", description="Migen based Fpga logic analyzer", long_description=README, author="Florent Kermarrec", author_email="florent@enjoy-digital.fr", url="http://enjoy-digital.fr", - download_url="https://github.com/Florent-Kermarrec/migScope", + download_url="https://github.com/Florent-Kermarrec/miscope", packages=find_packages(here), license="GPL", platforms=["Any"], diff --git a/sim/tb_Migscope.py b/sim/tb_migcope.py similarity index 100% rename from sim/tb_Migscope.py rename to sim/tb_migcope.py -- 2.30.2