fhdl.structure: document the API
authorRobert Jördens <jordens@gmail.com>
Fri, 29 Nov 2013 06:34:06 +0000 (23:34 -0700)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 29 Nov 2013 21:31:55 +0000 (22:31 +0100)
migen/fhdl/structure.py

index e02866c372ea506e72fd23533f777bf6c551a816..688f2468c758a8e6f4f4ff8cd708d2bfa5bd1844 100644 (file)
@@ -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: