doc: common scheduling models

This commit is contained in:
Sebastien Bourdeauducq 2012-06-24 17:18:45 +02:00
parent 1972de0c59
commit 377cfd2d2e
3 changed files with 72 additions and 47 deletions

View File

@ -8,6 +8,9 @@ Actors communicate by exchanging data units called tokens. A token contains arbi
Actors Actors
****** ******
Actors and endpoints
====================
Actors in Migen are implemented in FHDL. This low-level approach maximizes the practical flexibility: for example, an actor can manipulate the bus signals to implement a DMA master in order to read data from system memory. Actors in Migen are implemented in FHDL. This low-level approach maximizes the practical flexibility: for example, an actor can manipulate the bus signals to implement a DMA master in order to read data from system memory.
Token exchange ports of actors are called endpoints. Endpoints are unidirectional and can be sources (which transmit tokens out of the actor) or sinks (which receive tokens into the actor). Token exchange ports of actors are called endpoints. Endpoints are unidirectional and can be sources (which transmit tokens out of the actor) or sinks (which receive tokens into the actor).
@ -40,14 +43,14 @@ Actors are derived from the the ``migen.flow.actor.Actor`` base class. The const
An endpoint description is a triple consisting of: An endpoint description is a triple consisting of:
* The endpoint's name. * The endpoint's name.
* A reference to the ``migen.flow.actor.Sink`` or the ``migen.flow.actor.Source`` class, defining the token direction of the endpoint. * A reference to the ``migen.flow.actor.Sink`` or the ``migen.flow.actor.Source`` class, defining the token direction of the endpoint.
* The layout of the data record that the endpoint is dealing with. * The layout of the data record that the endpoint is dealing with.
Record layouts are a list of fields. Each field is described by a pair consisting of: Record layouts are a list of fields. Each field is described by a pair consisting of:
* The field's name. * The field's name.
* Either a BV object (see :ref:`bv`) if the field is a bit vector, or another record layout if the field is a lower-level record. * Either a BV object (see :ref:`bv`) if the field is a bit vector, or another record layout if the field is a lower-level record.
For example, this code: :: For example, this code: ::
@ -57,40 +60,58 @@ For example, this code: ::
creates an actor with: creates an actor with:
* One sink named ``operands`` accepting data structured as a 16-bit field ``a`` and a 16-bit field ``b``. Note that this is functionally different from having two endpoints ``a`` and ``b``, each accepting a single 16-bit field. With a single endpoint, the data is strobed when *both* ``a`` and ``b`` are valid, and ``a`` and ``b`` are *both* acknowledged *atomically*. With two endpoints, the actor has to deal with accepting ``a`` and ``b`` independently. Plumbing actors (see :ref:`plumbing`) and abstract networks (see :ref:`actornetworks`) provide a systematic way of converting between these two behaviours, so user actors should implement the behaviour that results in the simplest or highest performance design. * One sink named ``operands`` accepting data structured as a 16-bit field ``a`` and a 16-bit field ``b``. Note that this is functionally different from having two endpoints ``a`` and ``b``, each accepting a single 16-bit field. With a single endpoint, the data is strobed when *both* ``a`` and ``b`` are valid, and ``a`` and ``b`` are *both* acknowledged *atomically*. With two endpoints, the actor has to deal with accepting ``a`` and ``b`` independently. Plumbing actors (see :ref:`plumbing`) and abstract networks (see :ref:`actornetworks`) provide a systematic way of converting between these two behaviours, so user actors should implement the behaviour that results in the simplest or highest performance design.
* One source named ``result`` transmitting a single 17-bit field named ``r``. * One source named ``result`` transmitting a single 17-bit field named ``r``.
Implementing the functionality of the actor can be done in two ways: Implementing the functionality of the actor can be done in two ways:
* Overloading the ``get_fragment`` method. * Overloading the ``get_fragment`` method.
* Overloading both the ``get_control_fragment`` and ``get_process_fragment`` methods. The ``get_control_fragment`` method should return a fragment that manipulates the control signals (strobes, acknowledgements and the actor's busy signal) while ``get_process_fragment`` should return a fragment that manipulates the token payload. Overloading ``get_control_fragment`` alone allows you to define abstract actor classes implementing a given scheduling model. Migen comes with a library of such abstract classes for the most common schedules (see :ref:`schedmod`). * Overloading both the ``get_control_fragment`` and ``get_process_fragment`` methods. The ``get_control_fragment`` method should return a fragment that manipulates the control signals (strobes, acknowledgements and the actor's busy signal) while ``get_process_fragment`` should return a fragment that manipulates the token payload. Overloading ``get_control_fragment`` alone allows you to define abstract actor classes implementing a given scheduling model. Migen comes with a library of such abstract classes for the most common schedules (see :ref:`schedmod`).
Accessing the endpoints is done via the ``endpoints`` dictionary, which is keyed by endpoint names and contains instances of the ``migen.flow.actor.Endpoint`` class. The latter holds: Accessing the endpoints is done via the ``endpoints`` dictionary, which is keyed by endpoint names and contains instances of the ``migen.flow.actor.Endpoint`` class. The latter holds:
* A signal object ``stb``. * A signal object ``stb``.
* A signal object ``ack``. * A signal object ``ack``.
* The data payload ``token``. The individual fields are the items (in the Python sense) of this object. * The data payload ``token``. The individual fields are the items (in the Python sense) of this object.
Busy signal Busy signal
=========== ===========
The basic actor class creates a ``busy`` control signal that actor implementations should drive.
This signal represents whether the actor's state holds information that will cause the completion of the transmission of output tokens. For example:
* A "buffer" actor that simply registers and forwards incoming tokens should drive 1 on ``busy`` when its register contains valid data pending acknowledgement by the receiving actor, and 0 otherwise.
* An actor sequenced by a finite state machine should drive ``busy`` to 1 whenever the state machine leaves its idle state.
* An actor made of combinatorial logic is stateless and should tie ``busy`` to 0.
.. _schedmod: .. _schedmod:
Common scheduling models Common scheduling models
======================== ========================
For the simplest and most common scheduling cases, Migen provides logic to generate the handshake signals and the busy signal. This is done through abstract actor classes that overload ``get_control_fragment`` only, and the user should overload ``get_process_fragment`` to implement the actor's payload.
These classes are usable only when the actor has exactly one sink and one source (but those endpoints can contain an arbitrary data structure), and in the cases listed below.
Combinatorial Combinatorial
------------- -------------
The actor datapath is made entirely of combinatorial logic. The handshake signals pass through. A small integer adder would use this model. The actor datapath is made entirely of combinatorial logic. The handshake signals pass through. A small integer adder would use this model.
This model is implemented by the ``migen.flow.actor.CombinatorialActor`` class. There are no parameters or additional control signals.
N-sequential N-sequential
------------ ------------
The actor consumes one token at its input, and it produces one output token after N cycles. It cannot accept new input tokens until it has produced its output. A multicycle integer divider would use this model. The actor consumes one token at its input, and it produces one output token after N cycles. It cannot accept new input tokens until it has produced its output. A multicycle integer divider would use this model.
This model is implemented by the ``migen.flow.actor.SequentialActor`` class. The constructor of this class takes as parameter the number of cycles N. The class provides an extra control signal ``trigger`` that pulses to 1 for one cycle when the actor should register the inputs and start its processing. The actor is then expected to provide an output after the N cycles and hold it constant until the next trigger pulse.
N-pipelined N-pipelined
----------- -----------
This is similar to the sequential model, but the actor can always accept new input tokens. It produces an output token N cycles of latency after accepting an input token. A pipelined multiplier would use this model. This is similar to the sequential model, but the actor can always accept new input tokens. It produces an output token N cycles of latency after accepting an input token. A pipelined multiplier would use this model.
This model is implemented by the ``migen.flow.actor.PipelinedActor`` class. The constructor takes the number of pipeline stages N. There is an extra control signal ``pipe_ce`` that should enable or disable all synchronous statements in the datapath (i.e. it is the common clock enable signal for all the registers forming the pipeline stages).
The Migen actor library The Migen actor library
*********************** ***********************

View File

@ -19,9 +19,9 @@ Bit vector (BV)
=============== ===============
The bit vector (BV) object defines if a constant or signal is signed or unsigned, and how many bits it has. This is useful e.g. to: The bit vector (BV) object defines if a constant or signal is signed or unsigned, and how many bits it has. This is useful e.g. to:
* determine when to perform sign extension (FHDL uses the same rules as Verilog). * Determine when to perform sign extension (FHDL uses the same rules as Verilog).
* determine the size of registers. * Determine the size of registers.
* determine how many bits should be used by each value in concatenations. * Determine how many bits should be used by each value in concatenations.
Constant Constant
======== ========
@ -38,10 +38,10 @@ The main point of the signal object is that it is identified by its Python ID (a
The properties of a signal object are: The properties of a signal object are:
* a bit vector description * A bit vector description
* a name, used as a hint for the V*HDL back-end name mangler. * A name, used as a hint for the V*HDL back-end name mangler.
* a boolean "variable". If true, the signal will behave like a VHDL variable, or a Verilog reg that uses blocking assignment. This parameter only has an effect when the signal's value is modified in a synchronous statement. * A boolean "variable". If true, the signal will behave like a VHDL variable, or a Verilog reg that uses blocking assignment. This parameter only has an effect when the signal's value is modified in a synchronous statement.
* the signal's reset value. It must be an integer, and defaults to 0. When the signal's value is modified with a synchronous statement, the reset value is the initialization value of the associated register. When the signal is assigned to in a conditional combinatorial statement (``If`` or ``Case``), the reset value is the value that the signal has when no condition that causes the signal to be driven is verified. This enforces the absence of latches in designs. If the signal is permanently driven using a combinatorial statement, the reset value has no effect. * The signal's reset value. It must be an integer, and defaults to 0. When the signal's value is modified with a synchronous statement, the reset value is the initialization value of the associated register. When the signal is assigned to in a conditional combinatorial statement (``If`` or ``Case``), the reset value is the value that the signal has when no condition that causes the signal to be driven is verified. This enforces the absence of latches in designs. If the signal is permanently driven using a combinatorial statement, the reset value has no effect.
The sole purpose of the name property is to make the generated V*HDL code easier to understand and debug. From a purely functional point of view, it is perfectly OK to have several signals with the same name property. The back-end will generate a unique name for each object. If no name property is specified, Migen will analyze the code that created the signal object, and try to extract the variable or member name from there. For example, the following statements will create one or several signals named "bar": :: The sole purpose of the name property is to make the generated V*HDL code easier to understand and debug. From a purely functional point of view, it is perfectly OK to have several signals with the same name property. The back-end will generate a unique name for each object. If no name property is specified, Migen will analyze the code that created the signal object, and try to extract the variable or member name from there. For example, the following statements will create one or several signals named "bar": ::
@ -125,20 +125,20 @@ Instances
========= =========
Instance objects represent the parametrized instantiation of a V*HDL module, and the connection of its ports to FHDL signals. They are useful in a number of cases: Instance objects represent the parametrized instantiation of a V*HDL module, and the connection of its ports to FHDL signals. They are useful in a number of cases:
* reusing legacy or third-party V*HDL code. * Reusing legacy or third-party V*HDL code.
* using special FPGA features (DCM, ICAP, ...). * Using special FPGA features (DCM, ICAP, ...).
* implementing logic that cannot be expressed with FHDL (asynchronous circuits, ...). * Implementing logic that cannot be expressed with FHDL (asynchronous circuits, ...).
* breaking down a Migen system into multiple sub-systems, possibly using different clock domains. * Breaking down a Migen system into multiple sub-systems, possibly using different clock domains.
The properties of the instance object are: The properties of the instance object are:
* the type of the instance (i.e. name of the instantiated module). * The type of the instance (i.e. name of the instantiated module).
* a list of output ports of the instantiated module. Each element of the list is a pair containing a string, which is the name of the module's port, and either an existing signal (on which the port will be connected to) or a BV (which will cause the creation of a new signal). * A list of output ports of the instantiated module. Each element of the list is a pair containing a string, which is the name of the module's port, and either an existing signal (on which the port will be connected to) or a BV (which will cause the creation of a new signal).
* a list of input ports (likewise). * A list of input ports (likewise).
* a list of (name, value) pairs for the parameters ("generics" in VHDL) of the module. * A list of (name, value) pairs for the parameters ("generics" in VHDL) of the module.
* the name of the clock port of the module (if any). If this is specified, the port will be connected to the system clock. * The name of the clock port of the module (if any). If this is specified, the port will be connected to the system clock.
* the name of the reset port of the module (likewise). * The name of the reset port of the module (likewise).
* the name of the instance (can be mangled like signal names). * The name of the instance (can be mangled like signal names).
Memories Memories
======== ========
@ -146,21 +146,21 @@ Memories (on-chip SRAM) are supported using a mechanism similar to instances.
A memory object has the following parameters: A memory object has the following parameters:
* the width, which is the number of bits in each word. * The width, which is the number of bits in each word.
* the depth, which represents the number of words in the memory. * The depth, which represents the number of words in the memory.
* an optional list of integers used to initialize the memory. * An optional list of integers used to initialize the memory.
* a list of port descriptions. * A list of port descriptions.
Each port description contains: Each port description contains:
* the address signal (mandatory). * The address signal (mandatory).
* the data read signal (mandatory). * The data read signal (mandatory).
* the write enable signal (optional). If the port is using masked writes, the width of the write enable signal should match the number of sub-words. * The write enable signal (optional). If the port is using masked writes, the width of the write enable signal should match the number of sub-words.
* the data write signal (iff there is a write enable signal). * The data write signal (iff there is a write enable signal).
* whether reads are synchronous (default) or asynchronous. * Whether reads are synchronous (default) or asynchronous.
* the read enable port (optional, ignored for asynchronous ports). * The read enable port (optional, ignored for asynchronous ports).
* the write granularity (default 0), which defines the number of bits in each sub-word. If it is set to 0, the port is using whole-word writes only and the width of the write enable signal must be 1. This parameter is ignored if there is no write enable signal. * The write granularity (default 0), which defines the number of bits in each sub-word. If it is set to 0, the port is using whole-word writes only and the width of the write enable signal must be 1. This parameter is ignored if there is no write enable signal.
* the mode of the port (default ``WRITE_FIRST``, ignored for asynchronous ports). It can be: * The mode of the port (default ``WRITE_FIRST``, ignored for asynchronous ports). It can be:
* ``READ_FIRST``: during a write, the previous value is read. * ``READ_FIRST``: during a write, the previous value is read.
* ``WRITE_FIRST``: the written value is returned. * ``WRITE_FIRST``: the written value is returned.
@ -172,11 +172,11 @@ Fragments
********* *********
A "fragment" is a unit of logic, which is composed of: A "fragment" is a unit of logic, which is composed of:
* a list of combinatorial statements. * A list of combinatorial statements.
* a list of synchronous statements. * A list of synchronous statements.
* a list of instances. * A list of instances.
* a list of memories. * A list of memories.
* a list of simulation functions (see :ref:`simulating`). * A list of simulation functions (see :ref:`simulating`).
Fragments can reference arbitrary signals, including signals that are referenced in other fragments. Fragments can be combined using the "+" operator, which returns a new fragment containing the concatenation of each pair of lists. Fragments can reference arbitrary signals, including signals that are referenced in other fragments. Fragments can be combined using the "+" operator, which returns a new fragment containing the concatenation of each pair of lists.

View File

@ -52,3 +52,7 @@ Installing Migen
Either run the ``setup.py`` installation script or simply set ``PYTHONPATH`` to the root of the source directory. Either run the ``setup.py`` installation script or simply set ``PYTHONPATH`` to the root of the source directory.
For simulation support, an extra step is needed. See :ref:`vpisetup`. For simulation support, an extra step is needed. See :ref:`vpisetup`.
Feedback
********
Feedback concerning Migen or this manual should be sent to the Milkymist developers' mailing list at devel@lists.milkymist.org.