hdl.ast: add Sample.

This commit is contained in:
whitequark 2019-01-17 01:36:27 +00:00
parent b78a2be9f6
commit b3de114d67
2 changed files with 52 additions and 1 deletions

View file

@ -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, "<default>" 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))

View file

@ -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")