From 914d018cf8dc4d5b51d47aa699c1c0fdf731c78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Fri, 2 Jul 2021 16:09:37 +0200 Subject: [PATCH] phy/sim_utils: support low wait times (0/1) in PulseTiming --- litedram/phy/sim_utils.py | 67 ++++++++++++++++++++++++++---- test/test_sim_utils.py | 87 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 test/test_sim_utils.py diff --git a/litedram/phy/sim_utils.py b/litedram/phy/sim_utils.py index 3c52b98..1d4fc11 100644 --- a/litedram/phy/sim_utils.py +++ b/litedram/phy/sim_utils.py @@ -206,6 +206,48 @@ def log_level_getter(log_level): # Simulator ---------------------------------------------------------------------------------------- +class Timing(Module): + # slight modification of tXXDController + def __init__(self, t): + self.valid = Signal() + self.ready = Signal() + + if t is None: + t = 0 + + if isinstance(t, Signal): + count = Signal.like(t) + else: + count = Signal(max=max(t, 2)) + + ready = Signal() + ready_reg = Signal() + self.comb += [ + self.ready.eq(ready_reg | ready), + ready.eq((t == 0) & self.valid), + ] + + self.sync += \ + If(self.valid, + If(t == 0, + ready_reg.eq(1) + ).Else( + count.eq(t - 1), + If(t == 1, + ready_reg.eq(1) + ).Else( + ready_reg.eq(0) + ) + ), + ).Elif(~ready, + If(count > 1, + count.eq(count - 1), + ), + If(count == 1, + ready_reg.eq(1) + ) + ) + class PulseTiming(Module): """Timing monitor with pulse input/output @@ -214,20 +256,29 @@ class PulseTiming(Module): * countdown triggered by a low to high pulse on `trigger` * `ready` is initially low, only after a trigger it can become high * provides `ready_p` which is high only for 1 cycle when `ready` becomes high + * supports t values starting from 0, with t=0 `ready_p` will pulse in the same + cycle in which `trigger` is high """ def __init__(self, t): self.trigger = Signal() self.ready = Signal() self.ready_p = Signal() - ready_d = Signal() + trigger_d = Signal() triggered = Signal() - tctrl = tXXDController(t) - self.submodules += tctrl + timing = Timing(t) + self.submodules += timing - self.sync += If(self.trigger, triggered.eq(1)), - self.comb += [ - self.ready.eq(triggered & tctrl.ready), - self.ready_p.eq(edge(self, self.ready)), - tctrl.valid.eq(edge(self, self.trigger)), + self.sync += [ + If(self.trigger, triggered.eq(1)), + trigger_d.eq(self.trigger), + ] + self.comb += [ + self.ready.eq((triggered & timing.ready) | ((t == 0) & self.trigger)), + self.ready_p.eq(reduce(or_, [ + edge(self, self.ready), + (t == 0) & edge(self, self.trigger), + (t == 1) & edge(self, trigger_d), + ])), + timing.valid.eq(edge(self, self.trigger)), ] diff --git a/test/test_sim_utils.py b/test/test_sim_utils.py new file mode 100644 index 0000000..a524e6f --- /dev/null +++ b/test/test_sim_utils.py @@ -0,0 +1,87 @@ +# This file is part of LiteDRAM. +# +# Copyright (c) 2021 Antmicro +# SPDX-License-Identifier: BSD-2-Clause + +import unittest + +from migen import * + +from litedram.phy.sim_utils import PulseTiming + +class TestSimUtils(unittest.TestCase): + def pulse_timing_test(self, dut, *, trigger, ready, ready_p, generators=None): + generators = generators or [] + assert len(trigger) == len(ready) == len(ready_p) + + ready_hist = "" + ready_p_hist = "" + + def generator(): + nonlocal ready_hist, ready_p_hist + for i in range(len(trigger)): + yield dut.trigger.eq(int(trigger[i])) + yield + ready_hist += str((yield dut.ready)) + ready_p_hist += str((yield dut.ready_p)) + + run_simulation(dut, [generator(), *generators]) + self.assertEqual(ready_hist, ready) + self.assertEqual(ready_p_hist, ready_p) + + def test_pulse_timing_basic(self): + self.pulse_timing_test(PulseTiming(4), + trigger = "01000000", + ready = "00000111", + ready_p = "00000100", + ) + + def test_pulse_timing_1(self): + self.pulse_timing_test(PulseTiming(1), + trigger = "01000000", + ready = "00111111", + ready_p = "00100000", + ) + + def test_pulse_timing_0(self): + self.pulse_timing_test(PulseTiming(0), + trigger = "01000000", + ready = "01111111", + ready_p = "01000000", + ) + + def pulse_timing_signal_test(self, t, **kwargs): + class Dut(PulseTiming): + def __init__(self): + self.t = Signal(3) + super().__init__(self.t) + dut = Dut() + def generator(): + yield + yield dut.t.eq(t) + yield + self.pulse_timing_test(dut, generators=[generator()], **kwargs) + + def test_pulse_timing_signal(self): + self.pulse_timing_signal_test( + t = 4, + trigger = "00100000000000000", + ready = "00000011111111111", + ready_p = "00000010000000000", + ) + + def test_pulse_timing_signal_1(self): + self.pulse_timing_signal_test( + t = 1, + trigger = "00100000000000000", + ready = "00011111111111111", + ready_p = "00010000000000000", + ) + + def test_pulse_timing_signal_0(self): + self.pulse_timing_signal_test( + t = 0, + trigger = "00100000000000000", + ready = "00111111111111111", + ready_p = "00100000000000000", + )