fhdl.structure: document the API
This commit is contained in:
parent
e469e5e539
commit
73db4944f1
|
@ -15,6 +15,16 @@ class HUID:
|
|||
return self.huid
|
||||
|
||||
class Value(HUID):
|
||||
"""Base class for operands
|
||||
|
||||
Instances of `Value` or its subclasses can be operands to
|
||||
arithmetic, comparison, bitwise, and logic operators.
|
||||
They can be assigned (:meth:`eq`) or indexed/sliced (using the usual
|
||||
Python indexing and slicing notation).
|
||||
|
||||
Values created from integers have the minimum bit width to necessary to
|
||||
represent the integer.
|
||||
"""
|
||||
def __invert__(self):
|
||||
return _Operator("~", [self])
|
||||
def __neg__(self):
|
||||
|
@ -83,6 +93,19 @@ class Value(HUID):
|
|||
raise KeyError
|
||||
|
||||
def eq(self, r):
|
||||
"""Assignment
|
||||
|
||||
Parameters
|
||||
----------
|
||||
r : Value, in
|
||||
Value to be assigned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
_Assign
|
||||
Assignment statement that can be used in combinatorial or
|
||||
synchronous context.
|
||||
"""
|
||||
return _Assign(self, r)
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -95,6 +118,22 @@ class _Operator(Value):
|
|||
self.operands = operands
|
||||
|
||||
def Mux(sel, val1, val0):
|
||||
"""Multiplex between two values
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sel : Value(1), in
|
||||
Selector.
|
||||
val1 : Value(N), in
|
||||
val0 : Value(N), in
|
||||
Input values.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Value(N), out
|
||||
Output `Value`. If `sel` is asserted, the Mux returns
|
||||
`val1`, else `val0`.
|
||||
"""
|
||||
return _Operator("m", [sel, val1, val0])
|
||||
|
||||
class _Slice(Value):
|
||||
|
@ -105,17 +144,101 @@ class _Slice(Value):
|
|||
self.stop = stop
|
||||
|
||||
class Cat(Value):
|
||||
"""Concatenate values
|
||||
|
||||
Form a compound `Value` from several smaller ones by concatenation.
|
||||
The first argument occupies the lower bits of the result.
|
||||
The return value can be used on either side of an assignment, that
|
||||
is, the concatenated value can be used as an argument on the RHS or
|
||||
as a target on the LHS. If it is used on the LHS, it must solely
|
||||
consist of `Signal` s. The bit length of the return value is the sum of
|
||||
the bit lengths of the arguments::
|
||||
|
||||
flen(Cat(*args)) == sum(flen(arg) for arg in args)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*args : Value, inout
|
||||
`Value` s to be concatenated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Cat, inout
|
||||
Resulting `Value` obtained by concatentation.
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
Value.__init__(self)
|
||||
self.l = args
|
||||
|
||||
class Replicate(Value):
|
||||
"""Replicate a value
|
||||
|
||||
An input value is replicated (repeated) several times
|
||||
to be used on the RHS of assignments::
|
||||
|
||||
flen(Replicate(s, n)) == flen(s)*n
|
||||
|
||||
Parameters
|
||||
----------
|
||||
v : Value, in
|
||||
Input value to be replicated.
|
||||
n : int
|
||||
Number of replications.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Replicate, out
|
||||
Replicated value.
|
||||
"""
|
||||
def __init__(self, v, n):
|
||||
Value.__init__(self)
|
||||
self.v = v
|
||||
self.n = n
|
||||
|
||||
class Signal(Value):
|
||||
"""A `Value` that can change
|
||||
|
||||
The `Signal` object represents a value that is expected to change
|
||||
in the circuit. It does exactly what Verilog's `wire` and
|
||||
`reg` and VHDL's `signal` do.
|
||||
|
||||
A `Signal` can be indexed to access a subset of its bits. Negative
|
||||
indices (`signal[-1]`) and the extended Python slicing notation
|
||||
(`signal[start:stop:step]`) are supported.
|
||||
The indeces 0 and -1 are the least and most significant bits
|
||||
respectively.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bits_sign : int or tuple
|
||||
Either an integer `bits` or a tuple `(bits, signed)`
|
||||
specifying the number of bits in this `Signal` and whether it is
|
||||
signed (can represent negative values). `signed` defaults to
|
||||
`False`.
|
||||
name : str or None
|
||||
Name hint for this signal. If `None` (default) the name is
|
||||
inferred from the variable name this `Signal` is assigned to.
|
||||
Name collisions are automatically resolved by prepending
|
||||
names of objects that contain this `Signal` and by
|
||||
appending integer sequences.
|
||||
variable : bool
|
||||
Deprecated.
|
||||
reset : int
|
||||
Reset (synchronous) or default (combinatorial) value.
|
||||
When this `Signal` is assigned to in synchronous context and the
|
||||
corresponding clock domain is reset, the `Signal` assumes the
|
||||
given value. When this `Signal` is unassigned in combinatorial
|
||||
context (due to conditional assignments not being taken),
|
||||
the `Signal` assumes its `reset` value. Defaults to 0.
|
||||
name_override : str or None
|
||||
Do not use the inferred name but the given one.
|
||||
min : int or None
|
||||
max : int or None
|
||||
If `bits_sign` is `None`, the signal bit width and signedness are
|
||||
determined by the integer range given by `min` (inclusive,
|
||||
defaults to 0) and `max` (exclusive, defaults to 2).
|
||||
related : Signal or None
|
||||
"""
|
||||
def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None):
|
||||
from migen.fhdl.size import bits_for
|
||||
|
||||
|
@ -149,11 +272,31 @@ class Signal(Value):
|
|||
return "<Signal " + (self.backtrace[-1][0] or "anonymous") + " at " + hex(id(self)) + ">"
|
||||
|
||||
class ClockSignal(Value):
|
||||
"""Clock signal for a given clock domain
|
||||
|
||||
`ClockSignal` s for a given clock domain can be retrieved multiple
|
||||
times. They all ultimately refer to the same signal.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cd : str
|
||||
Clock domain to obtain a clock signal for. Defaults to `"sys"`.
|
||||
"""
|
||||
def __init__(self, cd="sys"):
|
||||
Value.__init__(self)
|
||||
self.cd = cd
|
||||
|
||||
class ResetSignal(Value):
|
||||
"""Reset signal for a given clock domain
|
||||
|
||||
`ResetSignal` s for a given clock domain can be retrieved multiple
|
||||
times. They all ultimately refer to the same signal.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cd : str
|
||||
Clock domain to obtain a reset signal for. Defaults to `"sys"`.
|
||||
"""
|
||||
def __init__(self, cd="sys"):
|
||||
Value.__init__(self)
|
||||
self.cd = cd
|
||||
|
@ -166,16 +309,56 @@ class _Assign:
|
|||
self.r = r
|
||||
|
||||
class If:
|
||||
"""Conditional execution of statements
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cond : Value(1), in
|
||||
Condition
|
||||
*t : Statements
|
||||
Statements to execute if `cond` is asserted.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> a = Signal()
|
||||
>>> b = Signal()
|
||||
>>> c = Signal()
|
||||
>>> d = Signal()
|
||||
>>> If(a,
|
||||
... b.eq(1)
|
||||
... ).Elif(c,
|
||||
... b.eq(0)
|
||||
... ).Else(
|
||||
... b.eq(d)
|
||||
... )
|
||||
"""
|
||||
def __init__(self, cond, *t):
|
||||
self.cond = cond
|
||||
self.t = list(t)
|
||||
self.f = []
|
||||
|
||||
def Else(self, *f):
|
||||
"""Add an `else` conditional block
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*f : Statements
|
||||
Statements to execute if all previous conditions fail.
|
||||
"""
|
||||
_insert_else(self, list(f))
|
||||
return self
|
||||
|
||||
def Elif(self, cond, *t):
|
||||
"""Add an `else if` conditional block
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cond : Value(1), in
|
||||
Condition
|
||||
*t : Statements
|
||||
Statements to execute if previous conditions fail and `cond`
|
||||
is asserted.
|
||||
"""
|
||||
_insert_else(self, [If(cond, *t)])
|
||||
return self
|
||||
|
||||
|
@ -188,11 +371,44 @@ def _insert_else(obj, clause):
|
|||
o.f = clause
|
||||
|
||||
class Case:
|
||||
"""Case/Switch statement
|
||||
|
||||
Parameters
|
||||
----------
|
||||
test : Value, in
|
||||
Selector value used to decide which block to execute
|
||||
cases : dict
|
||||
Dictionary of cases. The keys are numeric constants to compare
|
||||
with `test`. The values are statements to be executed the
|
||||
corresponding key matches `test`. The dictionary may contain a
|
||||
string key `"default"` to mark a fall-through case that is
|
||||
executed if no other key matches.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> a = Signal()
|
||||
>>> b = Signal()
|
||||
>>> Case(a, {
|
||||
... 0: b.eq(1),
|
||||
... 1: b.eq(0),
|
||||
... "default": b.eq(0),
|
||||
... })
|
||||
"""
|
||||
def __init__(self, test, cases):
|
||||
self.test = test
|
||||
self.cases = cases
|
||||
|
||||
def makedefault(self, key=None):
|
||||
"""Mark a key as the default case
|
||||
|
||||
Deletes/Substitutes any previously existing default case.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : int or None
|
||||
Key to use as default case if no other key matches.
|
||||
By default, the largest key is the default key.
|
||||
"""
|
||||
if key is None:
|
||||
for choice in self.cases.keys():
|
||||
if key is None or choice > key:
|
||||
|
@ -217,6 +433,34 @@ class _ArrayProxy(Value):
|
|||
self.key)
|
||||
|
||||
class Array(list):
|
||||
"""Addressable multiplexer
|
||||
|
||||
An array is created from an iterable of values and indexed using the
|
||||
usual Python simple indexing notation (no negative indices or
|
||||
slices). It can be indexed by numeric constants, `Value` s, or
|
||||
`Signal` s.
|
||||
|
||||
The result of indexing the array is a proxy for the entry at the
|
||||
given index that can be used on either RHS or LHS of assignments.
|
||||
|
||||
An array can be indexed multiple times.
|
||||
|
||||
Multidimensional arrays are supported by packing inner arrays into
|
||||
outer arrays.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
values : iterable of ints, Values, Signals
|
||||
Entries of the array. Each entry can be a numeric constant, a
|
||||
`Signal` or a `Record`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> a = Array(range(10))
|
||||
>>> b = Signal(max=10)
|
||||
>>> c = Signal(max=10)
|
||||
>>> b.eq(a[9 - c])
|
||||
"""
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, Value):
|
||||
return _ArrayProxy(self, key)
|
||||
|
@ -224,6 +468,27 @@ class Array(list):
|
|||
return list.__getitem__(self, key)
|
||||
|
||||
class ClockDomain:
|
||||
"""Synchronous domain
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str or None
|
||||
Domain name. If None (the default) the name is inferred from the
|
||||
variable name this `ClockDomain` is assigned to (stripping any
|
||||
`"cd_"` prefix).
|
||||
reset_less : bool
|
||||
The domain does not use a reset signal. Registers within this
|
||||
domain are still all initialized to their reset state once, e.g.
|
||||
through Verilog `"initial"` statements.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
clk : Signal, inout
|
||||
The clock for this domain. Can be driven or used to drive other
|
||||
signals (preferably in combinatorial context).
|
||||
rst : Signal or None, inout
|
||||
Reset signal for this domain. Can be driven or used to drive.
|
||||
"""
|
||||
def __init__(self, name=None, reset_less=False):
|
||||
self.name = tracer.get_obj_var_name(name)
|
||||
if self.name is None:
|
||||
|
@ -237,6 +502,13 @@ class ClockDomain:
|
|||
self.rst = Signal(name_override=self.name + "_rst")
|
||||
|
||||
def rename(self, new_name):
|
||||
"""Rename the clock domain
|
||||
|
||||
Parameters
|
||||
----------
|
||||
new_name : str
|
||||
New name
|
||||
"""
|
||||
self.name = new_name
|
||||
self.clk.name_override = new_name + "_clk"
|
||||
if self.rst is not None:
|
||||
|
|
Loading…
Reference in New Issue