from nmigen import Module
from nmigen.cli import main, verilog
-from singlepipe import PassThroughStage
-from multipipe import CombMuxOutPipe
-from multipipe import PriorityCombMuxInPipe
+from nmutil.singlepipe import PassThroughStage
+from nmutil.multipipe import CombMuxOutPipe
+from nmutil.multipipe import PriorityCombMuxInPipe
from ieee754.fpcommon.getop import FPADDBaseData
from ieee754.fpcommon.denorm import FPSCData
+++ /dev/null
-""" Pipeline and BufferedHandshake examples
-"""
-
-from nmoperator import eq
-from iocontrol import (PrevControl, NextControl)
-from singlepipe import (PrevControl, NextControl, ControlBase,
- StageCls, Stage, StageChain,
- BufferedHandshake, UnbufferedPipeline)
-
-from nmigen import Signal, Module
-from nmigen.cli import verilog, rtlil
-
-
-class ExampleAddStage(StageCls):
- """ an example of how to use the buffered pipeline, as a class instance
- """
-
- def ispec(self):
- """ returns a tuple of input signals which will be the incoming data
- """
- return (Signal(16), Signal(16))
-
- def ospec(self):
- """ returns an output signal which will happen to contain the sum
- of the two inputs
- """
- return Signal(16)
-
- def process(self, i):
- """ process the input data (sums the values in the tuple) and returns it
- """
- return i[0] + i[1]
-
-
-class ExampleBufPipeAdd(BufferedHandshake):
- """ an example of how to use the buffered pipeline, using a class instance
- """
-
- def __init__(self):
- addstage = ExampleAddStage()
- BufferedHandshake.__init__(self, addstage)
-
-
-class ExampleStage(Stage):
- """ an example of how to use the buffered pipeline, in a static class
- fashion
- """
-
- def ispec():
- return Signal(16, name="example_input_signal")
-
- def ospec():
- return Signal(16, name="example_output_signal")
-
- def process(i):
- """ process the input data and returns it (adds 1)
- """
- return i + 1
-
-
-class ExampleStageCls(StageCls):
- """ an example of how to use the buffered pipeline, in a static class
- fashion
- """
-
- def ispec(self):
- return Signal(16, name="example_input_signal")
-
- def ospec(self):
- return Signal(16, name="example_output_signal")
-
- def process(self, i):
- """ process the input data and returns it (adds 1)
- """
- return i + 1
-
-
-class ExampleBufPipe(BufferedHandshake):
- """ an example of how to use the buffered pipeline.
- """
-
- def __init__(self):
- BufferedHandshake.__init__(self, ExampleStage)
-
-
-class ExamplePipeline(UnbufferedPipeline):
- """ an example of how to use the unbuffered pipeline.
- """
-
- def __init__(self):
- UnbufferedPipeline.__init__(self, ExampleStage)
-
-
-if __name__ == '__main__':
- dut = ExampleBufPipe()
- vl = rtlil.convert(dut, ports=dut.ports())
- with open("test_bufpipe.il", "w") as f:
- f.write(vl)
-
- dut = ExamplePipeline()
- vl = rtlil.convert(dut, ports=dut.ports())
- with open("test_combpipe.il", "w") as f:
- f.write(vl)
from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase
-from singlepipe import eq
+from nmutil.singlepipe import eq
class FPADD(FPBase):
from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPState
from fpcommon.getop import FPGetOp
-from singlepipe import eq
+from nmutil.singlepipe import eq
class FPMUL(FPBase):
from operator import or_
from functools import reduce
-from singlepipe import PrevControl, NextControl
+from nmutil.singlepipe import PrevControl, NextControl
from pipeline import ObjectProxy
+++ /dev/null
-""" IO Control API
-
- Associated development bugs:
- * http://bugs.libre-riscv.org/show_bug.cgi?id=64
- * http://bugs.libre-riscv.org/show_bug.cgi?id=57
-
- Stage API:
- ---------
-
- stage requires compliance with a strict API that may be
- implemented in several means, including as a static class.
-
- Stages do not HOLD data, and they definitely do not contain
- signalling (ready/valid). They do however specify the FORMAT
- of the incoming and outgoing data, and they provide a means to
- PROCESS that data (from incoming format to outgoing format).
-
- Stage Blocks really must be combinatorial blocks. It would be ok
- to have input come in from sync'd sources (clock-driven) however by
- doing so they would no longer be deterministic, and chaining such
- blocks with such side-effects together could result in unexpected,
- unpredictable, unreproduceable behaviour.
- So generally to be avoided, then unless you know what you are doing.
-
- the methods of a stage instance must be as follows:
-
- * ispec() - Input data format specification. Takes a bit of explaining.
- The requirements are: something that eventually derives from
- nmigen Value must be returned *OR* an iterator or iterable
- or sequence (list, tuple etc.) or generator must *yield*
- thing(s) that (eventually) derive from the nmigen Value class.
-
- Complex to state, very simple in practice:
- see test_buf_pipe.py for over 25 worked examples.
-
- * ospec() - Output data format specification.
- format requirements identical to ispec.
-
- * process(m, i) - Optional function for processing ispec-formatted data.
- returns a combinatorial block of a result that
- may be assigned to the output, by way of the "nmoperator.eq"
- function. Note that what is returned here can be
- extremely flexible. Even a dictionary can be returned
- as long as it has fields that match precisely with the
- Record into which its values is intended to be assigned.
- Again: see example unit tests for details.
-
- * setup(m, i) - Optional function for setting up submodules.
- may be used for more complex stages, to link
- the input (i) to submodules. must take responsibility
- for adding those submodules to the module (m).
- the submodules must be combinatorial blocks and
- must have their inputs and output linked combinatorially.
-
- Both StageCls (for use with non-static classes) and Stage (for use
- by static classes) are abstract classes from which, for convenience
- and as a courtesy to other developers, anything conforming to the
- Stage API may *choose* to derive. See Liskov Substitution Principle:
- https://en.wikipedia.org/wiki/Liskov_substitution_principle
-
- StageChain:
- ----------
-
- A useful combinatorial wrapper around stages that chains them together
- and then presents a Stage-API-conformant interface. By presenting
- the same API as the stages it wraps, it can clearly be used recursively.
-
- ControlBase:
- -----------
-
- The base class for pipelines. Contains previous and next ready/valid/data.
- Also has an extremely useful "connect" function that can be used to
- connect a chain of pipelines and present the exact same prev/next
- ready/valid/data API.
-
- Note: pipelines basically do not become pipelines as such until
- handed to a derivative of ControlBase. ControlBase itself is *not*
- strictly considered a pipeline class. Wishbone and AXI4 (master or
- slave) could be derived from ControlBase, for example.
-"""
-
-from nmigen import Signal, Cat, Const, Module, Value, Elaboratable
-from nmigen.cli import verilog, rtlil
-from nmigen.hdl.rec import Record
-
-from collections.abc import Sequence, Iterable
-from collections import OrderedDict
-
-import nmoperator
-
-
-class Object:
- def __init__(self):
- self.fields = OrderedDict()
-
- def __setattr__(self, k, v):
- print ("kv", k, v)
- if (k.startswith('_') or k in ["fields", "name", "src_loc"] or
- k in dir(Object) or "fields" not in self.__dict__):
- return object.__setattr__(self, k, v)
- self.fields[k] = v
-
- def __getattr__(self, k):
- if k in self.__dict__:
- return object.__getattr__(self, k)
- try:
- return self.fields[k]
- except KeyError as e:
- raise AttributeError(e)
-
- def __iter__(self):
- for x in self.fields.values(): # OrderedDict so order is preserved
- if isinstance(x, Iterable):
- yield from x
- else:
- yield x
-
- def eq(self, inp):
- res = []
- for (k, o) in self.fields.items():
- i = getattr(inp, k)
- print ("eq", o, i)
- rres = o.eq(i)
- if isinstance(rres, Sequence):
- res += rres
- else:
- res.append(rres)
- print (res)
- return res
-
- def ports(self): # being called "keys" would be much better
- return list(self)
-
-
-class RecordObject(Record):
- def __init__(self, layout=None, name=None):
- Record.__init__(self, layout=layout or [], name=None)
-
- def __setattr__(self, k, v):
- #print (dir(Record))
- if (k.startswith('_') or k in ["fields", "name", "src_loc"] or
- k in dir(Record) or "fields" not in self.__dict__):
- return object.__setattr__(self, k, v)
- self.fields[k] = v
- #print ("RecordObject setattr", k, v)
- if isinstance(v, Record):
- newlayout = {k: (k, v.layout)}
- elif isinstance(v, Value):
- newlayout = {k: (k, v.shape())}
- else:
- newlayout = {k: (k, nmoperator.shape(v))}
- self.layout.fields.update(newlayout)
-
- def __iter__(self):
- for x in self.fields.values(): # remember: fields is an OrderedDict
- if isinstance(x, Iterable):
- yield from x # a bit like flatten (nmigen.tools)
- else:
- yield x
-
- def ports(self): # would be better being called "keys"
- return list(self)
-
-
-class PrevControl(Elaboratable):
- """ contains signals that come *from* the previous stage (both in and out)
- * valid_i: previous stage indicating all incoming data is valid.
- may be a multi-bit signal, where all bits are required
- to be asserted to indicate "valid".
- * ready_o: output to next stage indicating readiness to accept data
- * data_i : an input - MUST be added by the USER of this class
- """
-
- def __init__(self, i_width=1, stage_ctl=False):
- self.stage_ctl = stage_ctl
- self.valid_i = Signal(i_width, name="p_valid_i") # prev >>in self
- self._ready_o = Signal(name="p_ready_o") # prev <<out self
- self.data_i = None # XXX MUST BE ADDED BY USER
- if stage_ctl:
- self.s_ready_o = Signal(name="p_s_o_rdy") # prev <<out self
- self.trigger = Signal(reset_less=True)
-
- @property
- def ready_o(self):
- """ public-facing API: indicates (externally) that stage is ready
- """
- if self.stage_ctl:
- return self.s_ready_o # set dynamically by stage
- return self._ready_o # return this when not under dynamic control
-
- def _connect_in(self, prev, direct=False, fn=None, do_data=True):
- """ internal helper function to connect stage to an input source.
- do not use to connect stage-to-stage!
- """
- valid_i = prev.valid_i if direct else prev.valid_i_test
- res = [self.valid_i.eq(valid_i),
- prev.ready_o.eq(self.ready_o)]
- if do_data is False:
- return res
- data_i = fn(prev.data_i) if fn is not None else prev.data_i
- return res + [nmoperator.eq(self.data_i, data_i)]
-
- @property
- def valid_i_test(self):
- vlen = len(self.valid_i)
- if vlen > 1:
- # multi-bit case: valid only when valid_i is all 1s
- all1s = Const(-1, (len(self.valid_i), False))
- valid_i = (self.valid_i == all1s)
- else:
- # single-bit valid_i case
- valid_i = self.valid_i
-
- # when stage indicates not ready, incoming data
- # must "appear" to be not ready too
- if self.stage_ctl:
- valid_i = valid_i & self.s_ready_o
-
- return valid_i
-
- def elaborate(self, platform):
- m = Module()
- m.d.comb += self.trigger.eq(self.valid_i_test & self.ready_o)
- return m
-
- def eq(self, i):
- return [nmoperator.eq(self.data_i, i.data_i),
- self.ready_o.eq(i.ready_o),
- self.valid_i.eq(i.valid_i)]
-
- def __iter__(self):
- yield self.valid_i
- yield self.ready_o
- if hasattr(self.data_i, "ports"):
- yield from self.data_i.ports()
- elif isinstance(self.data_i, Sequence):
- yield from self.data_i
- else:
- yield self.data_i
-
- def ports(self):
- return list(self)
-
-
-class NextControl(Elaboratable):
- """ contains the signals that go *to* the next stage (both in and out)
- * valid_o: output indicating to next stage that data is valid
- * ready_i: input from next stage indicating that it can accept data
- * data_o : an output - MUST be added by the USER of this class
- """
- def __init__(self, stage_ctl=False):
- self.stage_ctl = stage_ctl
- self.valid_o = Signal(name="n_valid_o") # self out>> next
- self.ready_i = Signal(name="n_ready_i") # self <<in next
- self.data_o = None # XXX MUST BE ADDED BY USER
- #if self.stage_ctl:
- self.d_valid = Signal(reset=1) # INTERNAL (data valid)
- self.trigger = Signal(reset_less=True)
-
- @property
- def ready_i_test(self):
- if self.stage_ctl:
- return self.ready_i & self.d_valid
- return self.ready_i
-
- def connect_to_next(self, nxt, do_data=True):
- """ helper function to connect to the next stage data/valid/ready.
- data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
- use this when connecting stage-to-stage
- """
- res = [nxt.valid_i.eq(self.valid_o),
- self.ready_i.eq(nxt.ready_o)]
- if do_data:
- res.append(nmoperator.eq(nxt.data_i, self.data_o))
- return res
-
- def _connect_out(self, nxt, direct=False, fn=None, do_data=True):
- """ internal helper function to connect stage to an output source.
- do not use to connect stage-to-stage!
- """
- ready_i = nxt.ready_i if direct else nxt.ready_i_test
- res = [nxt.valid_o.eq(self.valid_o),
- self.ready_i.eq(ready_i)]
- if not do_data:
- return res
- data_o = fn(nxt.data_o) if fn is not None else nxt.data_o
- return res + [nmoperator.eq(data_o, self.data_o)]
-
- def elaborate(self, platform):
- m = Module()
- m.d.comb += self.trigger.eq(self.ready_i_test & self.valid_o)
- return m
-
- def __iter__(self):
- yield self.ready_i
- yield self.valid_o
- if hasattr(self.data_o, "ports"):
- yield from self.data_o.ports()
- elif isinstance(self.data_o, Sequence):
- yield from self.data_o
- else:
- yield self.data_o
-
- def ports(self):
- return list(self)
-
from nmigen.cli import main, verilog
from fpbase import FPNumIn, FPNumOut, FPOpIn, FPOpOut, Overflow, FPBase, FPState
-from singlepipe import eq
+from nmutil.singlepipe import eq
class Div:
def __init__(self, width):
+++ /dev/null
-# Copyright (c) 2014 - 2019 The Regents of the University of
-# California (Regents). All Rights Reserved. Redistribution and use in
-# source and binary forms, with or without modification, are permitted
-# provided that the following conditions are met:
-# * Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# two paragraphs of disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# two paragraphs of disclaimer in the documentation and/or other materials
-# provided with the distribution.
-# * Neither the name of the Regents nor the names of its contributors
-# may be used to endorse or promote products derived from this
-# software without specific prior written permission.
-# IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
-# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
-# ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
-# REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
-# ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
-# TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
-# MODIFICATIONS.
-
-from nmigen import Module, Signal, Memory, Mux, Elaboratable
-from nmigen.tools import bits_for
-from nmigen.cli import main
-from nmigen.lib.fifo import FIFOInterface
-
-# translated from https://github.com/freechipsproject/chisel3/blob/a4a29e29c3f1eed18f851dcf10bdc845571dfcb6/src/main/scala/chisel3/util/Decoupled.scala#L185 # noqa
-
-
-class Queue(FIFOInterface, Elaboratable):
- def __init__(self, width, depth, fwft=True, pipe=False):
- """ Queue (FIFO) with pipe mode and first-write fall-through capability
-
- * :width: width of Queue data in/out
- * :depth: queue depth. NOTE: may be set to 0 (this is ok)
- * :fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
- * :pipe : pipe mode. NOTE: this mode can cause unanticipated
- problems. when read is enabled, so is writeable.
- therefore if read is enabled, the data ABSOLUTELY MUST
- be read.
-
- fwft mode = True basically means that the data may be transferred
- combinatorially from input to output.
-
- Attributes:
- * level: available free space (number of unread entries)
-
- din = enq_data, writable = enq_ready, we = enq_valid
- dout = deq_data, re = deq_ready, readable = deq_valid
- """
- FIFOInterface.__init__(self, width, depth, fwft)
- self.pipe = pipe
- self.depth = depth
- self.level = Signal(bits_for(depth))
-
- def elaborate(self, platform):
- m = Module()
-
- # set up an SRAM. XXX bug in Memory: cannot create SRAM of depth 1
- ram = Memory(self.width, self.depth if self.depth > 1 else 2)
- m.submodules.ram_read = ram_read = ram.read_port(synchronous=False)
- m.submodules.ram_write = ram_write = ram.write_port()
-
- # convenience names
- p_ready_o = self.writable
- p_valid_i = self.we
- enq_data = self.din
-
- n_valid_o = self.readable
- n_ready_i = self.re
- deq_data = self.dout
-
- # intermediaries
- ptr_width = bits_for(self.depth - 1) if self.depth > 1 else 0
- enq_ptr = Signal(ptr_width) # cyclic pointer to "insert" point (wrport)
- deq_ptr = Signal(ptr_width) # cyclic pointer to "remove" point (rdport)
- maybe_full = Signal() # not reset_less (set by sync)
-
- # temporaries
- do_enq = Signal(reset_less=True)
- do_deq = Signal(reset_less=True)
- ptr_diff = Signal(ptr_width)
- ptr_match = Signal(reset_less=True)
- empty = Signal(reset_less=True)
- full = Signal(reset_less=True)
- enq_max = Signal(reset_less=True)
- deq_max = Signal(reset_less=True)
-
- m.d.comb += [ptr_match.eq(enq_ptr == deq_ptr), # read-ptr = write-ptr
- ptr_diff.eq(enq_ptr - deq_ptr),
- enq_max.eq(enq_ptr == self.depth - 1),
- deq_max.eq(deq_ptr == self.depth - 1),
- empty.eq(ptr_match & ~maybe_full),
- full.eq(ptr_match & maybe_full),
- do_enq.eq(p_ready_o & p_valid_i), # write conditions ok
- do_deq.eq(n_ready_i & n_valid_o), # read conditions ok
-
- # set readable and writable (NOTE: see pipe mode below)
- n_valid_o.eq(~empty), # cannot read if empty!
- p_ready_o.eq(~full), # cannot write if full!
-
- # set up memory and connect to input and output
- ram_write.addr.eq(enq_ptr),
- ram_write.data.eq(enq_data),
- ram_write.en.eq(do_enq),
- ram_read.addr.eq(deq_ptr),
- deq_data.eq(ram_read.data) # NOTE: overridden in fwft mode
- ]
-
- # under write conditions, SRAM write-pointer moves on next clock
- with m.If(do_enq):
- m.d.sync += enq_ptr.eq(Mux(enq_max, 0, enq_ptr+1))
-
- # under read conditions, SRAM read-pointer moves on next clock
- with m.If(do_deq):
- m.d.sync += deq_ptr.eq(Mux(deq_max, 0, deq_ptr+1))
-
- # if read-but-not-write or write-but-not-read, maybe_full set
- with m.If(do_enq != do_deq):
- m.d.sync += maybe_full.eq(do_enq)
-
- # first-word fall-through: same as "flow" parameter in Chisel3 Queue
- # basically instead of relying on the Memory characteristics (which
- # in FPGAs do not have write-through), then when the queue is empty
- # take the output directly from the input, i.e. *bypass* the SRAM.
- # this done combinatorially to give the exact same characteristics
- # as Memory "write-through"... without relying on a changing API
- if self.fwft:
- with m.If(p_valid_i):
- m.d.comb += n_valid_o.eq(1)
- with m.If(empty):
- m.d.comb += deq_data.eq(enq_data)
- m.d.comb += do_deq.eq(0)
- with m.If(n_ready_i):
- m.d.comb += do_enq.eq(0)
-
- # pipe mode: if next stage says it's ready (readable), we
- # *must* declare the input ready (writeable).
- if self.pipe:
- with m.If(n_ready_i):
- m.d.comb += p_ready_o.eq(1)
-
- # set the count (available free space), optimise on power-of-two
- if self.depth == 1 << ptr_width: # is depth a power of 2
- m.d.comb += self.level.eq(
- Mux(maybe_full & ptr_match, self.depth, 0) | ptr_diff)
- else:
- m.d.comb += self.level.eq(Mux(ptr_match,
- Mux(maybe_full, self.depth, 0),
- Mux(deq_ptr > enq_ptr,
- self.depth + ptr_diff,
- ptr_diff)))
-
- return m
-
-
-if __name__ == "__main__":
- reg_stage = Queue(1, 1, pipe=True)
- break_ready_chain_stage = Queue(1, 1, pipe=True, fwft=True)
- m = Module()
- ports = []
-
- def queue_ports(queue, name_prefix):
- retval = []
- for name in ["level",
- "dout",
- "readable",
- "writable"]:
- port = getattr(queue, name)
- signal = Signal(port.shape(), name=name_prefix+name)
- m.d.comb += signal.eq(port)
- retval.append(signal)
- for name in ["re",
- "din",
- "we"]:
- port = getattr(queue, name)
- signal = Signal(port.shape(), name=name_prefix+name)
- m.d.comb += port.eq(signal)
- retval.append(signal)
- return retval
-
- m.submodules.reg_stage = reg_stage
- ports += queue_ports(reg_stage, "reg_stage_")
- m.submodules.break_ready_chain_stage = break_ready_chain_stage
- ports += queue_ports(break_ready_chain_stage, "break_ready_chain_stage_")
- main(m, ports=ports)
from nmigen.compat.sim import run_simulation
from nmigen.cli import verilog, rtlil
from nmigen.compat.fhdl.bitcontainer import value_bits_sign
-from singlepipe import cat, RecordObject
+from nmutil.singlepipe import cat, RecordObject
class RecordTest:
+++ /dev/null
-""" Stage API
-
- Associated development bugs:
- * http://bugs.libre-riscv.org/show_bug.cgi?id=64
- * http://bugs.libre-riscv.org/show_bug.cgi?id=57
-
- Stage API:
- ---------
-
- stage requires compliance with a strict API that may be
- implemented in several means, including as a static class.
-
- Stages do not HOLD data, and they definitely do not contain
- signalling (ready/valid). They do however specify the FORMAT
- of the incoming and outgoing data, and they provide a means to
- PROCESS that data (from incoming format to outgoing format).
-
- Stage Blocks really should be combinatorial blocks (Moore FSMs).
- It would be ok to have input come in from sync'd sources
- (clock-driven, Mealy FSMs) however by doing so they would no longer
- be deterministic, and chaining such blocks with such side-effects
- together could result in unexpected, unpredictable, unreproduceable
- behaviour.
-
- So generally to be avoided, then unless you know what you are doing.
- https://en.wikipedia.org/wiki/Moore_machine
- https://en.wikipedia.org/wiki/Mealy_machine
-
- the methods of a stage instance must be as follows:
-
- * ispec() - Input data format specification. Takes a bit of explaining.
- The requirements are: something that eventually derives from
- nmigen Value must be returned *OR* an iterator or iterable
- or sequence (list, tuple etc.) or generator must *yield*
- thing(s) that (eventually) derive from the nmigen Value class.
-
- Complex to state, very simple in practice:
- see test_buf_pipe.py for over 25 worked examples.
-
- * ospec() - Output data format specification.
- format requirements identical to ispec.
-
- * process(m, i) - Optional function for processing ispec-formatted data.
- returns a combinatorial block of a result that
- may be assigned to the output, by way of the "nmoperator.eq"
- function. Note that what is returned here can be
- extremely flexible. Even a dictionary can be returned
- as long as it has fields that match precisely with the
- Record into which its values is intended to be assigned.
- Again: see example unit tests for details.
-
- * setup(m, i) - Optional function for setting up submodules.
- may be used for more complex stages, to link
- the input (i) to submodules. must take responsibility
- for adding those submodules to the module (m).
- the submodules must be combinatorial blocks and
- must have their inputs and output linked combinatorially.
-
- Both StageCls (for use with non-static classes) and Stage (for use
- by static classes) are abstract classes from which, for convenience
- and as a courtesy to other developers, anything conforming to the
- Stage API may *choose* to derive. See Liskov Substitution Principle:
- https://en.wikipedia.org/wiki/Liskov_substitution_principle
-
- StageChain:
- ----------
-
- A useful combinatorial wrapper around stages that chains them together
- and then presents a Stage-API-conformant interface. By presenting
- the same API as the stages it wraps, it can clearly be used recursively.
-
- StageHelper:
- ----------
-
- A convenience wrapper around a Stage-API-compliant "thing" which
- complies with the Stage API and provides mandatory versions of
- all the optional bits.
-"""
-
-from abc import ABCMeta, abstractmethod
-import inspect
-
-import nmoperator
-
-
-def _spec(fn, name=None):
- """ useful function that determines if "fn" has an argument "name".
- if so, fn(name) is called otherwise fn() is called.
-
- means that ispec and ospec can be declared with *or without*
- a name argument. normally it would be necessary to have
- "ispec(name=None)" to achieve the same effect.
- """
- if name is None:
- return fn()
- varnames = dict(inspect.getmembers(fn.__code__))['co_varnames']
- if 'name' in varnames:
- return fn(name=name)
- return fn()
-
-
-class StageCls(metaclass=ABCMeta):
- """ Class-based "Stage" API. requires instantiation (after derivation)
-
- see "Stage API" above.. Note: python does *not* require derivation
- from this class. All that is required is that the pipelines *have*
- the functions listed in this class. Derivation from this class
- is therefore merely a "courtesy" to maintainers.
- """
- @abstractmethod
- def ispec(self): pass # REQUIRED
- @abstractmethod
- def ospec(self): pass # REQUIRED
- #@abstractmethod
- #def setup(self, m, i): pass # OPTIONAL
- #@abstractmethod
- #def process(self, i): pass # OPTIONAL
-
-
-class Stage(metaclass=ABCMeta):
- """ Static "Stage" API. does not require instantiation (after derivation)
-
- see "Stage API" above. Note: python does *not* require derivation
- from this class. All that is required is that the pipelines *have*
- the functions listed in this class. Derivation from this class
- is therefore merely a "courtesy" to maintainers.
- """
- @staticmethod
- @abstractmethod
- def ispec(): pass
-
- @staticmethod
- @abstractmethod
- def ospec(): pass
-
- #@staticmethod
- #@abstractmethod
- #def setup(m, i): pass
-
- #@staticmethod
- #@abstractmethod
- #def process(i): pass
-
-
-class StageHelper(Stage):
- """ a convenience wrapper around something that is Stage-API-compliant.
- (that "something" may be a static class, for example).
-
- StageHelper happens to also be compliant with the Stage API,
- it differs from the stage that it wraps in that all the "optional"
- functions are provided (hence the designation "convenience wrapper")
- """
- def __init__(self, stage):
- self.stage = stage
- self._ispecfn = None
- self._ospecfn = None
- if stage is not None:
- self.set_specs(self, self)
-
- def ospec(self, name):
- assert self._ospecfn is not None
- return _spec(self._ospecfn, name)
-
- def ispec(self, name):
- assert self._ispecfn is not None
- return _spec(self._ispecfn, name)
-
- def set_specs(self, p, n):
- """ sets up the ispecfn and ospecfn for getting input and output data
- """
- if hasattr(p, "stage"):
- p = p.stage
- if hasattr(n, "stage"):
- n = n.stage
- self._ispecfn = p.ispec
- self._ospecfn = n.ospec
-
- def new_specs(self, name):
- """ allocates new ispec and ospec pair
- """
- return (_spec(self.ispec, "%s_i" % name),
- _spec(self.ospec, "%s_o" % name))
-
- def process(self, i):
- if self.stage and hasattr(self.stage, "process"):
- return self.stage.process(i)
- return i
-
- def setup(self, m, i):
- if self.stage is not None and hasattr(self.stage, "setup"):
- self.stage.setup(m, i)
-
- def _postprocess(self, i): # XXX DISABLED
- return i # RETURNS INPUT
- if hasattr(self.stage, "postprocess"):
- return self.stage.postprocess(i)
- return i
-
-
-class StageChain(StageHelper):
- """ pass in a list of stages, and they will automatically be
- chained together via their input and output specs into a
- combinatorial chain, to create one giant combinatorial block.
-
- the end result basically conforms to the exact same Stage API.
-
- * input to this class will be the input of the first stage
- * output of first stage goes into input of second
- * output of second goes into input into third
- * ... (etc. etc.)
- * the output of this class will be the output of the last stage
-
- NOTE: whilst this is very similar to ControlBase.connect(), it is
- *really* important to appreciate that StageChain is pure
- combinatorial and bypasses (does not involve, at all, ready/valid
- signalling of any kind).
-
- ControlBase.connect on the other hand respects, connects, and uses
- ready/valid signalling.
-
- Arguments:
-
- * :chain: a chain of combinatorial blocks conforming to the Stage API
- NOTE: StageChain.ispec and ospect have to have something
- to return (beginning and end specs of the chain),
- therefore the chain argument must be non-zero length
-
- * :specallocate: if set, new input and output data will be allocated
- and connected (eq'd) to each chained Stage.
- in some cases if this is not done, the nmigen warning
- "driving from two sources, module is being flattened"
- will be issued.
-
- NOTE: do NOT use StageChain with combinatorial blocks that have
- side-effects (state-based / clock-based input) or conditional
- (inter-chain) dependencies, unless you really know what you are doing.
- """
- def __init__(self, chain, specallocate=False):
- assert len(chain) > 0, "stage chain must be non-zero length"
- self.chain = chain
- StageHelper.__init__(self, None)
- self.setup = self._sa_setup if specallocate else self._na_setup
- self.set_specs(self.chain[0], self.chain[-1])
-
- def _sa_setup(self, m, i):
- for (idx, c) in enumerate(self.chain):
- if hasattr(c, "setup"):
- c.setup(m, i) # stage may have some module stuff
- ofn = self.chain[idx].ospec # last assignment survives
- o = _spec(ofn, 'chainin%d' % idx)
- m.d.comb += nmoperator.eq(o, c.process(i)) # process input into "o"
- if idx == len(self.chain)-1:
- break
- ifn = self.chain[idx+1].ispec # new input on next loop
- i = _spec(ifn, 'chainin%d' % (idx+1))
- m.d.comb += nmoperator.eq(i, o) # assign to next input
- self.o = o
- return self.o # last loop is the output
-
- def _na_setup(self, m, i):
- for (idx, c) in enumerate(self.chain):
- if hasattr(c, "setup"):
- c.setup(m, i) # stage may have some module stuff
- i = o = c.process(i) # store input into "o"
- self.o = o
- return self.o # last loop is the output
-
- def process(self, i):
- return self.o # conform to Stage API: return last-loop output
-
-
+++ /dev/null
-""" Unit tests for Buffered and Unbuffered pipelines
-
- contains useful worked examples of how to use the Pipeline API,
- including:
-
- * Combinatorial Stage "Chaining"
- * class-based data stages
- * nmigen module-based data stages
- * special nmigen module-based data stage, where the stage *is* the module
- * Record-based data stages
- * static-class data stages
- * multi-stage pipelines (and how to connect them)
- * how to *use* the pipelines (see Test5) - how to get data in and out
-
-"""
-
-from nmigen import Module, Signal, Mux, Const, Elaboratable
-from nmigen.hdl.rec import Record
-from nmigen.compat.sim import run_simulation
-from nmigen.cli import verilog, rtlil
-
-from example_buf_pipe import ExampleBufPipe, ExampleBufPipeAdd
-from example_buf_pipe import ExamplePipeline, UnbufferedPipeline
-from example_buf_pipe import ExampleStageCls
-from example_buf_pipe import PrevControl, NextControl, BufferedHandshake
-from example_buf_pipe import StageChain, ControlBase, StageCls
-from singlepipe import UnbufferedPipeline2
-from singlepipe import SimpleHandshake
-from singlepipe import PassThroughHandshake
-from singlepipe import PassThroughStage
-from singlepipe import FIFOControl
-from singlepipe import RecordObject
-
-from random import randint, seed
-
-#seed(4)
-
-
-def check_o_n_valid(dut, val):
- o_n_valid = yield dut.n.valid_o
- assert o_n_valid == val
-
-def check_o_n_valid2(dut, val):
- o_n_valid = yield dut.n.valid_o
- assert o_n_valid == val
-
-
-def tbench(dut):
- #yield dut.i_p_rst.eq(1)
- yield dut.n.ready_i.eq(0)
- #yield dut.p.ready_o.eq(0)
- yield
- yield
- #yield dut.i_p_rst.eq(0)
- yield dut.n.ready_i.eq(1)
- yield dut.p.data_i.eq(5)
- yield dut.p.valid_i.eq(1)
- yield
-
- yield dut.p.data_i.eq(7)
- yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed
- yield
- yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt
-
- yield dut.p.data_i.eq(2)
- yield
- yield dut.n.ready_i.eq(0) # begin going into "stall" (next stage says ready)
- yield dut.p.data_i.eq(9)
- yield
- yield dut.p.valid_i.eq(0)
- yield dut.p.data_i.eq(12)
- yield
- yield dut.p.data_i.eq(32)
- yield dut.n.ready_i.eq(1)
- yield
- yield from check_o_n_valid(dut, 1) # buffer still needs to output
- yield
- yield from check_o_n_valid(dut, 1) # buffer still needs to output
- yield
- yield from check_o_n_valid(dut, 0) # buffer outputted, *now* we're done.
- yield
-
-
-def tbench2(dut):
- #yield dut.p.i_rst.eq(1)
- yield dut.n.ready_i.eq(0)
- #yield dut.p.ready_o.eq(0)
- yield
- yield
- #yield dut.p.i_rst.eq(0)
- yield dut.n.ready_i.eq(1)
- yield dut.p.data_i.eq(5)
- yield dut.p.valid_i.eq(1)
- yield
-
- yield dut.p.data_i.eq(7)
- yield from check_o_n_valid2(dut, 0) # effects of i_p_valid delayed 2 clocks
- yield
- yield from check_o_n_valid2(dut, 0) # effects of i_p_valid delayed 2 clocks
-
- yield dut.p.data_i.eq(2)
- yield
- yield from check_o_n_valid2(dut, 1) # ok *now* i_p_valid effect is felt
- yield dut.n.ready_i.eq(0) # begin going into "stall" (next stage says ready)
- yield dut.p.data_i.eq(9)
- yield
- yield dut.p.valid_i.eq(0)
- yield dut.p.data_i.eq(12)
- yield
- yield dut.p.data_i.eq(32)
- yield dut.n.ready_i.eq(1)
- yield
- yield from check_o_n_valid2(dut, 1) # buffer still needs to output
- yield
- yield from check_o_n_valid2(dut, 1) # buffer still needs to output
- yield
- yield from check_o_n_valid2(dut, 1) # buffer still needs to output
- yield
- yield from check_o_n_valid2(dut, 0) # buffer outputted, *now* we're done.
- yield
- yield
- yield
-
-
-class Test3:
- def __init__(self, dut, resultfn):
- self.dut = dut
- self.resultfn = resultfn
- self.data = []
- for i in range(num_tests):
- #data.append(randint(0, 1<<16-1))
- self.data.append(i+1)
- self.i = 0
- self.o = 0
-
- def send(self):
- while self.o != len(self.data):
- send_range = randint(0, 3)
- for j in range(randint(1,10)):
- if send_range == 0:
- send = True
- else:
- send = randint(0, send_range) != 0
- o_p_ready = yield self.dut.p.ready_o
- if not o_p_ready:
- yield
- continue
- if send and self.i != len(self.data):
- yield self.dut.p.valid_i.eq(1)
- yield self.dut.p.data_i.eq(self.data[self.i])
- self.i += 1
- else:
- yield self.dut.p.valid_i.eq(0)
- yield
-
- def rcv(self):
- while self.o != len(self.data):
- stall_range = randint(0, 3)
- for j in range(randint(1,10)):
- stall = randint(0, stall_range) != 0
- yield self.dut.n.ready_i.eq(stall)
- yield
- o_n_valid = yield self.dut.n.valid_o
- i_n_ready = yield self.dut.n.ready_i_test
- if not o_n_valid or not i_n_ready:
- continue
- data_o = yield self.dut.n.data_o
- self.resultfn(data_o, self.data[self.o], self.i, self.o)
- self.o += 1
- if self.o == len(self.data):
- break
-
-def resultfn_3(data_o, expected, i, o):
- assert data_o == expected + 1, \
- "%d-%d data %x not match %x\n" \
- % (i, o, data_o, expected)
-
-def data_placeholder():
- data = []
- for i in range(num_tests):
- d = PlaceHolder()
- d.src1 = randint(0, 1<<16-1)
- d.src2 = randint(0, 1<<16-1)
- data.append(d)
- return data
-
-def data_dict():
- data = []
- for i in range(num_tests):
- data.append({'src1': randint(0, 1<<16-1),
- 'src2': randint(0, 1<<16-1)})
- return data
-
-
-class Test5:
- def __init__(self, dut, resultfn, data=None, stage_ctl=False):
- self.dut = dut
- self.resultfn = resultfn
- self.stage_ctl = stage_ctl
- if data:
- self.data = data
- else:
- self.data = []
- for i in range(num_tests):
- self.data.append((randint(0, 1<<16-1), randint(0, 1<<16-1)))
- self.i = 0
- self.o = 0
-
- def send(self):
- while self.o != len(self.data):
- send_range = randint(0, 3)
- for j in range(randint(1,10)):
- if send_range == 0:
- send = True
- else:
- send = randint(0, send_range) != 0
- #send = True
- o_p_ready = yield self.dut.p.ready_o
- if not o_p_ready:
- yield
- continue
- if send and self.i != len(self.data):
- yield self.dut.p.valid_i.eq(1)
- for v in self.dut.set_input(self.data[self.i]):
- yield v
- self.i += 1
- else:
- yield self.dut.p.valid_i.eq(0)
- yield
-
- def rcv(self):
- while self.o != len(self.data):
- stall_range = randint(0, 3)
- for j in range(randint(1,10)):
- ready = randint(0, stall_range) != 0
- #ready = True
- yield self.dut.n.ready_i.eq(ready)
- yield
- o_n_valid = yield self.dut.n.valid_o
- i_n_ready = yield self.dut.n.ready_i_test
- if not o_n_valid or not i_n_ready:
- continue
- if isinstance(self.dut.n.data_o, Record):
- data_o = {}
- dod = self.dut.n.data_o
- for k, v in dod.fields.items():
- data_o[k] = yield v
- else:
- data_o = yield self.dut.n.data_o
- self.resultfn(data_o, self.data[self.o], self.i, self.o)
- self.o += 1
- if self.o == len(self.data):
- break
-
-def resultfn_5(data_o, expected, i, o):
- res = expected[0] + expected[1]
- assert data_o == res, \
- "%d-%d data %x not match %s\n" \
- % (i, o, data_o, repr(expected))
-
-def tbench4(dut):
- data = []
- for i in range(num_tests):
- #data.append(randint(0, 1<<16-1))
- data.append(i+1)
- i = 0
- o = 0
- while True:
- stall = randint(0, 3) != 0
- send = randint(0, 5) != 0
- yield dut.n.ready_i.eq(stall)
- o_p_ready = yield dut.p.ready_o
- if o_p_ready:
- if send and i != len(data):
- yield dut.p.valid_i.eq(1)
- yield dut.p.data_i.eq(data[i])
- i += 1
- else:
- yield dut.p.valid_i.eq(0)
- yield
- o_n_valid = yield dut.n.valid_o
- i_n_ready = yield dut.n.ready_i_test
- if o_n_valid and i_n_ready:
- data_o = yield dut.n.data_o
- assert data_o == data[o] + 2, "%d-%d data %x not match %x\n" \
- % (i, o, data_o, data[o])
- o += 1
- if o == len(data):
- break
-
-######################################################################
-# Test 2 and 4
-######################################################################
-
-class ExampleBufPipe2(ControlBase):
- """ Example of how to do chained pipeline stages.
- """
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = ExampleBufPipe()
- pipe2 = ExampleBufPipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Test 9
-######################################################################
-
-class ExampleBufPipeChain2(BufferedHandshake):
- """ connects two stages together as a *single* combinatorial stage.
- """
- def __init__(self):
- stage1 = ExampleStageCls()
- stage2 = ExampleStageCls()
- combined = StageChain([stage1, stage2])
- BufferedHandshake.__init__(self, combined)
-
-
-def data_chain2():
- data = []
- for i in range(num_tests):
- data.append(randint(0, 1<<16-2))
- return data
-
-
-def resultfn_9(data_o, expected, i, o):
- res = expected + 2
- assert data_o == res, \
- "%d-%d received data %x not match expected %x\n" \
- % (i, o, data_o, res)
-
-
-######################################################################
-# Test 6 and 10
-######################################################################
-
-class SetLessThan(Elaboratable):
- def __init__(self, width, signed):
- self.m = Module()
- self.src1 = Signal((width, signed), name="src1")
- self.src2 = Signal((width, signed), name="src2")
- self.output = Signal(width, name="out")
-
- def elaborate(self, platform):
- self.m.d.comb += self.output.eq(Mux(self.src1 < self.src2, 1, 0))
- return self.m
-
-
-class LTStage(StageCls):
- """ module-based stage example
- """
- def __init__(self):
- self.slt = SetLessThan(16, True)
-
- def ispec(self, name):
- return (Signal(16, name="%s_sig1" % name),
- Signal(16, name="%s_sig2" % name))
-
- def ospec(self, name):
- return Signal(16, "%s_out" % name)
-
- def setup(self, m, i):
- self.o = Signal(16)
- m.submodules.slt = self.slt
- m.d.comb += self.slt.src1.eq(i[0])
- m.d.comb += self.slt.src2.eq(i[1])
- m.d.comb += self.o.eq(self.slt.output)
-
- def process(self, i):
- return self.o
-
-
-class LTStageDerived(SetLessThan, StageCls):
- """ special version of a nmigen module where the module is also a stage
-
- shows that you don't actually need to combinatorially connect
- to the outputs, or add the module as a submodule: just return
- the module output parameter(s) from the Stage.process() function
- """
-
- def __init__(self):
- SetLessThan.__init__(self, 16, True)
-
- def ispec(self):
- return (Signal(16), Signal(16))
-
- def ospec(self):
- return Signal(16)
-
- def setup(self, m, i):
- m.submodules.slt = self
- m.d.comb += self.src1.eq(i[0])
- m.d.comb += self.src2.eq(i[1])
-
- def process(self, i):
- return self.output
-
-
-class ExampleLTPipeline(UnbufferedPipeline):
- """ an example of how to use the unbuffered pipeline.
- """
-
- def __init__(self):
- stage = LTStage()
- UnbufferedPipeline.__init__(self, stage)
-
-
-class ExampleLTBufferedPipeDerived(BufferedHandshake):
- """ an example of how to use the buffered pipeline.
- """
-
- def __init__(self):
- stage = LTStageDerived()
- BufferedHandshake.__init__(self, stage)
-
-
-def resultfn_6(data_o, expected, i, o):
- res = 1 if expected[0] < expected[1] else 0
- assert data_o == res, \
- "%d-%d data %x not match %s\n" \
- % (i, o, data_o, repr(expected))
-
-
-######################################################################
-# Test 7
-######################################################################
-
-class ExampleAddRecordStage(StageCls):
- """ example use of a Record
- """
-
- record_spec = [('src1', 16), ('src2', 16)]
- def ispec(self):
- """ returns a Record using the specification
- """
- return Record(self.record_spec)
-
- def ospec(self):
- return Record(self.record_spec)
-
- def process(self, i):
- """ process the input data, returning a dictionary with key names
- that exactly match the Record's attributes.
- """
- return {'src1': i.src1 + 1,
- 'src2': i.src2 + 1}
-
-######################################################################
-# Test 11
-######################################################################
-
-class ExampleAddRecordPlaceHolderStage(StageCls):
- """ example use of a Record, with a placeholder as the processing result
- """
-
- record_spec = [('src1', 16), ('src2', 16)]
- def ispec(self):
- """ returns a Record using the specification
- """
- return Record(self.record_spec)
-
- def ospec(self):
- return Record(self.record_spec)
-
- def process(self, i):
- """ process the input data, returning a PlaceHolder class instance
- with attributes that exactly match those of the Record.
- """
- o = PlaceHolder()
- o.src1 = i.src1 + 1
- o.src2 = i.src2 + 1
- return o
-
-
-# a dummy class that may have stuff assigned to instances once created
-class PlaceHolder: pass
-
-
-class ExampleAddRecordPipe(UnbufferedPipeline):
- """ an example of how to use the combinatorial pipeline.
- """
-
- def __init__(self):
- stage = ExampleAddRecordStage()
- UnbufferedPipeline.__init__(self, stage)
-
-
-def resultfn_7(data_o, expected, i, o):
- res = (expected['src1'] + 1, expected['src2'] + 1)
- assert data_o['src1'] == res[0] and data_o['src2'] == res[1], \
- "%d-%d data %s not match %s\n" \
- % (i, o, repr(data_o), repr(expected))
-
-
-class ExampleAddRecordPlaceHolderPipe(UnbufferedPipeline):
- """ an example of how to use the combinatorial pipeline.
- """
-
- def __init__(self):
- stage = ExampleAddRecordPlaceHolderStage()
- UnbufferedPipeline.__init__(self, stage)
-
-
-def resultfn_test11(data_o, expected, i, o):
- res1 = expected.src1 + 1
- res2 = expected.src2 + 1
- assert data_o['src1'] == res1 and data_o['src2'] == res2, \
- "%d-%d data %s not match %s\n" \
- % (i, o, repr(data_o), repr(expected))
-
-
-######################################################################
-# Test 8
-######################################################################
-
-
-class Example2OpClass:
- """ an example of a class used to store 2 operands.
- requires an eq function, to conform with the pipeline stage API
- """
-
- def __init__(self):
- self.op1 = Signal(16)
- self.op2 = Signal(16)
-
- def eq(self, i):
- return [self.op1.eq(i.op1), self.op2.eq(i.op2)]
-
-
-class ExampleAddClassStage(StageCls):
- """ an example of how to use the buffered pipeline, as a class instance
- """
-
- def ispec(self):
- """ returns an instance of an Example2OpClass.
- """
- return Example2OpClass()
-
- def ospec(self):
- """ returns an output signal which will happen to contain the sum
- of the two inputs
- """
- return Signal(16, name="add2_out")
-
- def process(self, i):
- """ process the input data (sums the values in the tuple) and returns it
- """
- return i.op1 + i.op2
-
-
-class ExampleBufPipeAddClass(BufferedHandshake):
- """ an example of how to use the buffered pipeline, using a class instance
- """
-
- def __init__(self):
- addstage = ExampleAddClassStage()
- BufferedHandshake.__init__(self, addstage)
-
-
-class TestInputAdd:
- """ the eq function, called by set_input, needs an incoming object
- that conforms to the Example2OpClass.eq function requirements
- easiest way to do that is to create a class that has the exact
- same member layout (self.op1, self.op2) as Example2OpClass
- """
- def __init__(self, op1, op2):
- self.op1 = op1
- self.op2 = op2
-
-
-def resultfn_8(data_o, expected, i, o):
- res = expected.op1 + expected.op2 # these are a TestInputAdd instance
- assert data_o == res, \
- "%d-%d data %s res %x not match %s\n" \
- % (i, o, repr(data_o), res, repr(expected))
-
-def data_2op():
- data = []
- for i in range(num_tests):
- data.append(TestInputAdd(randint(0, 1<<16-1), randint(0, 1<<16-1)))
- return data
-
-
-######################################################################
-# Test 12
-######################################################################
-
-class ExampleStageDelayCls(StageCls, Elaboratable):
- """ an example of how to use the buffered pipeline, in a static class
- fashion
- """
-
- def __init__(self, valid_trigger=2):
- self.count = Signal(2)
- self.valid_trigger = valid_trigger
-
- def ispec(self):
- return Signal(16, name="example_input_signal")
-
- def ospec(self):
- return Signal(16, name="example_output_signal")
-
- @property
- def d_ready(self):
- """ data is ready to be accepted when this is true
- """
- return (self.count == 1)# | (self.count == 3)
- return Const(1)
-
- def d_valid(self, ready_i):
- """ data is valid at output when this is true
- """
- return self.count == self.valid_trigger
- return Const(1)
-
- def process(self, i):
- """ process the input data and returns it (adds 1)
- """
- return i + 1
-
- def elaborate(self, platform):
- m = Module()
- m.d.sync += self.count.eq(self.count + 1)
- return m
-
-
-class ExampleBufDelayedPipe(BufferedHandshake):
-
- def __init__(self):
- stage = ExampleStageDelayCls(valid_trigger=2)
- BufferedHandshake.__init__(self, stage, stage_ctl=True)
-
- def elaborate(self, platform):
- m = BufferedHandshake.elaborate(self, platform)
- m.submodules.stage = self.stage
- return m
-
-
-def data_chain1():
- data = []
- for i in range(num_tests):
- data.append(1<<((i*3)%15))
- #data.append(randint(0, 1<<16-2))
- #print (hex(data[-1]))
- return data
-
-
-def resultfn_12(data_o, expected, i, o):
- res = expected + 1
- assert data_o == res, \
- "%d-%d data %x not match %x\n" \
- % (i, o, data_o, res)
-
-
-######################################################################
-# Test 13
-######################################################################
-
-class ExampleUnBufDelayedPipe(BufferedHandshake):
-
- def __init__(self):
- stage = ExampleStageDelayCls(valid_trigger=3)
- BufferedHandshake.__init__(self, stage, stage_ctl=True)
-
- def elaborate(self, platform):
- m = BufferedHandshake.elaborate(self, platform)
- m.submodules.stage = self.stage
- return m
-
-######################################################################
-# Test 15
-######################################################################
-
-class ExampleBufModeAdd1Pipe(SimpleHandshake):
-
- def __init__(self):
- stage = ExampleStageCls()
- SimpleHandshake.__init__(self, stage)
-
-
-######################################################################
-# Test 16
-######################################################################
-
-class ExampleBufModeUnBufPipe(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = ExampleBufModeAdd1Pipe()
- pipe2 = ExampleBufAdd1Pipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-######################################################################
-# Test 17
-######################################################################
-
-class ExampleUnBufAdd1Pipe2(UnbufferedPipeline2):
-
- def __init__(self):
- stage = ExampleStageCls()
- UnbufferedPipeline2.__init__(self, stage)
-
-
-######################################################################
-# Test 18
-######################################################################
-
-class PassThroughTest(PassThroughHandshake):
-
- def iospecfn(self):
- return Signal(16, "out")
-
- def __init__(self):
- stage = PassThroughStage(self.iospecfn)
- PassThroughHandshake.__init__(self, stage)
-
-def resultfn_identical(data_o, expected, i, o):
- res = expected
- assert data_o == res, \
- "%d-%d data %x not match %x\n" \
- % (i, o, data_o, res)
-
-
-######################################################################
-# Test 19
-######################################################################
-
-class ExamplePassAdd1Pipe(PassThroughHandshake):
-
- def __init__(self):
- stage = ExampleStageCls()
- PassThroughHandshake.__init__(self, stage)
-
-
-class ExampleBufPassThruPipe(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- # XXX currently fails: any other permutation works fine.
- # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
- # also fails using UnbufferedPipeline as well
- pipe1 = ExampleBufModeAdd1Pipe()
- pipe2 = ExamplePassAdd1Pipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Test 20
-######################################################################
-
-def iospecfn():
- return Signal(16, name="d_in")
-
-class FIFOTest16(FIFOControl):
-
- def __init__(self):
- stage = PassThroughStage(iospecfn)
- FIFOControl.__init__(self, 2, stage)
-
-
-######################################################################
-# Test 21
-######################################################################
-
-class ExampleFIFOPassThruPipe1(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = FIFOTest16()
- pipe2 = FIFOTest16()
- pipe3 = ExamplePassAdd1Pipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
- m.submodules.pipe3 = pipe3
-
- m.d.comb += self.connect([pipe1, pipe2, pipe3])
-
- return m
-
-
-######################################################################
-# Test 22
-######################################################################
-
-class Example2OpRecord(RecordObject):
- def __init__(self):
- RecordObject.__init__(self)
- self.op1 = Signal(16)
- self.op2 = Signal(16)
-
-
-class ExampleAddRecordObjectStage(StageCls):
-
- def ispec(self):
- """ returns an instance of an Example2OpRecord.
- """
- return Example2OpRecord()
-
- def ospec(self):
- """ returns an output signal which will happen to contain the sum
- of the two inputs
- """
- return Signal(16)
-
- def process(self, i):
- """ process the input data (sums the values in the tuple) and returns it
- """
- return i.op1 + i.op2
-
-
-class ExampleRecordHandshakeAddClass(SimpleHandshake):
-
- def __init__(self):
- addstage = ExampleAddRecordObjectStage()
- SimpleHandshake.__init__(self, stage=addstage)
-
-
-######################################################################
-# Test 23
-######################################################################
-
-def iospecfnrecord():
- return Example2OpRecord()
-
-class FIFOTestRecordControl(FIFOControl):
-
- def __init__(self):
- stage = PassThroughStage(iospecfnrecord)
- FIFOControl.__init__(self, 2, stage)
-
-
-class ExampleFIFORecordObjectPipe(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = FIFOTestRecordControl()
- pipe2 = ExampleRecordHandshakeAddClass()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Test 24
-######################################################################
-
-class FIFOTestRecordAddStageControl(FIFOControl):
-
- def __init__(self):
- stage = ExampleAddRecordObjectStage()
- FIFOControl.__init__(self, 2, stage)
-
-
-
-######################################################################
-# Test 25
-######################################################################
-
-class FIFOTestAdd16(FIFOControl):
-
- def __init__(self):
- stage = ExampleStageCls()
- FIFOControl.__init__(self, 2, stage)
-
-
-class ExampleFIFOAdd2Pipe(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = FIFOTestAdd16()
- pipe2 = FIFOTestAdd16()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Test 26
-######################################################################
-
-def iospecfn24():
- return (Signal(16, name="src1"), Signal(16, name="src2"))
-
-class FIFOTest2x16(FIFOControl):
-
- def __init__(self):
- stage = PassThroughStage(iospecfn2)
- FIFOControl.__init__(self, 2, stage)
-
-
-######################################################################
-# Test 997
-######################################################################
-
-class ExampleBufPassThruPipe2(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- # XXX currently fails: any other permutation works fine.
- # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
- # also fails using UnbufferedPipeline as well
- #pipe1 = ExampleUnBufAdd1Pipe()
- #pipe2 = ExampleBufAdd1Pipe()
- pipe1 = ExampleBufAdd1Pipe()
- pipe2 = ExamplePassAdd1Pipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Test 998
-######################################################################
-
-class ExampleBufPipe3(ControlBase):
- """ Example of how to do delayed pipeline, where the stage signals
- whether it is ready.
- """
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- pipe1 = ExampleBufDelayedPipe()
- pipe2 = ExampleBufPipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-######################################################################
-# Test 999 - XXX FAILS
-# http://bugs.libre-riscv.org/show_bug.cgi?id=57
-######################################################################
-
-class ExampleBufAdd1Pipe(BufferedHandshake):
-
- def __init__(self):
- stage = ExampleStageCls()
- BufferedHandshake.__init__(self, stage)
-
-
-class ExampleUnBufAdd1Pipe(UnbufferedPipeline):
-
- def __init__(self):
- stage = ExampleStageCls()
- UnbufferedPipeline.__init__(self, stage)
-
-
-class ExampleBufUnBufPipe(ControlBase):
-
- def elaborate(self, platform):
- m = ControlBase.elaborate(self, platform)
-
- # XXX currently fails: any other permutation works fine.
- # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
- # also fails using UnbufferedPipeline as well
- #pipe1 = ExampleUnBufAdd1Pipe()
- #pipe2 = ExampleBufAdd1Pipe()
- pipe1 = ExampleBufAdd1Pipe()
- pipe2 = ExampleUnBufAdd1Pipe()
-
- m.submodules.pipe1 = pipe1
- m.submodules.pipe2 = pipe2
-
- m.d.comb += self.connect([pipe1, pipe2])
-
- return m
-
-
-######################################################################
-# Unit Tests
-######################################################################
-
-num_tests = 10
-
-if __name__ == '__main__':
- if False:
- print ("test 1")
- dut = ExampleBufPipe()
- run_simulation(dut, tbench(dut), vcd_name="test_bufpipe.vcd")
-
- print ("test 2")
- dut = ExampleBufPipe2()
- run_simulation(dut, tbench2(dut), vcd_name="test_bufpipe2.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpipe2.il", "w") as f:
- f.write(vl)
-
-
- print ("test 3")
- dut = ExampleBufPipe()
- test = Test3(dut, resultfn_3)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe3.vcd")
-
- print ("test 3.5")
- dut = ExamplePipeline()
- test = Test3(dut, resultfn_3)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_combpipe3.vcd")
-
- print ("test 4")
- dut = ExampleBufPipe2()
- run_simulation(dut, tbench4(dut), vcd_name="test_bufpipe4.vcd")
-
- print ("test 5")
- dut = ExampleBufPipeAdd()
- test = Test5(dut, resultfn_5, stage_ctl=True)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe5.vcd")
-
- print ("test 6")
- dut = ExampleLTPipeline()
- test = Test5(dut, resultfn_6)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltcomb6.vcd")
-
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- list(dut.p.data_i) + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_ltcomb_pipe.il", "w") as f:
- f.write(vl)
-
- print ("test 7")
- dut = ExampleAddRecordPipe()
- data=data_dict()
- test = Test5(dut, resultfn_7, data=data)
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o,
- dut.p.data_i.src1, dut.p.data_i.src2,
- dut.n.data_o.src1, dut.n.data_o.src2]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_recordcomb_pipe.il", "w") as f:
- f.write(vl)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
-
- print ("test 8")
- dut = ExampleBufPipeAddClass()
- data=data_2op()
- test = Test5(dut, resultfn_8, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe8.vcd")
-
- print ("test 9")
- dut = ExampleBufPipeChain2()
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpipechain2.il", "w") as f:
- f.write(vl)
-
- data = data_chain2()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv],
- vcd_name="test_bufpipechain2.vcd")
-
- print ("test 10")
- dut = ExampleLTBufferedPipeDerived()
- test = Test5(dut, resultfn_6)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltbufpipe10.vcd")
- vl = rtlil.convert(dut, ports=ports)
- with open("test_ltbufpipe10.il", "w") as f:
- f.write(vl)
-
- print ("test 11")
- dut = ExampleAddRecordPlaceHolderPipe()
- data=data_placeholder()
- test = Test5(dut, resultfn_test11, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
-
-
- print ("test 12")
- dut = ExampleBufDelayedPipe()
- data = data_chain1()
- test = Test5(dut, resultfn_12, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe12.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpipe12.il", "w") as f:
- f.write(vl)
-
- print ("test 13")
- dut = ExampleUnBufDelayedPipe()
- data = data_chain1()
- test = Test5(dut, resultfn_12, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_unbufpipe13.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_unbufpipe13.il", "w") as f:
- f.write(vl)
-
- print ("test 15")
- dut = ExampleBufModeAdd1Pipe()
- data = data_chain1()
- test = Test5(dut, resultfn_12, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf15.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufunbuf15.il", "w") as f:
- f.write(vl)
-
- print ("test 16")
- dut = ExampleBufModeUnBufPipe()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf16.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufunbuf16.il", "w") as f:
- f.write(vl)
-
- print ("test 17")
- dut = ExampleUnBufAdd1Pipe2()
- data = data_chain1()
- test = Test5(dut, resultfn_12, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_unbufpipe17.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_unbufpipe17.il", "w") as f:
- f.write(vl)
-
- print ("test 18")
- dut = PassThroughTest()
- data = data_chain1()
- test = Test5(dut, resultfn_identical, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_passthru18.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_passthru18.il", "w") as f:
- f.write(vl)
-
- print ("test 19")
- dut = ExampleBufPassThruPipe()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpass19.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpass19.il", "w") as f:
- f.write(vl)
-
- print ("test 20")
- dut = FIFOTest16()
- data = data_chain1()
- test = Test5(dut, resultfn_identical, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_fifo20.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_fifo20.il", "w") as f:
- f.write(vl)
-
- print ("test 21")
- dut = ExampleFIFOPassThruPipe1()
- data = data_chain1()
- test = Test5(dut, resultfn_12, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_fifopass21.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_fifopass21.il", "w") as f:
- f.write(vl)
-
- print ("test 22")
- dut = ExampleRecordHandshakeAddClass()
- data=data_2op()
- test = Test5(dut, resultfn_8, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord22.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i.op1, dut.p.data_i.op2] + \
- [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_addrecord22.il", "w") as f:
- f.write(vl)
-
- print ("test 23")
- dut = ExampleFIFORecordObjectPipe()
- data=data_2op()
- test = Test5(dut, resultfn_8, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord23.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i.op1, dut.p.data_i.op2] + \
- [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_addrecord23.il", "w") as f:
- f.write(vl)
-
- print ("test 24")
- dut = FIFOTestRecordAddStageControl()
- data=data_2op()
- test = Test5(dut, resultfn_8, data=data)
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i.op1, dut.p.data_i.op2] + \
- [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_addrecord24.il", "w") as f:
- f.write(vl)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord24.vcd")
-
- print ("test 25")
- dut = ExampleFIFOAdd2Pipe()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_add2pipe25.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_add2pipe25.il", "w") as f:
- f.write(vl)
-
- print ("test 997")
- dut = ExampleBufPassThruPipe2()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpass997.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpass997.il", "w") as f:
- f.write(vl)
-
- print ("test 998 (fails, bug)")
- dut = ExampleBufPipe3()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe14.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufpipe14.il", "w") as f:
- f.write(vl)
-
- print ("test 999 (expected to fail, which is a bug)")
- dut = ExampleBufUnBufPipe()
- data = data_chain1()
- test = Test5(dut, resultfn_9, data=data)
- run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf999.vcd")
- ports = [dut.p.valid_i, dut.n.ready_i,
- dut.n.valid_o, dut.p.ready_o] + \
- [dut.p.data_i] + [dut.n.data_o]
- vl = rtlil.convert(dut, ports=ports)
- with open("test_bufunbuf999.il", "w") as f:
- f.write(vl)
-
from fpbase import FPNumIn, FPNumOut, FPOpIn, FPOpOut, FPBase, FPState
-from nmoperator import eq
-from singlepipe import SimpleHandshake, ControlBase
+from nmutil.nmoperator import eq
+from nmutil.singlepipe import SimpleHandshake, ControlBase
from test_buf_pipe import data_chain2, Test5
from nmigen.compat.sim import run_simulation
from nmigen.cli import verilog, rtlil
-from multipipe import CombMultiOutPipeline, CombMuxOutPipe
-from multipipe import PriorityCombMuxInPipe
-from singlepipe import SimpleHandshake, RecordObject, Object
+from nmutil.multipipe import CombMultiOutPipeline, CombMuxOutPipe
+from nmutil.multipipe import PriorityCombMuxInPipe
+from nmutil.singlepipe import SimpleHandshake, RecordObject, Object
class PassData2(RecordObject):
from nmigen.compat.sim import run_simulation
from nmigen.cli import verilog, rtlil
-from multipipe import CombMuxOutPipe
-from singlepipe import SimpleHandshake, PassThroughHandshake, RecordObject
+from nmutil.multipipe import CombMuxOutPipe
+from nmutil.singlepipe import SimpleHandshake, PassThroughHandshake, RecordObject
class PassInData(RecordObject):
from nmigen.compat.sim import run_simulation
from nmigen.cli import verilog, rtlil
-from singlepipe import PassThroughStage
-from multipipe import (CombMultiInPipeline, PriorityCombMuxInPipe)
+from nmutil.singlepipe import PassThroughStage
+from nmutil.multipipe import (CombMultiInPipeline, PriorityCombMuxInPipe)
class PassData:
from nmigen import Module
from nmigen.cli import main, verilog
-from singlepipe import (StageChain, SimpleHandshake,
+from nmutil.singlepipe import (StageChain, SimpleHandshake,
PassThroughStage)
from fpbase import FPState
from nmigen import Module
from nmigen.cli import main, verilog
-from singlepipe import (ControlBase, SimpleHandshake, PassThroughStage)
-from multipipe import CombMuxOutPipe
-from multipipe import PriorityCombMuxInPipe
+from nmutil.singlepipe import (ControlBase, SimpleHandshake, PassThroughStage)
+from nmutil.multipipe import CombMuxOutPipe
+from nmutil.multipipe import PriorityCombMuxInPipe
from ieee754.fpcommon.getop import FPADDBaseData
from ieee754.fpcommon.denorm import FPSCData
from math import log
from fpbase import FPNumDecode
-from singlepipe import SimpleHandshake, StageChain
+from nmutil.singlepipe import SimpleHandshake, StageChain
from fpbase import FPState, FPID
from ieee754.fpcommon.getop import FPADDBaseData
from fpbase import FPOpIn, FPOpOut
from fpbase import Trigger
-from singlepipe import (StageChain, SimpleHandshake)
+from nmutil.singlepipe import (StageChain, SimpleHandshake)
from fpbase import FPState, FPID
from ieee754.fpcommon.getop import (FPGetOp, FPADDBaseData, FPGet2Op)
from fpbase import FPNumIn, FPNumOut, FPOpIn, Overflow, FPBase, FPNumBase
from fpbase import MultiShiftRMerge, Trigger
-from singlepipe import (ControlBase, StageChain, SimpleHandshake,
+from nmutil.singlepipe import (ControlBase, StageChain, SimpleHandshake,
PassThroughStage, PrevControl)
-from multipipe import CombMuxOutPipe
-from multipipe import PriorityCombMuxInPipe
+from nmutil.multipipe import CombMuxOutPipe
+from nmutil.multipipe import PriorityCombMuxInPipe
from fpbase import FPState
-import nmoperator
+from nmutil import nmoperator
class FPGetOpMod(Elaboratable):
#from nmigen.cli import main, verilog
-from singlepipe import StageChain, SimpleHandshake
+from nmutil.singlepipe import StageChain, SimpleHandshake
from fpbase import FPState, FPID
from .postcalc import FPAddStage1Data
from fpbase import FPNumOut
from fpbase import FPState
from .roundz import FPRoundData
-from singlepipe import Object
+from nmutil.singlepipe import Object
class FPPackData(Object):
--- /dev/null
+""" IO Control API
+
+ Associated development bugs:
+ * http://bugs.libre-riscv.org/show_bug.cgi?id=64
+ * http://bugs.libre-riscv.org/show_bug.cgi?id=57
+
+ Stage API:
+ ---------
+
+ stage requires compliance with a strict API that may be
+ implemented in several means, including as a static class.
+
+ Stages do not HOLD data, and they definitely do not contain
+ signalling (ready/valid). They do however specify the FORMAT
+ of the incoming and outgoing data, and they provide a means to
+ PROCESS that data (from incoming format to outgoing format).
+
+ Stage Blocks really must be combinatorial blocks. It would be ok
+ to have input come in from sync'd sources (clock-driven) however by
+ doing so they would no longer be deterministic, and chaining such
+ blocks with such side-effects together could result in unexpected,
+ unpredictable, unreproduceable behaviour.
+ So generally to be avoided, then unless you know what you are doing.
+
+ the methods of a stage instance must be as follows:
+
+ * ispec() - Input data format specification. Takes a bit of explaining.
+ The requirements are: something that eventually derives from
+ nmigen Value must be returned *OR* an iterator or iterable
+ or sequence (list, tuple etc.) or generator must *yield*
+ thing(s) that (eventually) derive from the nmigen Value class.
+
+ Complex to state, very simple in practice:
+ see test_buf_pipe.py for over 25 worked examples.
+
+ * ospec() - Output data format specification.
+ format requirements identical to ispec.
+
+ * process(m, i) - Optional function for processing ispec-formatted data.
+ returns a combinatorial block of a result that
+ may be assigned to the output, by way of the "nmoperator.eq"
+ function. Note that what is returned here can be
+ extremely flexible. Even a dictionary can be returned
+ as long as it has fields that match precisely with the
+ Record into which its values is intended to be assigned.
+ Again: see example unit tests for details.
+
+ * setup(m, i) - Optional function for setting up submodules.
+ may be used for more complex stages, to link
+ the input (i) to submodules. must take responsibility
+ for adding those submodules to the module (m).
+ the submodules must be combinatorial blocks and
+ must have their inputs and output linked combinatorially.
+
+ Both StageCls (for use with non-static classes) and Stage (for use
+ by static classes) are abstract classes from which, for convenience
+ and as a courtesy to other developers, anything conforming to the
+ Stage API may *choose* to derive. See Liskov Substitution Principle:
+ https://en.wikipedia.org/wiki/Liskov_substitution_principle
+
+ StageChain:
+ ----------
+
+ A useful combinatorial wrapper around stages that chains them together
+ and then presents a Stage-API-conformant interface. By presenting
+ the same API as the stages it wraps, it can clearly be used recursively.
+
+ ControlBase:
+ -----------
+
+ The base class for pipelines. Contains previous and next ready/valid/data.
+ Also has an extremely useful "connect" function that can be used to
+ connect a chain of pipelines and present the exact same prev/next
+ ready/valid/data API.
+
+ Note: pipelines basically do not become pipelines as such until
+ handed to a derivative of ControlBase. ControlBase itself is *not*
+ strictly considered a pipeline class. Wishbone and AXI4 (master or
+ slave) could be derived from ControlBase, for example.
+"""
+
+from nmigen import Signal, Cat, Const, Module, Value, Elaboratable
+from nmigen.cli import verilog, rtlil
+from nmigen.hdl.rec import Record
+
+from collections.abc import Sequence, Iterable
+from collections import OrderedDict
+
+from nmutil import nmoperator
+
+
+class Object:
+ def __init__(self):
+ self.fields = OrderedDict()
+
+ def __setattr__(self, k, v):
+ print ("kv", k, v)
+ if (k.startswith('_') or k in ["fields", "name", "src_loc"] or
+ k in dir(Object) or "fields" not in self.__dict__):
+ return object.__setattr__(self, k, v)
+ self.fields[k] = v
+
+ def __getattr__(self, k):
+ if k in self.__dict__:
+ return object.__getattr__(self, k)
+ try:
+ return self.fields[k]
+ except KeyError as e:
+ raise AttributeError(e)
+
+ def __iter__(self):
+ for x in self.fields.values(): # OrderedDict so order is preserved
+ if isinstance(x, Iterable):
+ yield from x
+ else:
+ yield x
+
+ def eq(self, inp):
+ res = []
+ for (k, o) in self.fields.items():
+ i = getattr(inp, k)
+ print ("eq", o, i)
+ rres = o.eq(i)
+ if isinstance(rres, Sequence):
+ res += rres
+ else:
+ res.append(rres)
+ print (res)
+ return res
+
+ def ports(self): # being called "keys" would be much better
+ return list(self)
+
+
+class RecordObject(Record):
+ def __init__(self, layout=None, name=None):
+ Record.__init__(self, layout=layout or [], name=None)
+
+ def __setattr__(self, k, v):
+ #print (dir(Record))
+ if (k.startswith('_') or k in ["fields", "name", "src_loc"] or
+ k in dir(Record) or "fields" not in self.__dict__):
+ return object.__setattr__(self, k, v)
+ self.fields[k] = v
+ #print ("RecordObject setattr", k, v)
+ if isinstance(v, Record):
+ newlayout = {k: (k, v.layout)}
+ elif isinstance(v, Value):
+ newlayout = {k: (k, v.shape())}
+ else:
+ newlayout = {k: (k, nmoperator.shape(v))}
+ self.layout.fields.update(newlayout)
+
+ def __iter__(self):
+ for x in self.fields.values(): # remember: fields is an OrderedDict
+ if isinstance(x, Iterable):
+ yield from x # a bit like flatten (nmigen.tools)
+ else:
+ yield x
+
+ def ports(self): # would be better being called "keys"
+ return list(self)
+
+
+class PrevControl(Elaboratable):
+ """ contains signals that come *from* the previous stage (both in and out)
+ * valid_i: previous stage indicating all incoming data is valid.
+ may be a multi-bit signal, where all bits are required
+ to be asserted to indicate "valid".
+ * ready_o: output to next stage indicating readiness to accept data
+ * data_i : an input - MUST be added by the USER of this class
+ """
+
+ def __init__(self, i_width=1, stage_ctl=False):
+ self.stage_ctl = stage_ctl
+ self.valid_i = Signal(i_width, name="p_valid_i") # prev >>in self
+ self._ready_o = Signal(name="p_ready_o") # prev <<out self
+ self.data_i = None # XXX MUST BE ADDED BY USER
+ if stage_ctl:
+ self.s_ready_o = Signal(name="p_s_o_rdy") # prev <<out self
+ self.trigger = Signal(reset_less=True)
+
+ @property
+ def ready_o(self):
+ """ public-facing API: indicates (externally) that stage is ready
+ """
+ if self.stage_ctl:
+ return self.s_ready_o # set dynamically by stage
+ return self._ready_o # return this when not under dynamic control
+
+ def _connect_in(self, prev, direct=False, fn=None, do_data=True):
+ """ internal helper function to connect stage to an input source.
+ do not use to connect stage-to-stage!
+ """
+ valid_i = prev.valid_i if direct else prev.valid_i_test
+ res = [self.valid_i.eq(valid_i),
+ prev.ready_o.eq(self.ready_o)]
+ if do_data is False:
+ return res
+ data_i = fn(prev.data_i) if fn is not None else prev.data_i
+ return res + [nmoperator.eq(self.data_i, data_i)]
+
+ @property
+ def valid_i_test(self):
+ vlen = len(self.valid_i)
+ if vlen > 1:
+ # multi-bit case: valid only when valid_i is all 1s
+ all1s = Const(-1, (len(self.valid_i), False))
+ valid_i = (self.valid_i == all1s)
+ else:
+ # single-bit valid_i case
+ valid_i = self.valid_i
+
+ # when stage indicates not ready, incoming data
+ # must "appear" to be not ready too
+ if self.stage_ctl:
+ valid_i = valid_i & self.s_ready_o
+
+ return valid_i
+
+ def elaborate(self, platform):
+ m = Module()
+ m.d.comb += self.trigger.eq(self.valid_i_test & self.ready_o)
+ return m
+
+ def eq(self, i):
+ return [nmoperator.eq(self.data_i, i.data_i),
+ self.ready_o.eq(i.ready_o),
+ self.valid_i.eq(i.valid_i)]
+
+ def __iter__(self):
+ yield self.valid_i
+ yield self.ready_o
+ if hasattr(self.data_i, "ports"):
+ yield from self.data_i.ports()
+ elif isinstance(self.data_i, Sequence):
+ yield from self.data_i
+ else:
+ yield self.data_i
+
+ def ports(self):
+ return list(self)
+
+
+class NextControl(Elaboratable):
+ """ contains the signals that go *to* the next stage (both in and out)
+ * valid_o: output indicating to next stage that data is valid
+ * ready_i: input from next stage indicating that it can accept data
+ * data_o : an output - MUST be added by the USER of this class
+ """
+ def __init__(self, stage_ctl=False):
+ self.stage_ctl = stage_ctl
+ self.valid_o = Signal(name="n_valid_o") # self out>> next
+ self.ready_i = Signal(name="n_ready_i") # self <<in next
+ self.data_o = None # XXX MUST BE ADDED BY USER
+ #if self.stage_ctl:
+ self.d_valid = Signal(reset=1) # INTERNAL (data valid)
+ self.trigger = Signal(reset_less=True)
+
+ @property
+ def ready_i_test(self):
+ if self.stage_ctl:
+ return self.ready_i & self.d_valid
+ return self.ready_i
+
+ def connect_to_next(self, nxt, do_data=True):
+ """ helper function to connect to the next stage data/valid/ready.
+ data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
+ use this when connecting stage-to-stage
+ """
+ res = [nxt.valid_i.eq(self.valid_o),
+ self.ready_i.eq(nxt.ready_o)]
+ if do_data:
+ res.append(nmoperator.eq(nxt.data_i, self.data_o))
+ return res
+
+ def _connect_out(self, nxt, direct=False, fn=None, do_data=True):
+ """ internal helper function to connect stage to an output source.
+ do not use to connect stage-to-stage!
+ """
+ ready_i = nxt.ready_i if direct else nxt.ready_i_test
+ res = [nxt.valid_o.eq(self.valid_o),
+ self.ready_i.eq(ready_i)]
+ if not do_data:
+ return res
+ data_o = fn(nxt.data_o) if fn is not None else nxt.data_o
+ return res + [nmoperator.eq(data_o, self.data_o)]
+
+ def elaborate(self, platform):
+ m = Module()
+ m.d.comb += self.trigger.eq(self.ready_i_test & self.valid_o)
+ return m
+
+ def __iter__(self):
+ yield self.ready_i
+ yield self.valid_o
+ if hasattr(self.data_o, "ports"):
+ yield from self.data_o.ports()
+ elif isinstance(self.data_o, Sequence):
+ yield from self.data_o
+ else:
+ yield self.data_o
+
+ def ports(self):
+ return list(self)
+
from nmigen.cli import verilog, rtlil
from nmigen.lib.coding import PriorityEncoder
from nmigen.hdl.rec import Record, Layout
-from stageapi import _spec
+from nmutil.stageapi import _spec
from collections.abc import Sequence
from abc import ABCMeta, abstractmethod
from collections.abc import Sequence, Iterable
from collections import OrderedDict
-from queue import Queue
+from nmutil.queue import Queue
import inspect
from nmigen.compat.fhdl.bitcontainer import value_bits_sign
from contextlib import contextmanager
-from nmoperator import eq
-from singlepipe import StageCls, ControlBase, BufferedHandshake
-from singlepipe import UnbufferedPipeline
+from nmutil.nmoperator import eq
+from nmutil.singlepipe import StageCls, ControlBase, BufferedHandshake
+from nmutil.singlepipe import UnbufferedPipeline
# The following example shows how pyrtl can be used to make some interesting
--- /dev/null
+# Copyright (c) 2014 - 2019 The Regents of the University of
+# California (Regents). All Rights Reserved. Redistribution and use in
+# source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# two paragraphs of disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# two paragraphs of disclaimer in the documentation and/or other materials
+# provided with the distribution.
+# * Neither the name of the Regents nor the names of its contributors
+# may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+# ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+# REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+# ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
+# TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+# MODIFICATIONS.
+
+from nmigen import Module, Signal, Memory, Mux, Elaboratable
+from nmigen.tools import bits_for
+from nmigen.cli import main
+from nmigen.lib.fifo import FIFOInterface
+
+# translated from https://github.com/freechipsproject/chisel3/blob/a4a29e29c3f1eed18f851dcf10bdc845571dfcb6/src/main/scala/chisel3/util/Decoupled.scala#L185 # noqa
+
+
+class Queue(FIFOInterface, Elaboratable):
+ def __init__(self, width, depth, fwft=True, pipe=False):
+ """ Queue (FIFO) with pipe mode and first-write fall-through capability
+
+ * :width: width of Queue data in/out
+ * :depth: queue depth. NOTE: may be set to 0 (this is ok)
+ * :fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
+ * :pipe : pipe mode. NOTE: this mode can cause unanticipated
+ problems. when read is enabled, so is writeable.
+ therefore if read is enabled, the data ABSOLUTELY MUST
+ be read.
+
+ fwft mode = True basically means that the data may be transferred
+ combinatorially from input to output.
+
+ Attributes:
+ * level: available free space (number of unread entries)
+
+ din = enq_data, writable = enq_ready, we = enq_valid
+ dout = deq_data, re = deq_ready, readable = deq_valid
+ """
+ FIFOInterface.__init__(self, width, depth, fwft)
+ self.pipe = pipe
+ self.depth = depth
+ self.level = Signal(bits_for(depth))
+
+ def elaborate(self, platform):
+ m = Module()
+
+ # set up an SRAM. XXX bug in Memory: cannot create SRAM of depth 1
+ ram = Memory(self.width, self.depth if self.depth > 1 else 2)
+ m.submodules.ram_read = ram_read = ram.read_port(synchronous=False)
+ m.submodules.ram_write = ram_write = ram.write_port()
+
+ # convenience names
+ p_ready_o = self.writable
+ p_valid_i = self.we
+ enq_data = self.din
+
+ n_valid_o = self.readable
+ n_ready_i = self.re
+ deq_data = self.dout
+
+ # intermediaries
+ ptr_width = bits_for(self.depth - 1) if self.depth > 1 else 0
+ enq_ptr = Signal(ptr_width) # cyclic pointer to "insert" point (wrport)
+ deq_ptr = Signal(ptr_width) # cyclic pointer to "remove" point (rdport)
+ maybe_full = Signal() # not reset_less (set by sync)
+
+ # temporaries
+ do_enq = Signal(reset_less=True)
+ do_deq = Signal(reset_less=True)
+ ptr_diff = Signal(ptr_width)
+ ptr_match = Signal(reset_less=True)
+ empty = Signal(reset_less=True)
+ full = Signal(reset_less=True)
+ enq_max = Signal(reset_less=True)
+ deq_max = Signal(reset_less=True)
+
+ m.d.comb += [ptr_match.eq(enq_ptr == deq_ptr), # read-ptr = write-ptr
+ ptr_diff.eq(enq_ptr - deq_ptr),
+ enq_max.eq(enq_ptr == self.depth - 1),
+ deq_max.eq(deq_ptr == self.depth - 1),
+ empty.eq(ptr_match & ~maybe_full),
+ full.eq(ptr_match & maybe_full),
+ do_enq.eq(p_ready_o & p_valid_i), # write conditions ok
+ do_deq.eq(n_ready_i & n_valid_o), # read conditions ok
+
+ # set readable and writable (NOTE: see pipe mode below)
+ n_valid_o.eq(~empty), # cannot read if empty!
+ p_ready_o.eq(~full), # cannot write if full!
+
+ # set up memory and connect to input and output
+ ram_write.addr.eq(enq_ptr),
+ ram_write.data.eq(enq_data),
+ ram_write.en.eq(do_enq),
+ ram_read.addr.eq(deq_ptr),
+ deq_data.eq(ram_read.data) # NOTE: overridden in fwft mode
+ ]
+
+ # under write conditions, SRAM write-pointer moves on next clock
+ with m.If(do_enq):
+ m.d.sync += enq_ptr.eq(Mux(enq_max, 0, enq_ptr+1))
+
+ # under read conditions, SRAM read-pointer moves on next clock
+ with m.If(do_deq):
+ m.d.sync += deq_ptr.eq(Mux(deq_max, 0, deq_ptr+1))
+
+ # if read-but-not-write or write-but-not-read, maybe_full set
+ with m.If(do_enq != do_deq):
+ m.d.sync += maybe_full.eq(do_enq)
+
+ # first-word fall-through: same as "flow" parameter in Chisel3 Queue
+ # basically instead of relying on the Memory characteristics (which
+ # in FPGAs do not have write-through), then when the queue is empty
+ # take the output directly from the input, i.e. *bypass* the SRAM.
+ # this done combinatorially to give the exact same characteristics
+ # as Memory "write-through"... without relying on a changing API
+ if self.fwft:
+ with m.If(p_valid_i):
+ m.d.comb += n_valid_o.eq(1)
+ with m.If(empty):
+ m.d.comb += deq_data.eq(enq_data)
+ m.d.comb += do_deq.eq(0)
+ with m.If(n_ready_i):
+ m.d.comb += do_enq.eq(0)
+
+ # pipe mode: if next stage says it's ready (readable), we
+ # *must* declare the input ready (writeable).
+ if self.pipe:
+ with m.If(n_ready_i):
+ m.d.comb += p_ready_o.eq(1)
+
+ # set the count (available free space), optimise on power-of-two
+ if self.depth == 1 << ptr_width: # is depth a power of 2
+ m.d.comb += self.level.eq(
+ Mux(maybe_full & ptr_match, self.depth, 0) | ptr_diff)
+ else:
+ m.d.comb += self.level.eq(Mux(ptr_match,
+ Mux(maybe_full, self.depth, 0),
+ Mux(deq_ptr > enq_ptr,
+ self.depth + ptr_diff,
+ ptr_diff)))
+
+ return m
+
+
+if __name__ == "__main__":
+ reg_stage = Queue(1, 1, pipe=True)
+ break_ready_chain_stage = Queue(1, 1, pipe=True, fwft=True)
+ m = Module()
+ ports = []
+
+ def queue_ports(queue, name_prefix):
+ retval = []
+ for name in ["level",
+ "dout",
+ "readable",
+ "writable"]:
+ port = getattr(queue, name)
+ signal = Signal(port.shape(), name=name_prefix+name)
+ m.d.comb += signal.eq(port)
+ retval.append(signal)
+ for name in ["re",
+ "din",
+ "we"]:
+ port = getattr(queue, name)
+ signal = Signal(port.shape(), name=name_prefix+name)
+ m.d.comb += port.eq(signal)
+ retval.append(signal)
+ return retval
+
+ m.submodules.reg_stage = reg_stage
+ ports += queue_ports(reg_stage, "reg_stage_")
+ m.submodules.break_ready_chain_stage = break_ready_chain_stage
+ ports += queue_ports(break_ready_chain_stage, "break_ready_chain_stage_")
+ main(m, ports=ports)
from nmigen.cli import verilog, rtlil
from nmigen.hdl.rec import Record
-from queue import Queue
+from nmutil.queue import Queue
import inspect
-from iocontrol import (PrevControl, NextControl, Object, RecordObject)
-from stageapi import (_spec, StageCls, Stage, StageChain, StageHelper)
-import nmoperator
+from nmutil.iocontrol import (PrevControl, NextControl, Object, RecordObject)
+from nmutil.stageapi import (_spec, StageCls, Stage, StageChain, StageHelper)
+from nmutil import nmoperator
class RecordBasedStage(Stage):
--- /dev/null
+""" Stage API
+
+ Associated development bugs:
+ * http://bugs.libre-riscv.org/show_bug.cgi?id=64
+ * http://bugs.libre-riscv.org/show_bug.cgi?id=57
+
+ Stage API:
+ ---------
+
+ stage requires compliance with a strict API that may be
+ implemented in several means, including as a static class.
+
+ Stages do not HOLD data, and they definitely do not contain
+ signalling (ready/valid). They do however specify the FORMAT
+ of the incoming and outgoing data, and they provide a means to
+ PROCESS that data (from incoming format to outgoing format).
+
+ Stage Blocks really should be combinatorial blocks (Moore FSMs).
+ It would be ok to have input come in from sync'd sources
+ (clock-driven, Mealy FSMs) however by doing so they would no longer
+ be deterministic, and chaining such blocks with such side-effects
+ together could result in unexpected, unpredictable, unreproduceable
+ behaviour.
+
+ So generally to be avoided, then unless you know what you are doing.
+ https://en.wikipedia.org/wiki/Moore_machine
+ https://en.wikipedia.org/wiki/Mealy_machine
+
+ the methods of a stage instance must be as follows:
+
+ * ispec() - Input data format specification. Takes a bit of explaining.
+ The requirements are: something that eventually derives from
+ nmigen Value must be returned *OR* an iterator or iterable
+ or sequence (list, tuple etc.) or generator must *yield*
+ thing(s) that (eventually) derive from the nmigen Value class.
+
+ Complex to state, very simple in practice:
+ see test_buf_pipe.py for over 25 worked examples.
+
+ * ospec() - Output data format specification.
+ format requirements identical to ispec.
+
+ * process(m, i) - Optional function for processing ispec-formatted data.
+ returns a combinatorial block of a result that
+ may be assigned to the output, by way of the "nmoperator.eq"
+ function. Note that what is returned here can be
+ extremely flexible. Even a dictionary can be returned
+ as long as it has fields that match precisely with the
+ Record into which its values is intended to be assigned.
+ Again: see example unit tests for details.
+
+ * setup(m, i) - Optional function for setting up submodules.
+ may be used for more complex stages, to link
+ the input (i) to submodules. must take responsibility
+ for adding those submodules to the module (m).
+ the submodules must be combinatorial blocks and
+ must have their inputs and output linked combinatorially.
+
+ Both StageCls (for use with non-static classes) and Stage (for use
+ by static classes) are abstract classes from which, for convenience
+ and as a courtesy to other developers, anything conforming to the
+ Stage API may *choose* to derive. See Liskov Substitution Principle:
+ https://en.wikipedia.org/wiki/Liskov_substitution_principle
+
+ StageChain:
+ ----------
+
+ A useful combinatorial wrapper around stages that chains them together
+ and then presents a Stage-API-conformant interface. By presenting
+ the same API as the stages it wraps, it can clearly be used recursively.
+
+ StageHelper:
+ ----------
+
+ A convenience wrapper around a Stage-API-compliant "thing" which
+ complies with the Stage API and provides mandatory versions of
+ all the optional bits.
+"""
+
+from abc import ABCMeta, abstractmethod
+import inspect
+
+from nmutil import nmoperator
+
+
+def _spec(fn, name=None):
+ """ useful function that determines if "fn" has an argument "name".
+ if so, fn(name) is called otherwise fn() is called.
+
+ means that ispec and ospec can be declared with *or without*
+ a name argument. normally it would be necessary to have
+ "ispec(name=None)" to achieve the same effect.
+ """
+ if name is None:
+ return fn()
+ varnames = dict(inspect.getmembers(fn.__code__))['co_varnames']
+ if 'name' in varnames:
+ return fn(name=name)
+ return fn()
+
+
+class StageCls(metaclass=ABCMeta):
+ """ Class-based "Stage" API. requires instantiation (after derivation)
+
+ see "Stage API" above.. Note: python does *not* require derivation
+ from this class. All that is required is that the pipelines *have*
+ the functions listed in this class. Derivation from this class
+ is therefore merely a "courtesy" to maintainers.
+ """
+ @abstractmethod
+ def ispec(self): pass # REQUIRED
+ @abstractmethod
+ def ospec(self): pass # REQUIRED
+ #@abstractmethod
+ #def setup(self, m, i): pass # OPTIONAL
+ #@abstractmethod
+ #def process(self, i): pass # OPTIONAL
+
+
+class Stage(metaclass=ABCMeta):
+ """ Static "Stage" API. does not require instantiation (after derivation)
+
+ see "Stage API" above. Note: python does *not* require derivation
+ from this class. All that is required is that the pipelines *have*
+ the functions listed in this class. Derivation from this class
+ is therefore merely a "courtesy" to maintainers.
+ """
+ @staticmethod
+ @abstractmethod
+ def ispec(): pass
+
+ @staticmethod
+ @abstractmethod
+ def ospec(): pass
+
+ #@staticmethod
+ #@abstractmethod
+ #def setup(m, i): pass
+
+ #@staticmethod
+ #@abstractmethod
+ #def process(i): pass
+
+
+class StageHelper(Stage):
+ """ a convenience wrapper around something that is Stage-API-compliant.
+ (that "something" may be a static class, for example).
+
+ StageHelper happens to also be compliant with the Stage API,
+ it differs from the stage that it wraps in that all the "optional"
+ functions are provided (hence the designation "convenience wrapper")
+ """
+ def __init__(self, stage):
+ self.stage = stage
+ self._ispecfn = None
+ self._ospecfn = None
+ if stage is not None:
+ self.set_specs(self, self)
+
+ def ospec(self, name):
+ assert self._ospecfn is not None
+ return _spec(self._ospecfn, name)
+
+ def ispec(self, name):
+ assert self._ispecfn is not None
+ return _spec(self._ispecfn, name)
+
+ def set_specs(self, p, n):
+ """ sets up the ispecfn and ospecfn for getting input and output data
+ """
+ if hasattr(p, "stage"):
+ p = p.stage
+ if hasattr(n, "stage"):
+ n = n.stage
+ self._ispecfn = p.ispec
+ self._ospecfn = n.ospec
+
+ def new_specs(self, name):
+ """ allocates new ispec and ospec pair
+ """
+ return (_spec(self.ispec, "%s_i" % name),
+ _spec(self.ospec, "%s_o" % name))
+
+ def process(self, i):
+ if self.stage and hasattr(self.stage, "process"):
+ return self.stage.process(i)
+ return i
+
+ def setup(self, m, i):
+ if self.stage is not None and hasattr(self.stage, "setup"):
+ self.stage.setup(m, i)
+
+ def _postprocess(self, i): # XXX DISABLED
+ return i # RETURNS INPUT
+ if hasattr(self.stage, "postprocess"):
+ return self.stage.postprocess(i)
+ return i
+
+
+class StageChain(StageHelper):
+ """ pass in a list of stages, and they will automatically be
+ chained together via their input and output specs into a
+ combinatorial chain, to create one giant combinatorial block.
+
+ the end result basically conforms to the exact same Stage API.
+
+ * input to this class will be the input of the first stage
+ * output of first stage goes into input of second
+ * output of second goes into input into third
+ * ... (etc. etc.)
+ * the output of this class will be the output of the last stage
+
+ NOTE: whilst this is very similar to ControlBase.connect(), it is
+ *really* important to appreciate that StageChain is pure
+ combinatorial and bypasses (does not involve, at all, ready/valid
+ signalling of any kind).
+
+ ControlBase.connect on the other hand respects, connects, and uses
+ ready/valid signalling.
+
+ Arguments:
+
+ * :chain: a chain of combinatorial blocks conforming to the Stage API
+ NOTE: StageChain.ispec and ospect have to have something
+ to return (beginning and end specs of the chain),
+ therefore the chain argument must be non-zero length
+
+ * :specallocate: if set, new input and output data will be allocated
+ and connected (eq'd) to each chained Stage.
+ in some cases if this is not done, the nmigen warning
+ "driving from two sources, module is being flattened"
+ will be issued.
+
+ NOTE: do NOT use StageChain with combinatorial blocks that have
+ side-effects (state-based / clock-based input) or conditional
+ (inter-chain) dependencies, unless you really know what you are doing.
+ """
+ def __init__(self, chain, specallocate=False):
+ assert len(chain) > 0, "stage chain must be non-zero length"
+ self.chain = chain
+ StageHelper.__init__(self, None)
+ self.setup = self._sa_setup if specallocate else self._na_setup
+ self.set_specs(self.chain[0], self.chain[-1])
+
+ def _sa_setup(self, m, i):
+ for (idx, c) in enumerate(self.chain):
+ if hasattr(c, "setup"):
+ c.setup(m, i) # stage may have some module stuff
+ ofn = self.chain[idx].ospec # last assignment survives
+ o = _spec(ofn, 'chainin%d' % idx)
+ m.d.comb += nmoperator.eq(o, c.process(i)) # process input into "o"
+ if idx == len(self.chain)-1:
+ break
+ ifn = self.chain[idx+1].ispec # new input on next loop
+ i = _spec(ifn, 'chainin%d' % (idx+1))
+ m.d.comb += nmoperator.eq(i, o) # assign to next input
+ self.o = o
+ return self.o # last loop is the output
+
+ def _na_setup(self, m, i):
+ for (idx, c) in enumerate(self.chain):
+ if hasattr(c, "setup"):
+ c.setup(m, i) # stage may have some module stuff
+ i = o = c.process(i) # store input into "o"
+ self.o = o
+ return self.o # last loop is the output
+
+ def process(self, i):
+ return self.o # conform to Stage API: return last-loop output
+
+
--- /dev/null
+""" Pipeline and BufferedHandshake examples
+"""
+
+from nmutil.nmoperator import eq
+from nmutil.iocontrol import (PrevControl, NextControl)
+from nmutil.singlepipe import (PrevControl, NextControl, ControlBase,
+ StageCls, Stage, StageChain,
+ BufferedHandshake, UnbufferedPipeline)
+
+from nmigen import Signal, Module
+from nmigen.cli import verilog, rtlil
+
+
+class ExampleAddStage(StageCls):
+ """ an example of how to use the buffered pipeline, as a class instance
+ """
+
+ def ispec(self):
+ """ returns a tuple of input signals which will be the incoming data
+ """
+ return (Signal(16), Signal(16))
+
+ def ospec(self):
+ """ returns an output signal which will happen to contain the sum
+ of the two inputs
+ """
+ return Signal(16)
+
+ def process(self, i):
+ """ process the input data (sums the values in the tuple) and returns it
+ """
+ return i[0] + i[1]
+
+
+class ExampleBufPipeAdd(BufferedHandshake):
+ """ an example of how to use the buffered pipeline, using a class instance
+ """
+
+ def __init__(self):
+ addstage = ExampleAddStage()
+ BufferedHandshake.__init__(self, addstage)
+
+
+class ExampleStage(Stage):
+ """ an example of how to use the buffered pipeline, in a static class
+ fashion
+ """
+
+ def ispec():
+ return Signal(16, name="example_input_signal")
+
+ def ospec():
+ return Signal(16, name="example_output_signal")
+
+ def process(i):
+ """ process the input data and returns it (adds 1)
+ """
+ return i + 1
+
+
+class ExampleStageCls(StageCls):
+ """ an example of how to use the buffered pipeline, in a static class
+ fashion
+ """
+
+ def ispec(self):
+ return Signal(16, name="example_input_signal")
+
+ def ospec(self):
+ return Signal(16, name="example_output_signal")
+
+ def process(self, i):
+ """ process the input data and returns it (adds 1)
+ """
+ return i + 1
+
+
+class ExampleBufPipe(BufferedHandshake):
+ """ an example of how to use the buffered pipeline.
+ """
+
+ def __init__(self):
+ BufferedHandshake.__init__(self, ExampleStage)
+
+
+class ExamplePipeline(UnbufferedPipeline):
+ """ an example of how to use the unbuffered pipeline.
+ """
+
+ def __init__(self):
+ UnbufferedPipeline.__init__(self, ExampleStage)
+
+
+if __name__ == '__main__':
+ dut = ExampleBufPipe()
+ vl = rtlil.convert(dut, ports=dut.ports())
+ with open("test_bufpipe.il", "w") as f:
+ f.write(vl)
+
+ dut = ExamplePipeline()
+ vl = rtlil.convert(dut, ports=dut.ports())
+ with open("test_combpipe.il", "w") as f:
+ f.write(vl)
--- /dev/null
+""" Unit tests for Buffered and Unbuffered pipelines
+
+ contains useful worked examples of how to use the Pipeline API,
+ including:
+
+ * Combinatorial Stage "Chaining"
+ * class-based data stages
+ * nmigen module-based data stages
+ * special nmigen module-based data stage, where the stage *is* the module
+ * Record-based data stages
+ * static-class data stages
+ * multi-stage pipelines (and how to connect them)
+ * how to *use* the pipelines (see Test5) - how to get data in and out
+
+"""
+
+from nmigen import Module, Signal, Mux, Const, Elaboratable
+from nmigen.hdl.rec import Record
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+
+from example_buf_pipe import ExampleBufPipe, ExampleBufPipeAdd
+from example_buf_pipe import ExamplePipeline, UnbufferedPipeline
+from example_buf_pipe import ExampleStageCls
+from example_buf_pipe import PrevControl, NextControl, BufferedHandshake
+from example_buf_pipe import StageChain, ControlBase, StageCls
+from nmutil.singlepipe import UnbufferedPipeline2
+from nmutil.singlepipe import SimpleHandshake
+from nmutil.singlepipe import PassThroughHandshake
+from nmutil.singlepipe import PassThroughStage
+from nmutil.singlepipe import FIFOControl
+from nmutil.singlepipe import RecordObject
+
+from random import randint, seed
+
+#seed(4)
+
+
+def check_o_n_valid(dut, val):
+ o_n_valid = yield dut.n.valid_o
+ assert o_n_valid == val
+
+def check_o_n_valid2(dut, val):
+ o_n_valid = yield dut.n.valid_o
+ assert o_n_valid == val
+
+
+def tbench(dut):
+ #yield dut.i_p_rst.eq(1)
+ yield dut.n.ready_i.eq(0)
+ #yield dut.p.ready_o.eq(0)
+ yield
+ yield
+ #yield dut.i_p_rst.eq(0)
+ yield dut.n.ready_i.eq(1)
+ yield dut.p.data_i.eq(5)
+ yield dut.p.valid_i.eq(1)
+ yield
+
+ yield dut.p.data_i.eq(7)
+ yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed
+ yield
+ yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt
+
+ yield dut.p.data_i.eq(2)
+ yield
+ yield dut.n.ready_i.eq(0) # begin going into "stall" (next stage says ready)
+ yield dut.p.data_i.eq(9)
+ yield
+ yield dut.p.valid_i.eq(0)
+ yield dut.p.data_i.eq(12)
+ yield
+ yield dut.p.data_i.eq(32)
+ yield dut.n.ready_i.eq(1)
+ yield
+ yield from check_o_n_valid(dut, 1) # buffer still needs to output
+ yield
+ yield from check_o_n_valid(dut, 1) # buffer still needs to output
+ yield
+ yield from check_o_n_valid(dut, 0) # buffer outputted, *now* we're done.
+ yield
+
+
+def tbench2(dut):
+ #yield dut.p.i_rst.eq(1)
+ yield dut.n.ready_i.eq(0)
+ #yield dut.p.ready_o.eq(0)
+ yield
+ yield
+ #yield dut.p.i_rst.eq(0)
+ yield dut.n.ready_i.eq(1)
+ yield dut.p.data_i.eq(5)
+ yield dut.p.valid_i.eq(1)
+ yield
+
+ yield dut.p.data_i.eq(7)
+ yield from check_o_n_valid2(dut, 0) # effects of i_p_valid delayed 2 clocks
+ yield
+ yield from check_o_n_valid2(dut, 0) # effects of i_p_valid delayed 2 clocks
+
+ yield dut.p.data_i.eq(2)
+ yield
+ yield from check_o_n_valid2(dut, 1) # ok *now* i_p_valid effect is felt
+ yield dut.n.ready_i.eq(0) # begin going into "stall" (next stage says ready)
+ yield dut.p.data_i.eq(9)
+ yield
+ yield dut.p.valid_i.eq(0)
+ yield dut.p.data_i.eq(12)
+ yield
+ yield dut.p.data_i.eq(32)
+ yield dut.n.ready_i.eq(1)
+ yield
+ yield from check_o_n_valid2(dut, 1) # buffer still needs to output
+ yield
+ yield from check_o_n_valid2(dut, 1) # buffer still needs to output
+ yield
+ yield from check_o_n_valid2(dut, 1) # buffer still needs to output
+ yield
+ yield from check_o_n_valid2(dut, 0) # buffer outputted, *now* we're done.
+ yield
+ yield
+ yield
+
+
+class Test3:
+ def __init__(self, dut, resultfn):
+ self.dut = dut
+ self.resultfn = resultfn
+ self.data = []
+ for i in range(num_tests):
+ #data.append(randint(0, 1<<16-1))
+ self.data.append(i+1)
+ self.i = 0
+ self.o = 0
+
+ def send(self):
+ while self.o != len(self.data):
+ send_range = randint(0, 3)
+ for j in range(randint(1,10)):
+ if send_range == 0:
+ send = True
+ else:
+ send = randint(0, send_range) != 0
+ o_p_ready = yield self.dut.p.ready_o
+ if not o_p_ready:
+ yield
+ continue
+ if send and self.i != len(self.data):
+ yield self.dut.p.valid_i.eq(1)
+ yield self.dut.p.data_i.eq(self.data[self.i])
+ self.i += 1
+ else:
+ yield self.dut.p.valid_i.eq(0)
+ yield
+
+ def rcv(self):
+ while self.o != len(self.data):
+ stall_range = randint(0, 3)
+ for j in range(randint(1,10)):
+ stall = randint(0, stall_range) != 0
+ yield self.dut.n.ready_i.eq(stall)
+ yield
+ o_n_valid = yield self.dut.n.valid_o
+ i_n_ready = yield self.dut.n.ready_i_test
+ if not o_n_valid or not i_n_ready:
+ continue
+ data_o = yield self.dut.n.data_o
+ self.resultfn(data_o, self.data[self.o], self.i, self.o)
+ self.o += 1
+ if self.o == len(self.data):
+ break
+
+def resultfn_3(data_o, expected, i, o):
+ assert data_o == expected + 1, \
+ "%d-%d data %x not match %x\n" \
+ % (i, o, data_o, expected)
+
+def data_placeholder():
+ data = []
+ for i in range(num_tests):
+ d = PlaceHolder()
+ d.src1 = randint(0, 1<<16-1)
+ d.src2 = randint(0, 1<<16-1)
+ data.append(d)
+ return data
+
+def data_dict():
+ data = []
+ for i in range(num_tests):
+ data.append({'src1': randint(0, 1<<16-1),
+ 'src2': randint(0, 1<<16-1)})
+ return data
+
+
+class Test5:
+ def __init__(self, dut, resultfn, data=None, stage_ctl=False):
+ self.dut = dut
+ self.resultfn = resultfn
+ self.stage_ctl = stage_ctl
+ if data:
+ self.data = data
+ else:
+ self.data = []
+ for i in range(num_tests):
+ self.data.append((randint(0, 1<<16-1), randint(0, 1<<16-1)))
+ self.i = 0
+ self.o = 0
+
+ def send(self):
+ while self.o != len(self.data):
+ send_range = randint(0, 3)
+ for j in range(randint(1,10)):
+ if send_range == 0:
+ send = True
+ else:
+ send = randint(0, send_range) != 0
+ #send = True
+ o_p_ready = yield self.dut.p.ready_o
+ if not o_p_ready:
+ yield
+ continue
+ if send and self.i != len(self.data):
+ yield self.dut.p.valid_i.eq(1)
+ for v in self.dut.set_input(self.data[self.i]):
+ yield v
+ self.i += 1
+ else:
+ yield self.dut.p.valid_i.eq(0)
+ yield
+
+ def rcv(self):
+ while self.o != len(self.data):
+ stall_range = randint(0, 3)
+ for j in range(randint(1,10)):
+ ready = randint(0, stall_range) != 0
+ #ready = True
+ yield self.dut.n.ready_i.eq(ready)
+ yield
+ o_n_valid = yield self.dut.n.valid_o
+ i_n_ready = yield self.dut.n.ready_i_test
+ if not o_n_valid or not i_n_ready:
+ continue
+ if isinstance(self.dut.n.data_o, Record):
+ data_o = {}
+ dod = self.dut.n.data_o
+ for k, v in dod.fields.items():
+ data_o[k] = yield v
+ else:
+ data_o = yield self.dut.n.data_o
+ self.resultfn(data_o, self.data[self.o], self.i, self.o)
+ self.o += 1
+ if self.o == len(self.data):
+ break
+
+def resultfn_5(data_o, expected, i, o):
+ res = expected[0] + expected[1]
+ assert data_o == res, \
+ "%d-%d data %x not match %s\n" \
+ % (i, o, data_o, repr(expected))
+
+def tbench4(dut):
+ data = []
+ for i in range(num_tests):
+ #data.append(randint(0, 1<<16-1))
+ data.append(i+1)
+ i = 0
+ o = 0
+ while True:
+ stall = randint(0, 3) != 0
+ send = randint(0, 5) != 0
+ yield dut.n.ready_i.eq(stall)
+ o_p_ready = yield dut.p.ready_o
+ if o_p_ready:
+ if send and i != len(data):
+ yield dut.p.valid_i.eq(1)
+ yield dut.p.data_i.eq(data[i])
+ i += 1
+ else:
+ yield dut.p.valid_i.eq(0)
+ yield
+ o_n_valid = yield dut.n.valid_o
+ i_n_ready = yield dut.n.ready_i_test
+ if o_n_valid and i_n_ready:
+ data_o = yield dut.n.data_o
+ assert data_o == data[o] + 2, "%d-%d data %x not match %x\n" \
+ % (i, o, data_o, data[o])
+ o += 1
+ if o == len(data):
+ break
+
+######################################################################
+# Test 2 and 4
+######################################################################
+
+class ExampleBufPipe2(ControlBase):
+ """ Example of how to do chained pipeline stages.
+ """
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = ExampleBufPipe()
+ pipe2 = ExampleBufPipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Test 9
+######################################################################
+
+class ExampleBufPipeChain2(BufferedHandshake):
+ """ connects two stages together as a *single* combinatorial stage.
+ """
+ def __init__(self):
+ stage1 = ExampleStageCls()
+ stage2 = ExampleStageCls()
+ combined = StageChain([stage1, stage2])
+ BufferedHandshake.__init__(self, combined)
+
+
+def data_chain2():
+ data = []
+ for i in range(num_tests):
+ data.append(randint(0, 1<<16-2))
+ return data
+
+
+def resultfn_9(data_o, expected, i, o):
+ res = expected + 2
+ assert data_o == res, \
+ "%d-%d received data %x not match expected %x\n" \
+ % (i, o, data_o, res)
+
+
+######################################################################
+# Test 6 and 10
+######################################################################
+
+class SetLessThan(Elaboratable):
+ def __init__(self, width, signed):
+ self.m = Module()
+ self.src1 = Signal((width, signed), name="src1")
+ self.src2 = Signal((width, signed), name="src2")
+ self.output = Signal(width, name="out")
+
+ def elaborate(self, platform):
+ self.m.d.comb += self.output.eq(Mux(self.src1 < self.src2, 1, 0))
+ return self.m
+
+
+class LTStage(StageCls):
+ """ module-based stage example
+ """
+ def __init__(self):
+ self.slt = SetLessThan(16, True)
+
+ def ispec(self, name):
+ return (Signal(16, name="%s_sig1" % name),
+ Signal(16, name="%s_sig2" % name))
+
+ def ospec(self, name):
+ return Signal(16, "%s_out" % name)
+
+ def setup(self, m, i):
+ self.o = Signal(16)
+ m.submodules.slt = self.slt
+ m.d.comb += self.slt.src1.eq(i[0])
+ m.d.comb += self.slt.src2.eq(i[1])
+ m.d.comb += self.o.eq(self.slt.output)
+
+ def process(self, i):
+ return self.o
+
+
+class LTStageDerived(SetLessThan, StageCls):
+ """ special version of a nmigen module where the module is also a stage
+
+ shows that you don't actually need to combinatorially connect
+ to the outputs, or add the module as a submodule: just return
+ the module output parameter(s) from the Stage.process() function
+ """
+
+ def __init__(self):
+ SetLessThan.__init__(self, 16, True)
+
+ def ispec(self):
+ return (Signal(16), Signal(16))
+
+ def ospec(self):
+ return Signal(16)
+
+ def setup(self, m, i):
+ m.submodules.slt = self
+ m.d.comb += self.src1.eq(i[0])
+ m.d.comb += self.src2.eq(i[1])
+
+ def process(self, i):
+ return self.output
+
+
+class ExampleLTPipeline(UnbufferedPipeline):
+ """ an example of how to use the unbuffered pipeline.
+ """
+
+ def __init__(self):
+ stage = LTStage()
+ UnbufferedPipeline.__init__(self, stage)
+
+
+class ExampleLTBufferedPipeDerived(BufferedHandshake):
+ """ an example of how to use the buffered pipeline.
+ """
+
+ def __init__(self):
+ stage = LTStageDerived()
+ BufferedHandshake.__init__(self, stage)
+
+
+def resultfn_6(data_o, expected, i, o):
+ res = 1 if expected[0] < expected[1] else 0
+ assert data_o == res, \
+ "%d-%d data %x not match %s\n" \
+ % (i, o, data_o, repr(expected))
+
+
+######################################################################
+# Test 7
+######################################################################
+
+class ExampleAddRecordStage(StageCls):
+ """ example use of a Record
+ """
+
+ record_spec = [('src1', 16), ('src2', 16)]
+ def ispec(self):
+ """ returns a Record using the specification
+ """
+ return Record(self.record_spec)
+
+ def ospec(self):
+ return Record(self.record_spec)
+
+ def process(self, i):
+ """ process the input data, returning a dictionary with key names
+ that exactly match the Record's attributes.
+ """
+ return {'src1': i.src1 + 1,
+ 'src2': i.src2 + 1}
+
+######################################################################
+# Test 11
+######################################################################
+
+class ExampleAddRecordPlaceHolderStage(StageCls):
+ """ example use of a Record, with a placeholder as the processing result
+ """
+
+ record_spec = [('src1', 16), ('src2', 16)]
+ def ispec(self):
+ """ returns a Record using the specification
+ """
+ return Record(self.record_spec)
+
+ def ospec(self):
+ return Record(self.record_spec)
+
+ def process(self, i):
+ """ process the input data, returning a PlaceHolder class instance
+ with attributes that exactly match those of the Record.
+ """
+ o = PlaceHolder()
+ o.src1 = i.src1 + 1
+ o.src2 = i.src2 + 1
+ return o
+
+
+# a dummy class that may have stuff assigned to instances once created
+class PlaceHolder: pass
+
+
+class ExampleAddRecordPipe(UnbufferedPipeline):
+ """ an example of how to use the combinatorial pipeline.
+ """
+
+ def __init__(self):
+ stage = ExampleAddRecordStage()
+ UnbufferedPipeline.__init__(self, stage)
+
+
+def resultfn_7(data_o, expected, i, o):
+ res = (expected['src1'] + 1, expected['src2'] + 1)
+ assert data_o['src1'] == res[0] and data_o['src2'] == res[1], \
+ "%d-%d data %s not match %s\n" \
+ % (i, o, repr(data_o), repr(expected))
+
+
+class ExampleAddRecordPlaceHolderPipe(UnbufferedPipeline):
+ """ an example of how to use the combinatorial pipeline.
+ """
+
+ def __init__(self):
+ stage = ExampleAddRecordPlaceHolderStage()
+ UnbufferedPipeline.__init__(self, stage)
+
+
+def resultfn_test11(data_o, expected, i, o):
+ res1 = expected.src1 + 1
+ res2 = expected.src2 + 1
+ assert data_o['src1'] == res1 and data_o['src2'] == res2, \
+ "%d-%d data %s not match %s\n" \
+ % (i, o, repr(data_o), repr(expected))
+
+
+######################################################################
+# Test 8
+######################################################################
+
+
+class Example2OpClass:
+ """ an example of a class used to store 2 operands.
+ requires an eq function, to conform with the pipeline stage API
+ """
+
+ def __init__(self):
+ self.op1 = Signal(16)
+ self.op2 = Signal(16)
+
+ def eq(self, i):
+ return [self.op1.eq(i.op1), self.op2.eq(i.op2)]
+
+
+class ExampleAddClassStage(StageCls):
+ """ an example of how to use the buffered pipeline, as a class instance
+ """
+
+ def ispec(self):
+ """ returns an instance of an Example2OpClass.
+ """
+ return Example2OpClass()
+
+ def ospec(self):
+ """ returns an output signal which will happen to contain the sum
+ of the two inputs
+ """
+ return Signal(16, name="add2_out")
+
+ def process(self, i):
+ """ process the input data (sums the values in the tuple) and returns it
+ """
+ return i.op1 + i.op2
+
+
+class ExampleBufPipeAddClass(BufferedHandshake):
+ """ an example of how to use the buffered pipeline, using a class instance
+ """
+
+ def __init__(self):
+ addstage = ExampleAddClassStage()
+ BufferedHandshake.__init__(self, addstage)
+
+
+class TestInputAdd:
+ """ the eq function, called by set_input, needs an incoming object
+ that conforms to the Example2OpClass.eq function requirements
+ easiest way to do that is to create a class that has the exact
+ same member layout (self.op1, self.op2) as Example2OpClass
+ """
+ def __init__(self, op1, op2):
+ self.op1 = op1
+ self.op2 = op2
+
+
+def resultfn_8(data_o, expected, i, o):
+ res = expected.op1 + expected.op2 # these are a TestInputAdd instance
+ assert data_o == res, \
+ "%d-%d data %s res %x not match %s\n" \
+ % (i, o, repr(data_o), res, repr(expected))
+
+def data_2op():
+ data = []
+ for i in range(num_tests):
+ data.append(TestInputAdd(randint(0, 1<<16-1), randint(0, 1<<16-1)))
+ return data
+
+
+######################################################################
+# Test 12
+######################################################################
+
+class ExampleStageDelayCls(StageCls, Elaboratable):
+ """ an example of how to use the buffered pipeline, in a static class
+ fashion
+ """
+
+ def __init__(self, valid_trigger=2):
+ self.count = Signal(2)
+ self.valid_trigger = valid_trigger
+
+ def ispec(self):
+ return Signal(16, name="example_input_signal")
+
+ def ospec(self):
+ return Signal(16, name="example_output_signal")
+
+ @property
+ def d_ready(self):
+ """ data is ready to be accepted when this is true
+ """
+ return (self.count == 1)# | (self.count == 3)
+ return Const(1)
+
+ def d_valid(self, ready_i):
+ """ data is valid at output when this is true
+ """
+ return self.count == self.valid_trigger
+ return Const(1)
+
+ def process(self, i):
+ """ process the input data and returns it (adds 1)
+ """
+ return i + 1
+
+ def elaborate(self, platform):
+ m = Module()
+ m.d.sync += self.count.eq(self.count + 1)
+ return m
+
+
+class ExampleBufDelayedPipe(BufferedHandshake):
+
+ def __init__(self):
+ stage = ExampleStageDelayCls(valid_trigger=2)
+ BufferedHandshake.__init__(self, stage, stage_ctl=True)
+
+ def elaborate(self, platform):
+ m = BufferedHandshake.elaborate(self, platform)
+ m.submodules.stage = self.stage
+ return m
+
+
+def data_chain1():
+ data = []
+ for i in range(num_tests):
+ data.append(1<<((i*3)%15))
+ #data.append(randint(0, 1<<16-2))
+ #print (hex(data[-1]))
+ return data
+
+
+def resultfn_12(data_o, expected, i, o):
+ res = expected + 1
+ assert data_o == res, \
+ "%d-%d data %x not match %x\n" \
+ % (i, o, data_o, res)
+
+
+######################################################################
+# Test 13
+######################################################################
+
+class ExampleUnBufDelayedPipe(BufferedHandshake):
+
+ def __init__(self):
+ stage = ExampleStageDelayCls(valid_trigger=3)
+ BufferedHandshake.__init__(self, stage, stage_ctl=True)
+
+ def elaborate(self, platform):
+ m = BufferedHandshake.elaborate(self, platform)
+ m.submodules.stage = self.stage
+ return m
+
+######################################################################
+# Test 15
+######################################################################
+
+class ExampleBufModeAdd1Pipe(SimpleHandshake):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ SimpleHandshake.__init__(self, stage)
+
+
+######################################################################
+# Test 16
+######################################################################
+
+class ExampleBufModeUnBufPipe(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = ExampleBufModeAdd1Pipe()
+ pipe2 = ExampleBufAdd1Pipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+######################################################################
+# Test 17
+######################################################################
+
+class ExampleUnBufAdd1Pipe2(UnbufferedPipeline2):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ UnbufferedPipeline2.__init__(self, stage)
+
+
+######################################################################
+# Test 18
+######################################################################
+
+class PassThroughTest(PassThroughHandshake):
+
+ def iospecfn(self):
+ return Signal(16, "out")
+
+ def __init__(self):
+ stage = PassThroughStage(self.iospecfn)
+ PassThroughHandshake.__init__(self, stage)
+
+def resultfn_identical(data_o, expected, i, o):
+ res = expected
+ assert data_o == res, \
+ "%d-%d data %x not match %x\n" \
+ % (i, o, data_o, res)
+
+
+######################################################################
+# Test 19
+######################################################################
+
+class ExamplePassAdd1Pipe(PassThroughHandshake):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ PassThroughHandshake.__init__(self, stage)
+
+
+class ExampleBufPassThruPipe(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ # XXX currently fails: any other permutation works fine.
+ # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
+ # also fails using UnbufferedPipeline as well
+ pipe1 = ExampleBufModeAdd1Pipe()
+ pipe2 = ExamplePassAdd1Pipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Test 20
+######################################################################
+
+def iospecfn():
+ return Signal(16, name="d_in")
+
+class FIFOTest16(FIFOControl):
+
+ def __init__(self):
+ stage = PassThroughStage(iospecfn)
+ FIFOControl.__init__(self, 2, stage)
+
+
+######################################################################
+# Test 21
+######################################################################
+
+class ExampleFIFOPassThruPipe1(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = FIFOTest16()
+ pipe2 = FIFOTest16()
+ pipe3 = ExamplePassAdd1Pipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+ m.submodules.pipe3 = pipe3
+
+ m.d.comb += self.connect([pipe1, pipe2, pipe3])
+
+ return m
+
+
+######################################################################
+# Test 22
+######################################################################
+
+class Example2OpRecord(RecordObject):
+ def __init__(self):
+ RecordObject.__init__(self)
+ self.op1 = Signal(16)
+ self.op2 = Signal(16)
+
+
+class ExampleAddRecordObjectStage(StageCls):
+
+ def ispec(self):
+ """ returns an instance of an Example2OpRecord.
+ """
+ return Example2OpRecord()
+
+ def ospec(self):
+ """ returns an output signal which will happen to contain the sum
+ of the two inputs
+ """
+ return Signal(16)
+
+ def process(self, i):
+ """ process the input data (sums the values in the tuple) and returns it
+ """
+ return i.op1 + i.op2
+
+
+class ExampleRecordHandshakeAddClass(SimpleHandshake):
+
+ def __init__(self):
+ addstage = ExampleAddRecordObjectStage()
+ SimpleHandshake.__init__(self, stage=addstage)
+
+
+######################################################################
+# Test 23
+######################################################################
+
+def iospecfnrecord():
+ return Example2OpRecord()
+
+class FIFOTestRecordControl(FIFOControl):
+
+ def __init__(self):
+ stage = PassThroughStage(iospecfnrecord)
+ FIFOControl.__init__(self, 2, stage)
+
+
+class ExampleFIFORecordObjectPipe(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = FIFOTestRecordControl()
+ pipe2 = ExampleRecordHandshakeAddClass()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Test 24
+######################################################################
+
+class FIFOTestRecordAddStageControl(FIFOControl):
+
+ def __init__(self):
+ stage = ExampleAddRecordObjectStage()
+ FIFOControl.__init__(self, 2, stage)
+
+
+
+######################################################################
+# Test 25
+######################################################################
+
+class FIFOTestAdd16(FIFOControl):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ FIFOControl.__init__(self, 2, stage)
+
+
+class ExampleFIFOAdd2Pipe(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = FIFOTestAdd16()
+ pipe2 = FIFOTestAdd16()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Test 26
+######################################################################
+
+def iospecfn24():
+ return (Signal(16, name="src1"), Signal(16, name="src2"))
+
+class FIFOTest2x16(FIFOControl):
+
+ def __init__(self):
+ stage = PassThroughStage(iospecfn2)
+ FIFOControl.__init__(self, 2, stage)
+
+
+######################################################################
+# Test 997
+######################################################################
+
+class ExampleBufPassThruPipe2(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ # XXX currently fails: any other permutation works fine.
+ # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
+ # also fails using UnbufferedPipeline as well
+ #pipe1 = ExampleUnBufAdd1Pipe()
+ #pipe2 = ExampleBufAdd1Pipe()
+ pipe1 = ExampleBufAdd1Pipe()
+ pipe2 = ExamplePassAdd1Pipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Test 998
+######################################################################
+
+class ExampleBufPipe3(ControlBase):
+ """ Example of how to do delayed pipeline, where the stage signals
+ whether it is ready.
+ """
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ pipe1 = ExampleBufDelayedPipe()
+ pipe2 = ExampleBufPipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+######################################################################
+# Test 999 - XXX FAILS
+# http://bugs.libre-riscv.org/show_bug.cgi?id=57
+######################################################################
+
+class ExampleBufAdd1Pipe(BufferedHandshake):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ BufferedHandshake.__init__(self, stage)
+
+
+class ExampleUnBufAdd1Pipe(UnbufferedPipeline):
+
+ def __init__(self):
+ stage = ExampleStageCls()
+ UnbufferedPipeline.__init__(self, stage)
+
+
+class ExampleBufUnBufPipe(ControlBase):
+
+ def elaborate(self, platform):
+ m = ControlBase.elaborate(self, platform)
+
+ # XXX currently fails: any other permutation works fine.
+ # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
+ # also fails using UnbufferedPipeline as well
+ #pipe1 = ExampleUnBufAdd1Pipe()
+ #pipe2 = ExampleBufAdd1Pipe()
+ pipe1 = ExampleBufAdd1Pipe()
+ pipe2 = ExampleUnBufAdd1Pipe()
+
+ m.submodules.pipe1 = pipe1
+ m.submodules.pipe2 = pipe2
+
+ m.d.comb += self.connect([pipe1, pipe2])
+
+ return m
+
+
+######################################################################
+# Unit Tests
+######################################################################
+
+num_tests = 10
+
+if __name__ == '__main__':
+ if False:
+ print ("test 1")
+ dut = ExampleBufPipe()
+ run_simulation(dut, tbench(dut), vcd_name="test_bufpipe.vcd")
+
+ print ("test 2")
+ dut = ExampleBufPipe2()
+ run_simulation(dut, tbench2(dut), vcd_name="test_bufpipe2.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpipe2.il", "w") as f:
+ f.write(vl)
+
+
+ print ("test 3")
+ dut = ExampleBufPipe()
+ test = Test3(dut, resultfn_3)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe3.vcd")
+
+ print ("test 3.5")
+ dut = ExamplePipeline()
+ test = Test3(dut, resultfn_3)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_combpipe3.vcd")
+
+ print ("test 4")
+ dut = ExampleBufPipe2()
+ run_simulation(dut, tbench4(dut), vcd_name="test_bufpipe4.vcd")
+
+ print ("test 5")
+ dut = ExampleBufPipeAdd()
+ test = Test5(dut, resultfn_5, stage_ctl=True)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe5.vcd")
+
+ print ("test 6")
+ dut = ExampleLTPipeline()
+ test = Test5(dut, resultfn_6)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltcomb6.vcd")
+
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ list(dut.p.data_i) + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_ltcomb_pipe.il", "w") as f:
+ f.write(vl)
+
+ print ("test 7")
+ dut = ExampleAddRecordPipe()
+ data=data_dict()
+ test = Test5(dut, resultfn_7, data=data)
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o,
+ dut.p.data_i.src1, dut.p.data_i.src2,
+ dut.n.data_o.src1, dut.n.data_o.src2]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_recordcomb_pipe.il", "w") as f:
+ f.write(vl)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
+
+ print ("test 8")
+ dut = ExampleBufPipeAddClass()
+ data=data_2op()
+ test = Test5(dut, resultfn_8, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe8.vcd")
+
+ print ("test 9")
+ dut = ExampleBufPipeChain2()
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpipechain2.il", "w") as f:
+ f.write(vl)
+
+ data = data_chain2()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv],
+ vcd_name="test_bufpipechain2.vcd")
+
+ print ("test 10")
+ dut = ExampleLTBufferedPipeDerived()
+ test = Test5(dut, resultfn_6)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltbufpipe10.vcd")
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_ltbufpipe10.il", "w") as f:
+ f.write(vl)
+
+ print ("test 11")
+ dut = ExampleAddRecordPlaceHolderPipe()
+ data=data_placeholder()
+ test = Test5(dut, resultfn_test11, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
+
+
+ print ("test 12")
+ dut = ExampleBufDelayedPipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_12, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe12.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpipe12.il", "w") as f:
+ f.write(vl)
+
+ print ("test 13")
+ dut = ExampleUnBufDelayedPipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_12, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_unbufpipe13.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_unbufpipe13.il", "w") as f:
+ f.write(vl)
+
+ print ("test 15")
+ dut = ExampleBufModeAdd1Pipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_12, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf15.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufunbuf15.il", "w") as f:
+ f.write(vl)
+
+ print ("test 16")
+ dut = ExampleBufModeUnBufPipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf16.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufunbuf16.il", "w") as f:
+ f.write(vl)
+
+ print ("test 17")
+ dut = ExampleUnBufAdd1Pipe2()
+ data = data_chain1()
+ test = Test5(dut, resultfn_12, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_unbufpipe17.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_unbufpipe17.il", "w") as f:
+ f.write(vl)
+
+ print ("test 18")
+ dut = PassThroughTest()
+ data = data_chain1()
+ test = Test5(dut, resultfn_identical, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_passthru18.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_passthru18.il", "w") as f:
+ f.write(vl)
+
+ print ("test 19")
+ dut = ExampleBufPassThruPipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpass19.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpass19.il", "w") as f:
+ f.write(vl)
+
+ print ("test 20")
+ dut = FIFOTest16()
+ data = data_chain1()
+ test = Test5(dut, resultfn_identical, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_fifo20.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_fifo20.il", "w") as f:
+ f.write(vl)
+
+ print ("test 21")
+ dut = ExampleFIFOPassThruPipe1()
+ data = data_chain1()
+ test = Test5(dut, resultfn_12, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_fifopass21.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_fifopass21.il", "w") as f:
+ f.write(vl)
+
+ print ("test 22")
+ dut = ExampleRecordHandshakeAddClass()
+ data=data_2op()
+ test = Test5(dut, resultfn_8, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord22.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i.op1, dut.p.data_i.op2] + \
+ [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_addrecord22.il", "w") as f:
+ f.write(vl)
+
+ print ("test 23")
+ dut = ExampleFIFORecordObjectPipe()
+ data=data_2op()
+ test = Test5(dut, resultfn_8, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord23.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i.op1, dut.p.data_i.op2] + \
+ [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_addrecord23.il", "w") as f:
+ f.write(vl)
+
+ print ("test 24")
+ dut = FIFOTestRecordAddStageControl()
+ data=data_2op()
+ test = Test5(dut, resultfn_8, data=data)
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i.op1, dut.p.data_i.op2] + \
+ [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_addrecord24.il", "w") as f:
+ f.write(vl)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord24.vcd")
+
+ print ("test 25")
+ dut = ExampleFIFOAdd2Pipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_add2pipe25.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_add2pipe25.il", "w") as f:
+ f.write(vl)
+
+ print ("test 997")
+ dut = ExampleBufPassThruPipe2()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpass997.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpass997.il", "w") as f:
+ f.write(vl)
+
+ print ("test 998 (fails, bug)")
+ dut = ExampleBufPipe3()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe14.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufpipe14.il", "w") as f:
+ f.write(vl)
+
+ print ("test 999 (expected to fail, which is a bug)")
+ dut = ExampleBufUnBufPipe()
+ data = data_chain1()
+ test = Test5(dut, resultfn_9, data=data)
+ run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufunbuf999.vcd")
+ ports = [dut.p.valid_i, dut.n.ready_i,
+ dut.n.valid_o, dut.p.ready_o] + \
+ [dut.p.data_i] + [dut.n.data_o]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufunbuf999.il", "w") as f:
+ f.write(vl)
+