From b3de114d67036fff0d9d084ee0dfd16784807d25 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 17 Jan 2019 01:36:27 +0000 Subject: [PATCH] hdl.ast: add Sample. --- nmigen/hdl/ast.py | 32 +++++++++++++++++++++++++++++++- nmigen/test/test_hdl_ast.py | 21 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index be1440b..bee4a1d 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -10,7 +10,7 @@ from ..tools import * __all__ = [ "Value", "Const", "C", "AnyConst", "AnySeq", "Operator", "Mux", "Part", "Slice", "Cat", "Repl", - "Array", "ArrayProxy", + "Array", "ArrayProxy", "Sample", "Signal", "ClockSignal", "ResetSignal", "Statement", "Assign", "Assert", "Assume", "Switch", "Delay", "Tick", "Passive", "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", @@ -831,6 +831,30 @@ class ArrayProxy(Value): return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index) +class Sample(Value): + def __init__(self, value, clocks, domain): + super().__init__(src_loc_at=1) + self.value = Value.wrap(value) + self.clocks = int(clocks) + self.domain = domain + if not isinstance(self.value, (Const, Signal)): + raise TypeError("Sampled value may only be a signal or a constant, not {!r}" + .format(self.value)) + if self.clocks < 0: + raise ValueError("Cannot sample a value {} cycles in the future" + .format(-self.clocks)) + + def shape(self): + return self.value.shape() + + def _rhs_signals(self): + return ValueSet((self,)) + + def __repr__(self): + return "(sample {!r} @ {}[{}])".format( + self.value, "" if self.domain is None else self.domain, self.clocks) + + class _StatementList(list): def __repr__(self): return "({})".format(" ".join(map(repr, self))) @@ -1088,6 +1112,8 @@ class ValueKey: elif isinstance(self.value, ArrayProxy): return hash((ValueKey(self.value.index), tuple(ValueKey(e) for e in self.value._iter_as_values()))) + elif isinstance(self.value, Sample): + return hash((ValueKey(self.value.value), self.value.clocks, self.value.domain)) else: # :nocov: raise TypeError("Object '{!r}' cannot be used as a key in value collections" .format(self.value)) @@ -1126,6 +1152,10 @@ class ValueKey: all(ValueKey(a) == ValueKey(b) for a, b in zip(self.value._iter_as_values(), other.value._iter_as_values()))) + elif isinstance(self.value, Sample): + return (ValueKey(self.value.value) == ValueKey(other.value.value) and + self.value.clocks == other.value.clocks and + self.value.domain == self.value.domain) else: # :nocov: raise TypeError("Object '{!r}' cannot be used as a key in value collections" .format(self.value)) diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 603a6cc..1aee24b 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -496,3 +496,24 @@ class ResetSignalTestCase(FHDLTestCase): def test_repr(self): s1 = ResetSignal() self.assertEqual(repr(s1), "(rst sync)") + + +class SampleTestCase(FHDLTestCase): + def test_const(self): + s = Sample(1, 1, "sync") + self.assertEqual(s.shape(), (1, False)) + + def test_signal(self): + s = Sample(Signal(2), 1, "sync") + self.assertEqual(s.shape(), (2, False)) + + def test_wrong_value_operator(self): + with self.assertRaises(TypeError, + "Sampled value may only be a signal or a constant, not " + "(+ (sig $signal) (const 1'd1))"): + Sample(Signal() + 1, 1, "sync") + + def test_wrong_clocks_neg(self): + with self.assertRaises(ValueError, + "Cannot sample a value 1 cycles in the future"): + Sample(Signal(), -1, "sync") -- 2.30.2