From ab96494cfc126d41ad21967448f8fcdf4e756c53 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sun, 18 Oct 2020 02:27:04 +0100 Subject: [PATCH 01/13] platform.ice40: add support for getting an async-safe ripple counter stage --- software/glasgow/platform/ice40.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/software/glasgow/platform/ice40.py b/software/glasgow/platform/ice40.py index 3ea742375..dae192bf0 100644 --- a/software/glasgow/platform/ice40.py +++ b/software/glasgow/platform/ice40.py @@ -116,3 +116,34 @@ def f_out_diff(variant): i_RESETB=~ResetSignal(pll.idomain), i_BYPASS=Const(0), ) + + def get_ripple_ff_stage(self, d_out, clk, clk_en=None, rst=None): + """ + a single stage of a ripple counter + + d_out should be used as the clock for the following stage, and as the data output + """ + if clk_en is None: + clk_en = Const(1) + if rst is None: + rst = Const(0) + + m = Module() + d_in = Signal() + + m.submodules += [ + Instance("SB_LUT4", + p_LUT_INIT=Const(0x00FF, 16), + i_I0=0, i_I1=0, i_I2=0, i_I3=d_out, + o_O=d_in + ), + Instance("SB_DFFNER", + i_D=d_in, + o_Q=d_out, + i_C=clk, + i_E=clk_en, + i_R=rst, + ), + ] + + return m From e57b398b3d836e68ef1b1a5eb4049b9889dcf4fb Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sun, 18 Oct 2020 02:27:47 +0100 Subject: [PATCH 02/13] gateware.ripple: add support for a ripple counter, with external clock --- software/glasgow/gateware/ripple.py | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 software/glasgow/gateware/ripple.py diff --git a/software/glasgow/gateware/ripple.py b/software/glasgow/gateware/ripple.py new file mode 100644 index 000000000..0a7ae36c9 --- /dev/null +++ b/software/glasgow/gateware/ripple.py @@ -0,0 +1,30 @@ +import logging +from nmigen import * + +__all__ = ["RippleCounter"] + +class RippleCounter(Elaboratable): + def __init__(self, clk, clk_en=None, rst=None, width=8, logger=None): + self.logger = logger or logging.getLogger(__name__) + self.clk = clk + self.clk_en = clk_en + self.rst = rst + self.width = width + self.count = Signal(width) + + def elaborate(self, platform): + if not hasattr(platform, "get_ripple_ff_stage"): + raise NotImplementedError("No Ripple Counter support for platform") + + m = Module() + + clk_chain = self.clk + + for i in range(self.width): + d_out = Signal() + clk_en = self.clk_en if i == 0 else None + m.submodules += platform.get_ripple_ff_stage(d_out, clk_chain, clk_en, self.rst) + m.d.comb += self.count[i].eq(d_out) + clk_chain = d_out + + return m From a098c73e466713ef219d7be07f012b9fc38f3f35 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sun, 11 Oct 2020 03:17:43 +0100 Subject: [PATCH 03/13] support.si_prefix: implement "real" to "scaled with si prefix" number conversion --- software/glasgow/support/si_prefix.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 software/glasgow/support/si_prefix.py diff --git a/software/glasgow/support/si_prefix.py b/software/glasgow/support/si_prefix.py new file mode 100644 index 000000000..cbdb1798d --- /dev/null +++ b/software/glasgow/support/si_prefix.py @@ -0,0 +1,16 @@ +def num_to_si(num, long_prefix=False): + prefixes = [ + ( 3, 'G', 'Giga' ), + ( 2, 'M', 'Mega' ), + ( 1, 'k', 'Kilo' ), + ( 0, '', '' ), + ( -1, 'm', 'mili' ), + ( -2, 'u', 'micro' ), + ( -3, 'n', 'nano' ), + ] + try: + factor, tshort, tlong = next(filter(lambda x: num > (1000 ** x[0]), prefixes)) + except StopIteration: + factor, tshort, tlong = prefixes[-1] + prefix = tlong if long_prefix else tshort + return num * (1000 ** -factor), prefix From 95f2e25d355ef20387706bf59c1cd8eb7afe4982 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sun, 18 Oct 2020 02:28:39 +0100 Subject: [PATCH 04/13] WIP: applet.interface.freq_counter: implement basic frequency counter This commit is work-in-progress: - Create `FrequencytCounterInterface` class - Add duration command line argument - longer run offers higher resolution - Clean up output --- software/glasgow/applet/all.py | 1 + .../applet/interface/freq_counter/__init__.py | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 software/glasgow/applet/interface/freq_counter/__init__.py diff --git a/software/glasgow/applet/all.py b/software/glasgow/applet/all.py index f8178f8f5..4d40bf522 100644 --- a/software/glasgow/applet/all.py +++ b/software/glasgow/applet/all.py @@ -12,6 +12,7 @@ from .interface.jtag_svf import JTAGSVFApplet from .interface.ps2_host import PS2HostApplet from .interface.sbw_probe import SpyBiWireProbeApplet +from .interface.freq_counter import FrequencyCounterApplet from .memory._24x import Memory24xApplet from .memory._25x import Memory25xApplet diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py new file mode 100644 index 000000000..bdbd3e2b1 --- /dev/null +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -0,0 +1,104 @@ +import enum +import asyncio +import logging +from nmigen import * + +from ....gateware.pads import * +from ....gateware.ripple import * +from ....support.si_prefix import num_to_si +from ... import * + + +class _Command(enum.IntEnum): + GO = 0x00 + + +class FrequencyCounterSubtarget(Elaboratable): + def __init__(self, pads, clk_count, edge_count, running, out_fifo): + self.pads = pads + self.clk_count = clk_count + self.edge_count = edge_count + self.running = running + self.out_fifo = out_fifo + + def elaborate(self, platform): + m = Module() + + trigger = Signal() + m.d.comb += [ + self.out_fifo.r_en.eq(self.out_fifo.r_rdy), + trigger.eq(self.out_fifo.r_en & (self.out_fifo.r_data == _Command.GO)), + ] + + clk_count = Signal.like(self.clk_count) + with m.If(trigger): + m.d.sync += clk_count.eq(self.clk_count) + with m.Elif(clk_count > 0): + m.d.sync += clk_count.eq(clk_count - 1) + m.d.comb += self.running.eq(1) + + m.submodules.ripple = RippleCounter( + rst=trigger, + clk=self.pads.i_t.i, + clk_en=self.running, + width=32, + ) + m.d.comb += self.edge_count.eq(m.submodules.ripple.count) + + return m + +class FrequencyCounterApplet(GlasgowApplet, name="freq-counter"): + logger = logging.getLogger(__name__) + help = "frequency counter" + description = """ + Simple frequency counter, based on a ripple counter. + """ + + @classmethod + def add_build_arguments(cls, parser, access): + super().add_build_arguments(parser, access) + + access.add_pin_argument(parser, "i", default=True) + + def build(self, target, args): + self.mux_interface = iface = target.multiplexer.claim_interface(self, args) + + reg_clk_count, self.__reg_clk_count = target.registers.add_rw(32) + reg_edge_count, self.__reg_edge_count = target.registers.add_ro(32) + reg_running, self.__reg_running = target.registers.add_ro(1) + + subtarget = iface.add_subtarget(FrequencyCounterSubtarget( + pads=iface.get_pads(args, pins=("i",)), + clk_count=reg_clk_count, + edge_count=reg_edge_count, + running=reg_running, + out_fifo=iface.get_out_fifo(), + )) + + self.sys_clk_freq = target.sys_clk_freq + + @classmethod + def add_run_arguments(cls, parser, access): + super().add_run_arguments(parser, access) + + async def measure(self, device, args, clk_count): + iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) + + await device.write_register(self.__reg_clk_count, clk_count, width=4) + + await iface.write([ _Command.GO ]) + await iface.flush() + + while await device.read_register(self.__reg_running, width=1): + await asyncio.sleep(0.1) + + edge_count = await device.read_register(self.__reg_edge_count, width=4) + + sample_duration = clk_count / self.sys_clk_freq + signal_freq = edge_count / sample_duration + + return signal_freq + + async def run(self, device, args): + signal_freq = await self.measure(device, args, int(self.sys_clk_freq * 2)) + print('signal frequency: {:>7.3f} {:1}Hz'.format( *num_to_si(signal_freq) )) From 09aec7bed0c7d5e95c123bec79aeb3354392355c Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 13:17:08 +0000 Subject: [PATCH 05/13] WIP: add --duration argument --- software/glasgow/applet/interface/freq_counter/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index bdbd3e2b1..c7bfc273e 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -60,6 +60,10 @@ def add_build_arguments(cls, parser, access): access.add_pin_argument(parser, "i", default=True) + parser.add_argument( + "--duration", metavar="DURATION", type=float, default=2.0, + help="how long to run for, longer gives higher resolution (default: %(default)s)") + def build(self, target, args): self.mux_interface = iface = target.multiplexer.claim_interface(self, args) @@ -100,5 +104,5 @@ async def measure(self, device, args, clk_count): return signal_freq async def run(self, device, args): - signal_freq = await self.measure(device, args, int(self.sys_clk_freq * 2)) + signal_freq = await self.measure(device, args, int(self.sys_clk_freq * args.duration)) print('signal frequency: {:>7.3f} {:1}Hz'.format( *num_to_si(signal_freq) )) From 8d8f40ef2f98de702c9c0ac301bffd1279825691 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 18:33:12 +0000 Subject: [PATCH 06/13] WIP: move control into FrequencyCounterInterface() --- .../applet/interface/freq_counter/__init__.py | 105 +++++++++++++++--- 1 file changed, 87 insertions(+), 18 deletions(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index c7bfc273e..7dc3a163c 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -47,6 +47,46 @@ def elaborate(self, platform): return m +class FrequencyCounterInterface: + def __init__(self, applet, device, interface): + self.applet = applet + self.device = device + self.lower = interface + + async def configure(self, duration=2.0): + ctr = int(self.applet.sys_clk_freq * duration) + + # this is broken (see comment below) + #await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) + + await self.applet.set_clk_count(ctr) + + async def start(self): + await self.lower.write([ _Command.GO ]) + await self.lower.flush() + + async def is_running(self): + return await self.applet.get_running() + + async def wait(self): + while await self.is_running(): + await asyncio.sleep(0.1) + + async def get_result(self): + clk_count = await self.applet.get_clk_count() + edge_count = await self.applet.get_edge_count() + + sample_duration = clk_count / self.applet.sys_clk_freq + signal_freq = edge_count / sample_duration + + return signal_freq + + async def measure(self, duration=2.0): + await self.configure(duration) + await self.start() + await self.wait() + return await self.get_result() + class FrequencyCounterApplet(GlasgowApplet, name="freq-counter"): logger = logging.getLogger(__name__) help = "frequency counter" @@ -85,24 +125,53 @@ def build(self, target, args): def add_run_arguments(cls, parser, access): super().add_run_arguments(parser, access) - async def measure(self, device, args, clk_count): - iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) - - await device.write_register(self.__reg_clk_count, clk_count, width=4) - - await iface.write([ _Command.GO ]) - await iface.flush() - - while await device.read_register(self.__reg_running, width=1): - await asyncio.sleep(0.1) - - edge_count = await device.read_register(self.__reg_edge_count, width=4) - - sample_duration = clk_count / self.sys_clk_freq - signal_freq = edge_count / sample_duration + async def run(self, device, args): + self.device = device - return signal_freq + iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) + freq_ctr = FrequencyCounterInterface(self, device, iface) - async def run(self, device, args): - signal_freq = await self.measure(device, args, int(self.sys_clk_freq * args.duration)) + signal_freq = await freq_ctr.measure(args.duration) print('signal frequency: {:>7.3f} {:1}Hz'.format( *num_to_si(signal_freq) )) + + # TODO: for some reason, accessing the registers from the FrequencyCounterInterface + # class will raise an odd / malformed AttributeException... as below. This exception + # isn't raised by GlasgowHardwareDevice.write_register(), but appears to occur on the + # return - wrapping below with a try / except / pass effectively resolves the issue, + # but A) that's disgusting, and B) it still breaks assignment / register_read() calls. + # + # for the moment, I've put proxy functions here, but I'd like to remove them...? + # + # $ glasgow run freq-counter -V 3.3 + # I: g.device.hardware: device already has bitstream ID 171709aadf51812cc9d1e3e54e881a43 + # I: g.cli: running handler for applet 'freq-counter' + # I: g.applet.interface.freq_counter: port(s) A, B voltage set to 3.3 V + # Traceback (most recent call last): + # File "/home/attie/proj_local/glasgow/venv/bin/glasgow", line 11, in + # load_entry_point('glasgow', 'console_scripts', 'glasgow')() + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/cli.py", line 857, in main + # exit(loop.run_until_complete(_main())) + # File "/home/attie/.bin/python3.8.2/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete + # return future.result() + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/cli.py", line 650, in _main + # task.result() + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/cli.py", line 600, in run_applet + # iface = await applet.run(device, args) + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 136, in run + # signal_freq = await freq_ctr.measure(args.duration) + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 85, in measure + # await self.configure(duration) + # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 60, in configure + # await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) + # AttributeError: 'FrequencyCounterApplet' object has no attribute '_FrequencyCounterInterface__reg_clk_count' + + async def get_clk_count(self): + return await self.device.read_register(self.__reg_clk_count, width=4) + async def set_clk_count(self, value): + await self.device.write_register(self.__reg_clk_count, value, width=4) + + async def get_edge_count(self): + return await self.device.read_register(self.__reg_edge_count, width=4) + + async def get_running(self): + return bool(await self.device.read_register(self.__reg_running, width=1)) From c9cc042f22d9709fe1ce16429238bf9ac06ee7e0 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 18:55:13 +0000 Subject: [PATCH 07/13] WIP: fixup num_to_si() ... 1 Hz in should give 1 Hz out, not 1000 mHz --- software/glasgow/support/si_prefix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/glasgow/support/si_prefix.py b/software/glasgow/support/si_prefix.py index cbdb1798d..e8b296a20 100644 --- a/software/glasgow/support/si_prefix.py +++ b/software/glasgow/support/si_prefix.py @@ -9,7 +9,7 @@ def num_to_si(num, long_prefix=False): ( -3, 'n', 'nano' ), ] try: - factor, tshort, tlong = next(filter(lambda x: num > (1000 ** x[0]), prefixes)) + factor, tshort, tlong = next(filter(lambda x: num >= (1000 ** x[0]), prefixes)) except StopIteration: factor, tshort, tlong = prefixes[-1] prefix = tlong if long_prefix else tshort From de13266bc33d182fac9999c4a42439044ec7f2a8 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 18:55:41 +0000 Subject: [PATCH 08/13] WIP: add precision to output report --- software/glasgow/applet/interface/freq_counter/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index 7dc3a163c..9df20b539 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -79,7 +79,9 @@ async def get_result(self): sample_duration = clk_count / self.applet.sys_clk_freq signal_freq = edge_count / sample_duration - return signal_freq + precision = self.applet.sys_clk_freq / clk_count + + return signal_freq, precision async def measure(self, duration=2.0): await self.configure(duration) @@ -131,8 +133,9 @@ async def run(self, device, args): iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) freq_ctr = FrequencyCounterInterface(self, device, iface) - signal_freq = await freq_ctr.measure(args.duration) + signal_freq, precision = await freq_ctr.measure(args.duration) print('signal frequency: {:>7.3f} {:1}Hz'.format( *num_to_si(signal_freq) )) + print('precision: +/- {:>7.3f} {:1}Hz'.format( *num_to_si(precision) )) # TODO: for some reason, accessing the registers from the FrequencyCounterInterface # class will raise an odd / malformed AttributeException... as below. This exception From 26e8351ea335e914632cf8705380de1b76e0c843 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 19:09:36 +0000 Subject: [PATCH 09/13] WIP: add note about slow edges --- .../applet/interface/freq_counter/__init__.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index 9df20b539..6ac420478 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -1,3 +1,9 @@ +# +# notes: +# - does not play nicely with slow edges or analog signals (e.g: sine wave) +# will produce very inaccurate and inconsistent results +# + import enum import asyncio import logging @@ -41,7 +47,7 @@ def elaborate(self, platform): rst=trigger, clk=self.pads.i_t.i, clk_en=self.running, - width=32, + width=64, ) m.d.comb += self.edge_count.eq(m.submodules.ripple.count) @@ -57,7 +63,7 @@ async def configure(self, duration=2.0): ctr = int(self.applet.sys_clk_freq * duration) # this is broken (see comment below) - #await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) + #await self.device.write_register(self.applet.__reg_clk_count, ctr, width=8) await self.applet.set_clk_count(ctr) @@ -109,8 +115,8 @@ def add_build_arguments(cls, parser, access): def build(self, target, args): self.mux_interface = iface = target.multiplexer.claim_interface(self, args) - reg_clk_count, self.__reg_clk_count = target.registers.add_rw(32) - reg_edge_count, self.__reg_edge_count = target.registers.add_ro(32) + reg_clk_count, self.__reg_clk_count = target.registers.add_rw(64) + reg_edge_count, self.__reg_edge_count = target.registers.add_ro(64) reg_running, self.__reg_running = target.registers.add_ro(1) subtarget = iface.add_subtarget(FrequencyCounterSubtarget( @@ -165,16 +171,16 @@ async def run(self, device, args): # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 85, in measure # await self.configure(duration) # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 60, in configure - # await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) + # await self.device.write_register(self.applet.__reg_clk_count, ctr, width=8) # AttributeError: 'FrequencyCounterApplet' object has no attribute '_FrequencyCounterInterface__reg_clk_count' async def get_clk_count(self): - return await self.device.read_register(self.__reg_clk_count, width=4) + return await self.device.read_register(self.__reg_clk_count, width=8) async def set_clk_count(self, value): - await self.device.write_register(self.__reg_clk_count, value, width=4) + await self.device.write_register(self.__reg_clk_count, value, width=8) async def get_edge_count(self): - return await self.device.read_register(self.__reg_edge_count, width=4) + return await self.device.read_register(self.__reg_edge_count, width=8) async def get_running(self): return bool(await self.device.read_register(self.__reg_running, width=1)) From 3f693958b2e1aeaf23a21bee7c52f02b16223a4b Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 19:09:46 +0000 Subject: [PATCH 10/13] WIP: add pull-downs --- software/glasgow/applet/interface/freq_counter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index 6ac420478..703ece2e3 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -136,7 +136,7 @@ def add_run_arguments(cls, parser, access): async def run(self, device, args): self.device = device - iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) + iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args, pull_low={args.pin_i}) freq_ctr = FrequencyCounterInterface(self, device, iface) signal_freq, precision = await freq_ctr.measure(args.duration) From 1a6bbbf38608a8a7946d4014b3cb38fb0418e37b Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 21:17:57 +0000 Subject: [PATCH 11/13] WIP: add interact() phase, so that interaction can be scripted --- software/glasgow/applet/interface/freq_counter/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index 703ece2e3..abec84069 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -139,6 +139,9 @@ async def run(self, device, args): iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args, pull_low={args.pin_i}) freq_ctr = FrequencyCounterInterface(self, device, iface) + return freq_ctr + + async def interact(self, device, args, freq_ctr): signal_freq, precision = await freq_ctr.measure(args.duration) print('signal frequency: {:>7.3f} {:1}Hz'.format( *num_to_si(signal_freq) )) print('precision: +/- {:>7.3f} {:1}Hz'.format( *num_to_si(precision) )) From 5646fc4c1812c72f69d29cd3be552ddc33a8d0ac Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 22:59:07 +0000 Subject: [PATCH 12/13] WIP: reduce to 32-bit again... 64-bit registers aren't "atomic" --- .../applet/interface/freq_counter/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index abec84069..684307498 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -47,7 +47,7 @@ def elaborate(self, platform): rst=trigger, clk=self.pads.i_t.i, clk_en=self.running, - width=64, + width=32, ) m.d.comb += self.edge_count.eq(m.submodules.ripple.count) @@ -63,7 +63,7 @@ async def configure(self, duration=2.0): ctr = int(self.applet.sys_clk_freq * duration) # this is broken (see comment below) - #await self.device.write_register(self.applet.__reg_clk_count, ctr, width=8) + #await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) await self.applet.set_clk_count(ctr) @@ -115,8 +115,8 @@ def add_build_arguments(cls, parser, access): def build(self, target, args): self.mux_interface = iface = target.multiplexer.claim_interface(self, args) - reg_clk_count, self.__reg_clk_count = target.registers.add_rw(64) - reg_edge_count, self.__reg_edge_count = target.registers.add_ro(64) + reg_clk_count, self.__reg_clk_count = target.registers.add_rw(32) + reg_edge_count, self.__reg_edge_count = target.registers.add_ro(32) reg_running, self.__reg_running = target.registers.add_ro(1) subtarget = iface.add_subtarget(FrequencyCounterSubtarget( @@ -174,16 +174,19 @@ async def interact(self, device, args, freq_ctr): # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 85, in measure # await self.configure(duration) # File "/home/attie/proj_local/glasgow/glasgow/software/glasgow/applet/interface/freq_counter/__init__.py", line 60, in configure - # await self.device.write_register(self.applet.__reg_clk_count, ctr, width=8) + # await self.device.write_register(self.applet.__reg_clk_count, ctr, width=4) # AttributeError: 'FrequencyCounterApplet' object has no attribute '_FrequencyCounterInterface__reg_clk_count' async def get_clk_count(self): - return await self.device.read_register(self.__reg_clk_count, width=8) + return await self.device.read_register(self.__reg_clk_count, width=4) async def set_clk_count(self, value): - await self.device.write_register(self.__reg_clk_count, value, width=8) + await self.device.write_register(self.__reg_clk_count, value, width=4) + + async def get_ctr(self): + return await self.device.read_register(self.__reg_ctr, width=4) async def get_edge_count(self): - return await self.device.read_register(self.__reg_edge_count, width=8) + return await self.device.read_register(self.__reg_edge_count, width=4) async def get_running(self): return bool(await self.device.read_register(self.__reg_running, width=1)) From e0155fd5c38c705e38e953c30846adbe23ba8f9d Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Wed, 30 Dec 2020 22:59:34 +0000 Subject: [PATCH 13/13] WIP: add note about runtime vs max freq --- .../glasgow/applet/interface/freq_counter/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/software/glasgow/applet/interface/freq_counter/__init__.py b/software/glasgow/applet/interface/freq_counter/__init__.py index 684307498..1c4a867c3 100644 --- a/software/glasgow/applet/interface/freq_counter/__init__.py +++ b/software/glasgow/applet/interface/freq_counter/__init__.py @@ -2,6 +2,16 @@ # notes: # - does not play nicely with slow edges or analog signals (e.g: sine wave) # will produce very inaccurate and inconsistent results +# - max runtime is ~89 sec, at which the max freq is ~48 MHz +# - given a max freq of ~100 MHz, the max sensible runtime is ~42 sec +# - given the diminishing returns in precision past a few seconds of runtime, long runtimes aren't actually that helpful +# - a 1 sec runtime will give a precision of +/- 1.000 Hz (max freq of ~4.2 GHz) +# - a 2 sec runtime will give a precision of +/- 0.500 Hz (max freq of ~2.1 GHz) +# - a 5 sec runtime will give a precision of +/- 0.200 Hz (max freq of ~858 MHz) +# - a 15 sec runtime will give a precision of +/- 0.066 Hz (max freq of ~286 MHz) +# - a 20 sec runtime will give a precision of +/- 0.050 Hz (max freq of ~214 MHz) +# - a 30 sec runtime will give a precision of +/- 0.033 Hz (max freq of ~140 MHz) +# - given the crossover, a hard limit of 20 seconds has been put on the runtime # import enum