Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 8144f96985ee
Choose a base ref
...
head repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1dc2ae57a2ac
Choose a head ref
  • 1 commit
  • 3 files changed
  • 1 contributor

Commits on Jun 16, 2020

  1. Copy the full SHA
    1dc2ae5 View commit details
Showing with 116 additions and 2 deletions.
  1. +1 −0 software/glasgow/applet/all.py
  2. +113 −0 software/glasgow/applet/interface/analyzer/__init__.py
  3. +2 −2 software/glasgow/gateware/analyzer.py
1 change: 1 addition & 0 deletions software/glasgow/applet/all.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .internal.selftest import SelfTestApplet
from .internal.benchmark import BenchmarkApplet

from .interface.analyzer import AnalyzerApplet
from .interface.uart import UARTApplet
from .interface.spi_master import SPIMasterApplet
from .interface.i2c_initiator import I2CInitiatorApplet
113 changes: 113 additions & 0 deletions software/glasgow/applet/interface/analyzer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import logging
import argparse
from vcd import VCDWriter
from nmigen import *
from nmigen.lib.cdc import FFSynchronizer

from ....gateware.pads import *
from ....gateware.analyzer import *
from ... import *


class AnalyzerSubtarget(Elaboratable):
def __init__(self, pads, in_fifo):
self.pads = pads
self.in_fifo = in_fifo

self.analyzer = EventAnalyzer(in_fifo)
self.event_source = self.analyzer.add_event_source("pin", "change", len(pads.i_t.i))

def elaborate(self, platform):
m = Module()
m.submodules += self.analyzer

pins_i = Signal.like(self.pads.i_t.i)
pins_r = Signal.like(self.pads.i_t.i)
m.submodules += FFSynchronizer(self.pads.i_t.i, pins_i)

m.d.sync += pins_r.eq(pins_i)
m.d.comb += [
self.event_source.data.eq(pins_i),
self.event_source.trigger.eq(pins_i != pins_r)
]

return m


class AnalyzerInterface:
def __init__(self, interface, event_sources):
self.lower = interface
self.decoder = TraceDecoder(event_sources)

async def read(self):
self.decoder.process(await self.lower.read())
return self.decoder.flush()


class AnalyzerApplet(GlasgowApplet, name="analyzer"):
logger = logging.getLogger(__name__)
help = "capture logic waveforms"
description = """
Capture waveforms, similar to a logic analyzer.
"""

@classmethod
def add_build_arguments(cls, parser, access):
super().add_build_arguments(parser, access)

access.add_pin_set_argument(parser, "i", width=range(1, 17), default=1)

def build(self, target, args):
self.mux_interface = iface = target.multiplexer.claim_interface(self, args)
subtarget = iface.add_subtarget(AnalyzerSubtarget(
pads=iface.get_pads(args, pin_sets=("i",)),
in_fifo=iface.get_in_fifo(),
))

self._sample_freq = target.sys_clk_freq
self._event_sources = subtarget.analyzer.event_sources

async def run(self, device, args):
iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args)
return AnalyzerInterface(iface, self._event_sources)

@classmethod
def add_interact_arguments(cls, parser):
parser.add_argument(
"file", metavar="VCD-FILE", type=argparse.FileType("w"),
help="write VCD waveforms to VCD-FILE")

async def interact(self, device, args, iface):
vcd_writer = VCDWriter(args.file, timescale="1 ns", check_values=False)
signals = []
for index in range(self._event_sources[0].width):
signals.append(vcd_writer.register_var(scope="", name="pin[{}]".format(index),
var_type="wire", size=1, init=0))

try:
overrun = False
while not overrun:
for cycle, events in await iface.read():
timestamp = cycle * 1_000_000_000 // self._sample_freq

if events == "overrun":
self.logger.error("FIFO overrun, shutting down")
for signal in signals:
vcd_writer.change(signal, timestamp, "x")
overrun = True
break

if "pin" in events: # could be also "throttle"
value = events["pin"]
for bit, signal in enumerate(signals):
vcd_writer.change(signal, timestamp, (value >> bit) & 1)

finally:
vcd_writer.close(timestamp)

# -------------------------------------------------------------------------------------------------

class AnalyzerAppletTestCase(GlasgowAppletTestCase, applet=AnalyzerApplet):
@synthesis_test
def test_build(self):
self.assertBuilds()
4 changes: 2 additions & 2 deletions software/glasgow/gateware/analyzer.py
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ class EventAnalyzer(Module):
once the event FIFO high-water mark is reached.
The event analyzer tries to make efficient use of power-of-2 wide block RAMs and be highly
tunable. To achieve this, it separates the event FIFO from the event data FIFOs, and does not
tunable. To achieve this, it separates the event FIFO from the event data FIFOs, and avoids
storing timestamps explicitly. In a system with `n` events, each of which carries `d_n` bits
of data, there would be a single event FIFO that is `n` bits wide, where a bit being set means
that event `n` occurred at a given cycle; `n` event data FIFOs that are `d_n` bits wide each,
@@ -80,7 +80,7 @@ def __init__(self, output_fifo, event_depth=None, delay_width=16):
self.event_sources = Array()
self.done = Signal()
self.throttle = Signal()
self.overrun = Signal()
self.overrun = Signal()

def add_event_source(self, name, kind, width, fields=(), depth=None):
if depth is None: