diff --git a/litescope/bridge/uart2wb.py b/litescope/bridge/uart2wb.py index 697843b38..d8953eabb 100644 --- a/litescope/bridge/uart2wb.py +++ b/litescope/bridge/uart2wb.py @@ -1,14 +1,9 @@ -from migen.fhdl.std import * -from migen.genlib.record import * -from migen.genlib.fsm import FSM, NextState -from migen.genlib.misc import chooser -from migen.bank.description import * +from litescope.common import * from migen.bus import wishbone +from migen.genlib.misc import chooser from misoclib.uart import UARTRX, UARTTX -from litescope.common import * - class UART(Module, AutoCSR): def __init__(self, pads, clk_freq, baud=115200): self._tuning_word = CSRStorage(32, reset=int((baud/clk_freq)*2**32)) diff --git a/litescope/common.py b/litescope/common.py index b6bc372e9..de47205bf 100644 --- a/litescope/common.py +++ b/litescope/common.py @@ -1,16 +1,16 @@ -from migen.genlib.record import * +from migen.fhdl.std import * +from migen.bank.description import * +from migen.genlib.fsm import FSM, NextState +from migen.flow.actor import * +from migen.actorlib.fifo import AsyncFIFO, SyncFIFO +from migen.flow.plumbing import Buffer +from migen.fhdl.specials import Memory -def dat_layout(dw): - return [ - ("stb", 1, DIR_M_TO_S), - ("dat", dw, DIR_M_TO_S) - ] +def data_layout(dw): + return [("data", dw, DIR_M_TO_S)] def hit_layout(): - return [ - ("stb", 1, DIR_M_TO_S), - ("hit", 1, DIR_M_TO_S) - ] + return [("hit", 1, DIR_M_TO_S)] @DecorateModule(InsertReset) @DecorateModule(InsertCE) diff --git a/litescope/core/port.py b/litescope/core/port.py new file mode 100644 index 000000000..b19c8f2de --- /dev/null +++ b/litescope/core/port.py @@ -0,0 +1,94 @@ +from litescope.common import * + +class LiteScopeTerm(Module): + def __init__(self, dw): + self.dw = dw + self.sink = sink = Sink(data_layout(dw)) + self.source = source = Source(hit_layout()) + + self.trig = Signal(dw) + self.mask = Signal(dw) + ### + self.comb += [ + source.stb.eq(sink.stb), + source.hit.eq((sink.data & self.mask) == self.trig), + sink.ack.eq(source.ack) + ] + +class LiteScopeTermCSR(LiteScopeTerm, AutoCSR): + def __init__(self, dw): + LiteScopeTerm.__init__(self, dw) + self._trig = CSRStorage(dw) + self._mask = CSRStorage(dw) + ### + self.comb += [ + self.trig.eq(self._trig.storage), + self.mask.eq(self._mask.storage) + ] + +class LiteScopeRangeDetector(Module): + def __init__(self, dw): + self.dw = dw + self.sink = sink = Sink(data_layout(dw)) + self.source = source = Source(hit_layout()) + + self.low = Signal(dw) + self.high = Signal(dw) + ### + self.comb += [ + source.stb.eq(sink.stb), + source.hit.eq((sink.data >= self.low) & (sink.data <= self.high)), + sink.ack.eq(source.ack) + ] + +class LiteScopeRangeDetectorCSR(LiteScopeRangeDetector, AutoCSR): + def __init__(self, dw): + LiteScopeRangeDetector.__init__(self, dw) + self._low = CSRStorage(dw) + self._high = CSRStorage(dw) + ### + self.comb += [ + self.low.eq(self._low.storage), + self.high.eq(self._high.storage) + ] + +class LiteScopeEdgeDetector(Module): + def __init__(self, dw): + self.dw = dw + self.sink = sink = Sink(data_layout(dw)) + self.source = source = Source(hit_layout()) + + self.rising_mask = Signal(dw) + self.falling_mask = Signal(dw) + self.both_mask = Signal(dw) + ### + self.buffer = Buffer(self.sink.description) + self.comb += Record.connect(self.sink, self.buffer.sink) + + rising = Signal(dw) + rising.eq(self.rising_mask & sink.data & ~self.buffer.source.data) + + falling = Signal(dw) + falling.eq(self.falling_mask & sink.data & ~self.buffer.source.data) + + both = Signal(dw) + both.eq(self.both_mask & sink.data & ~self.buffer.source.data) + + self.comb += [ + source.stb.eq(sink.stb & self.buffer.source.stb), + self.buffer.source.ack.eq(source.ack), + source.hit.eq(rising | falling | both) + ] + +class LiteScopeEdgeDetectorCSR(LiteScopeEdgeDetector, AutoCSR): + def __init__(self, dw): + LiteScopeEdgeDetector.__init__(self, dw) + self._rising = CSRStorage(dw) + self._falling = CSRStorage(dw) + self._both = CSRStorage(dw) + ### + self.comb += [ + self.rising.eq(self._rising.storage), + self.falling.eq(self._falling.storage), + self.both.eq(self._both.storage) + ] diff --git a/litescope/core/storage.py b/litescope/core/storage.py index cbd14c938..095a12a80 100644 --- a/litescope/core/storage.py +++ b/litescope/core/storage.py @@ -1,27 +1,17 @@ -from migen.fhdl.std import * -from migen.bank.description import * -from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO -from migen.genlib.fsm import FSM, NextState -from migen.genlib.record import * - from litescope.common import * -class LiteScopeRunLengthEncoder(Module, AutoCSR): - def __init__(self, width, length=1024): - self.width = width +class LiteScopeRunLengthEncoder(Module): + def __init__(self, dw, length=1024): + self.dw = dw self.length = length - self.sink = Record(dat_layout(width)) - self.source = Record(dat_layout(width)) - - self._enable = CSRStorage() + self.sink = sink = Sink(data_layout(dw)) + self.source = source = Source(data_layout(dw)) + self.enable = Signal() ### - - enable = self._enable.storage - - sink_d = Record(dat_layout(width)) - self.sync += If(self.sink.stb, sink_d.eq(self.sink)) + sink_d = Sink(data_layout(dw)) + self.sync += If(sink.stb, sink_d.eq(sink)) cnt = Signal(max=length) cnt_inc = Signal() @@ -37,78 +27,79 @@ class LiteScopeRunLengthEncoder(Module, AutoCSR): self.comb += cnt_max.eq(cnt == length) change = Signal() - self.comb += change.eq(self.sink.stb & (self.sink.dat != sink_d.dat)) + self.comb += change.eq(sink.stb & (sink.dat != sink_d.dat)) fsm = FSM(reset_state="BYPASS") self.submodules += fsm - fsm.act("BYPASS", - sink_d.connect(self.source), + Record.connect(sink_d, source), cnt_reset.eq(1), - If(enable & ~change & self.sink.stb, NextState("COUNT")) + If(self.enable & ~change & sink.stb, NextState("COUNT")) ) - fsm.act("COUNT", - cnt_inc.eq(self.sink.stb), - If(change | cnt_max | ~enable, - self.source.stb.eq(1), - self.source.dat[width-1].eq(1), # Set RLE bit - self.source.dat[:flen(cnt)].eq(cnt), + cnt_inc.eq(sink.stb), + If(change | cnt_max | ~self.enable, + source.stb.eq(1), + source.dat[dw-1].eq(1), # Set RLE bit + source.dat[:flen(cnt)].eq(cnt), NextState("BYPASS") ) - ), + ) + +class LiteScopeRunLengthEncoderCSR(Module, AutoCSR): + def __init__(self, rle): + self.submodules += rle + self._enable = CSRStorage() + ### + self.comb += rle.enable.eq(self_enable.storage) class LiteScopeRecorder(Module, AutoCSR): - def __init__(self, width, depth): - self.width = width + def __init__(self, dw, depth): + self.dw = dw - self.trig_sink = Record(hit_layout()) - self.dat_sink = Record(dat_layout(width)) + self.trigger_sink = trigger_sink = Sink(hit_layout()) + self.data_sink = data_sink = Sink(data_layout(dw)) self._trigger = CSR() self._length = CSRStorage(bits_for(depth)) self._offset = CSRStorage(bits_for(depth)) self._done = CSRStatus() - self._read_en = CSR() - self._read_empty = CSRStatus() - self._read_dat = CSRStatus(width) + self._source_stb = CSRStatus() + self._source_ack = CSR() + self._source_data = CSRStatus(dw) ### - fifo = InsertReset(SyncFIFO(width, depth)) + fifo = InsertReset(SyncFIFO(data_layout(dw), depth, buffered=True)) self.submodules += fifo fsm = FSM(reset_state="IDLE") self.submodules += fsm - - self.comb += [ - self._read_empty.status.eq(~fifo.readable), - self._read_dat.status.eq(fifo.dout), + self._source_stb.status.eq(fifo.source.stb), + self._source_data.status.eq(fifo.source.data) ] - fsm.act("IDLE", - If(self._trigger.re & self._trigger.r, + self._done.status.eq(1), + If(self._trigger.re, NextState("PRE_HIT_RECORDING"), fifo.reset.eq(1), ), - fifo.re.eq(self._read_en.re & self._read_en.r), - self._done.status.eq(1) + fifo.source.ack.eq(self._source_ack.re) ) - fsm.act("PRE_HIT_RECORDING", - fifo.we.eq(self.dat_sink.stb), - fifo.din.eq(self.dat_sink.dat), + fifo.sink.stb.eq(data_sink.stb), + fifo.sink.data.eq(data_sink.data), + data_sink.ack.eq(fifo.sink.ack), - fifo.re.eq(fifo.level >= self._offset.storage), - - If(self.trig_sink.stb & self.trig_sink.hit, NextState("POST_HIT_RECORDING")) + fifo.source.ack.eq(fifo.fifo.level >= self._offset.storage), + If(trigger_sink.stb & trigger_sink.hit, NextState("POST_HIT_RECORDING")) ) - fsm.act("POST_HIT_RECORDING", - fifo.we.eq(self.dat_sink.stb), - fifo.din.eq(self.dat_sink.dat), + fifo.sink.stb.eq(data_sink.stb), + fifo.sink.data.eq(data_sink.data), + data_sink.ack.eq(fifo.sink.ack), - If(~fifo.writable | (fifo.level >= self._length.storage), NextState("IDLE")) + If(~fifo.sink.ack | (fifo.fifo.level >= self._length.storage), NextState("IDLE")) ) diff --git a/litescope/core/trigger.py b/litescope/core/trigger.py index f9b792bec..f1c16877a 100644 --- a/litescope/core/trigger.py +++ b/litescope/core/trigger.py @@ -1,140 +1,73 @@ -from migen.fhdl.std import * -from migen.fhdl.specials import Memory -from migen.bank.description import * -from migen.genlib.record import * - from litescope.common import * -class LiteScopeTerm(Module, AutoCSR): - def __init__(self, width): - self.width = width - - self.sink = Record(dat_layout(width)) - self.source = Record(hit_layout()) - - self._trig = CSRStorage(width) - self._mask = CSRStorage(width) - - ### - - trig = self._trig.storage - mask = self._mask.storage - dat = self.sink.dat - hit = self.source.hit - - self.comb += [ - hit.eq((dat & mask) == trig), - self.source.stb.eq(self.sink.stb) - ] - -class LiteScopeRangeDetector(Module, AutoCSR): - def __init__(self, width): - self.width = width - - self.sink = Record(dat_layout(width)) - self.source = Record(hit_layout()) - - self._low = CSRStorage(width) - self._high = CSRStorage(width) - - ### - - low = self._low.storage - high = self._high.storage - dat = self.sink.dat - hit = self.source.hit - - self.comb += [ - hit.eq((dat >= low) & (dat <= high)), - self.source.stb.eq(self.sink.stb) - ] - -class LiteScopeEdgeDetector(Module, AutoCSR): - def __init__(self, width): - self.width = width - - self.sink = Record(dat_layout(width)) - self.source = Record(hit_layout()) - - self._rising_mask = CSRStorage(width) - self._falling_mask = CSRStorage(width) - self._both_mask = CSRStorage(width) - - ### - - rising_mask = self._rising_mask.storage - falling_mask = self._falling_mask.storage - both_mask = self._both_mask.storage - - dat = self.sink.dat - dat_d = Signal(width) - rising_hit = Signal() - falling_hit = Signal() - both_hit = Signal() - hit = self.source.hit - - self.sync += dat_d.eq(dat) - - self.comb += [ - rising_hit.eq(rising_mask & dat & ~dat_d), - falling_hit.eq(rising_mask & ~dat & dat_d), - both_hit.eq((both_mask & dat) != (both_mask & dat_d)), - hit.eq(rising_hit | falling_hit | both_hit), - self.source.stb.eq(self.sink.stb) - ] - class LiteScopeSum(Module, AutoCSR): - def __init__(self, ports=4): + def __init__(self, ports): + self.sinks = sinks = [Sink(hit_layout()) for i in range(ports)] + self.source = source = Source(hit_layout()) - self.sinks = [Record(hit_layout()) for p in range(ports)] - self.source = Record(hit_layout()) - - self._prog_we = CSRStorage() - self._prog_adr = CSRStorage(ports) #FIXME - self._prog_dat = CSRStorage() + self.prog_we = Signal() + self.prog_adr = Signal(ports) + self.prog_dat = Signal() mem = Memory(1, 2**ports) - lut_port = mem.get_port() - prog_port = mem.get_port(write_capable=True) - - self.specials += mem, lut_port, prog_port + lut = mem.get_port() + prog = mem.get_port(write_capable=True) + self.specials += mem, lut, prog ### - # Lut prog + # program port self.comb += [ - prog_port.we.eq(self._prog_we.storage), - prog_port.adr.eq(self._prog_adr.storage), - prog_port.dat_w.eq(self._prog_dat.storage) + prog.we.eq(self.prog_we), + prog.adr.eq(self.prog_adr), + prog.dat_w.eq(self.prog_dat) ] - # Lut read - for i, sink in enumerate(self.sinks): - self.comb += lut_port.adr[i].eq(sink.hit) + # LUT port + for i, sink in enumerate(sinks): + self.comb += lut.adr[i].eq(sink.hit) - # Drive source + # drive source self.comb += [ - self.source.stb.eq(optree("&", [sink.stb for sink in self.sinks])), - self.source.hit.eq(lut_port.dat_r), + source.stb.eq(optree("&", [sink.stb for sink in sinks])), + source.hit.eq(lut.dat_r) ] + for i, sink in enumerate(sinks): + self.comb += sink.ack.eq(sink.stb & source.ack) +class LiteScopeSumCSR(Module, AutoCSR): + def __init__(self, ports): + LiteScopeSum.__init__(self, ports) + self._prog_we = CSR() + self._prog_adr = CSRStorage(ports) + self._prog_dat = CSRStorage() + ### + self.comb += [ + self.prog_we.eq(self._prog_we.re & self._prog_we.r), + self.prog_adr.eq(self._prog_adr.storage), + self.prog_dat.eq(self._prog_dat.storage) + ] class LiteScopeTrigger(Module, AutoCSR): - def __init__(self, width, ports): - self.width = width - self.ports = ports + def __init__(self, dw): + self.dw = dw + self.ports = [] + self.sink = Sink(data_layout(dw)) + self.source = Source(hit_layout()) - self.submodules.sum = LiteScopeSum(len(ports)) - for i, port in enumerate(ports): - setattr(self.submodules, "port"+str(i), port) - - self.sink = Record(dat_layout(width)) - self.source = self.sum.source + def add_port(self, port): + setattr(self.submodules, "port"+str(len(self.ports)), port) + self.ports.append(port) + def do_finalize(self): + self.submodules.sum = LiteScopeSumCSR(len(self.ports)) ### - - for i, port in enumerate(ports): + for i, port in enumerate(self.ports): + # Note: port's ack is not used and supposed to be always 1 self.comb += [ - self.sink.connect(port.sink), - port.source.connect(self.sum.sinks[i]) + port.sink.stb.eq(self.sink.stb), + port.sink.data.eq(self.sink.data), + self.sink.ack.eq(1), + Record.connect(port.source, self.sum.sinks[i]) ] + self.comb += Record.connect(self.sum.source, self.source) diff --git a/litescope/frontend/io.py b/litescope/frontend/io.py index 3fb2d3183..7233a0b39 100644 --- a/litescope/frontend/io.py +++ b/litescope/frontend/io.py @@ -1,5 +1,4 @@ -from migen.fhdl.structure import * -from migen.bank.description import * +from litescope.common import * class LiteScopeIO(Module, AutoCSR): def __init__(self, width): diff --git a/litescope/frontend/la.py b/litescope/frontend/la.py index 638fcd0fe..8bdd71999 100644 --- a/litescope/frontend/la.py +++ b/litescope/frontend/la.py @@ -1,96 +1,66 @@ -from migen.fhdl.std import * -from migen.bank.description import * -from migen.actorlib.fifo import AsyncFIFO - from litescope.common import * from litescope.core.trigger import LiteScopeTrigger from litescope.core.storage import LiteScopeRecorder, LiteScopeRunLengthEncoder from mibuild.tools import write_to_file -def _getattr_all(l, attr): - it = iter(l) - r = getattr(next(it), attr) - for e in it: - if getattr(e, attr) != r: - raise ValueError - return r - class LiteScopeLA(Module, AutoCSR): - def __init__(self, depth, dat, with_rle=False, clk_domain="sys", pipe=False): + def __init__(self, layout, depth, clk_domain="sys", input_buffer=False, with_rle=False): + self.layout = layout + self.data = Cat(*layout) + self.dw = flen(self.data) self.depth = depth - if isinstance(dat, tuple): - dat = Cat(*dat) self.with_rle = with_rle self.clk_domain = clk_domain - self.pipe = pipe - self.ports = [] - self.width = flen(dat) + self.input_buffer = input_buffer - self.stb = Signal(reset=1) - self.dat = dat - - def add_port(self, port_class): - port = port_class(self.width) - self.ports.append(port) - - def do_finalize(self): - stb = self.stb - dat = self.dat - if self.pipe: - sync = getattr(self.sync, self.clk_domain) - stb_new = Signal() - dat_new = Signal(flen(dat)) - sync += [ - stb_new.eq(stb), - dat_new.eq(dat) - ] - stb = stb_new - dat = dat_new - - if self.clk_domain is not "sys": - fifo = AsyncFIFO([("dat", self.width)], 32) - self.submodules += RenameClockDomains(fifo, {"write": self.clk_domain, "read": "sys"}) - self.comb += [ - fifo.sink.stb.eq(stb), - fifo.sink.dat.eq(dat) - ] - sink = Record(dat_layout(self.width)) - self.comb += [ - sink.stb.eq(fifo.source.stb), - sink.dat.eq(fifo.source.dat), - fifo.source.ack.eq(1) - ] - else: - sink = Record(dat_layout(self.width)) - self.comb += [ - sink.stb.eq(stb), - sink.dat.eq(dat) - ] - - self.submodules.trigger = trigger = LiteScopeTrigger(self.width, self.ports) - self.submodules.recorder = recorder = LiteScopeRecorder(self.width, self.depth) + self.sink = Sink(data_layout(self.dw)) self.comb += [ - sink.connect(trigger.sink), - trigger.source.connect(recorder.trig_sink) + self.sink.stb.eq(1), + self.sink.data.eq(self.data) ] + self.submodules.trigger = trigger = LiteScopeTrigger(self.dw) + self.submodules.recorder = recorder = LiteScopeRecorder(self.dw, self.depth) + + def do_finalize(self): + # insert Buffer on sink (optional, can be used to improve timings) + if self.input_buffer: + self.submodules.buffer = Buffer(self.sink.description) + self.comb += Record.connect(self.sink, self.buffer.sink) + self.sink = self.buffer.source + + # clock domain crossing (optional, required when capture_clk is not sys_clk) + # XXX : sys_clk must be faster than capture_clk, add Converter on data to remove this limitation + if self.clk_domain is not "sys": + self.submodules.fifo = AsyncFIFO(self.sink.description, 32) + self.submodules += RenameClockDomains(self.fifo, {"write": self.clk_domain, "read": "sys"}) + self.comb += Record.connect(self.sink, self.fifo.sink) + self.sink = self.fifo.source + + # connect everything + self.comb += [ + self.trigger.sink.stb.eq(self.sink.stb), + self.trigger.sink.data.eq(self.sink.data), + Record.connect(self.trigger.source, self.recorder.trigger_sink) + ] if self.with_rle: - self.submodules.rle = rle = LiteScopeRunLengthEncoder(self.width) + rle = LiteScopeRunLengthEncoder(self.dw) + self.submodules += rle self.comb += [ - sink.connect(rle.sink), - rle.source.connect(recorder.dat_sink) + Record.connect(self.sink, rle.sink), + Record.connect(rle.source, self.recorder.data_sink) ] else: - self.comb += sink.connect(recorder.dat_sink) + self.comb += Record.connect(self.sink, self.recorder.data_sink) - def export(self, layout, vns, filename): + def export(self, vns, filename): def format_line(*args): return ",".join(args) + "\n" r = "" - r += format_line("config", "width", str(self.width)) + r += format_line("config", "dw", str(self.dw)) r += format_line("config", "depth", str(self.depth)) r += format_line("config", "with_rle", str(int(self.with_rle))) - for e in layout: + for e in self.layout: r += format_line("layout", vns.get_name(e), str(flen(e))) write_to_file(filename, r) diff --git a/litescope/host/driver.py b/litescope/host/driver.py index f6f88bf0f..0fed42948 100644 --- a/litescope/host/driver.py +++ b/litescope/host/driver.py @@ -121,7 +121,7 @@ class LiteScopeLADriver(): self.get_config() self.get_layout() self.build() - self.dat = Dat(self.width) + self.dat = Dat(self.dw) def get_config(self): csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#') @@ -208,11 +208,9 @@ class LiteScopeLADriver(): def read(self): self.show_state("READ") - empty = self.recorder_read_empty.read() - while(not empty): - self.dat.append(self.recorder_read_dat.read()) - empty = self.recorder_read_empty.read() - self.recorder_read_en.write(1) + 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() diff --git a/targets/simple.py b/targets/simple.py index 3aaa8d994..6fa3dc998 100644 --- a/targets/simple.py +++ b/targets/simple.py @@ -11,7 +11,7 @@ from litescope.common import * from litescope.bridge.uart2wb import LiteScopeUART2WB from litescope.frontend.io import LiteScopeIO from litescope.frontend.la import LiteScopeLA -from litescope.core.trigger import LiteScopeTerm +from litescope.core.port import LiteScopeTermCSR class _CRG(Module): def __init__(self, clk_in): @@ -97,12 +97,12 @@ class LiteScopeSoC(GenSoC, AutoCSR): cnt0, cnt1 ) - self.submodules.la = LiteScopeLA(512, self.debug) - self.la.add_port(LiteScopeTerm) + self.submodules.la = LiteScopeLA(self.debug, 512) + self.la.trigger.add_port(LiteScopeTermCSR(self.la.dw)) atexit.register(self.exit, platform) def exit(self, platform): if platform.vns is not None: - self.la.export(self.debug, platform.vns, "./test/la.csv") + self.la.export(platform.vns, "./test/la.csv") default_subtarget = LiteScopeSoC