def eq(self, i):
return [self.a.eq(i.a), self.b.eq(i.b), self.mid.eq(i.mid)]
+ def ports(self):
+ return [self.a, self.b, self.mid]
+
class FPADDStageOut:
def __init__(self, width, id_wid):
def eq(self, i):
return [self.z.eq(i.z), self.mid.eq(i.mid)]
+ def ports(self):
+ return [self.z, self.mid]
+
# matches the format of FPADDStageOut, allows eq function to do assignments
class PlaceHolder: pass
return o
+class FPADDBasePipe1(UnbufferedPipeline):
+ def __init__(self, width, id_wid):
+ stage = FPAddBaseStage(width, id_wid)
+ UnbufferedPipeline.__init__(self, stage)
+
+
class FPADDBasePipe(ControlBase):
def __init__(self, width, id_wid):
ControlBase.__init__(self)
+ self.pipe1 = FPADDBasePipe1(width, id_wid)
+ self.p.i_data = self.pipe1.stage.ispec()
+ self.n.o_data = self.pipe1.stage.ospec()
def elaborate(self, platform):
m = Module()
- stage1 = FPAddBaseStage(width, id_wid)
- m.d.comb += self.connect([stage1])
+ m.submodules.pipe1 = self.pipe1
+ self.connect(m, [self.pipe1])
return m
def __init__(self, width, id_wid):
self.width, self.id_wid = width, id_wid
def ispec(self): return FPADDStageIn(self.width, self.id_wid)
- def ospec(self): return self.ospec()
+ def ospec(self): return self.ispec()
def process(self, i): return i
self.num_rows = num_rows
stage = FPAddInPassThruStage(width, id_width)
PriorityCombPipeline.__init__(self, stage, p_len=self.num_rows)
+ #self.p.i_data = stage.ispec()
+ #self.n.o_data = stage.ospec()
def ports(self):
res = []
def __init__(self, width, id_wid):
self.width, self.id_wid = width, id_wid
def ispec(self): return FPADDStageOut(self.width, self.id_wid)
- def ospec(self): return self.ospec()
+ def ospec(self): return self.ispec()
def process(self, i): return i
self.num_rows = num_rows
stage = FPAddOutPassThruStage(width, id_wid)
MuxCombPipeline.__init__(self, stage, n_len=self.num_rows)
+ #self.p.i_data = stage.ispec()
+ #self.n.o_data = stage.ospec()
def ports(self):
res = [self.p.i_valid, self.p.o_ready] + \
return res
-
-
class FPADDMuxInOut:
""" Reservation-Station version of FPADD pipeline.
data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
use this when connecting stage-to-stage
"""
+ print ("connect next", self, nxt)
return [nxt.i_valid.eq(self.o_valid),
self.i_ready.eq(nxt.o_ready),
eq(nxt.i_data, self.o_data),
o, i = [o], [i]
res = []
for (ao, ai) in zip(o, i):
- #print ("eq", ao, ai)
+ print ("eq", ao, ai)
if isinstance(ao, Record):
for idx, (field_name, field_shape, _) in enumerate(ao.layout):
if isinstance(field_shape, Layout):
def connect_to_next(self, nxt):
""" helper function to connect to the next stage data/valid/ready.
"""
+ print ("ControlBase connect next", self, nxt)
return self.n.connect_to_next(nxt.p)
def _connect_in(self, prev):
# connect front of chain to ourselves
front = pipechain[0]
- self.p.i_data = front.stage.ispec()
+ #self.p.i_data = front.stage.ispec()
eqs += front._connect_in(self)
# connect end of chain to ourselves
end = pipechain[-1]
- self.n.o_data = end.stage.ospec()
+ #self.n.o_data = end.stage.ospec()
eqs += end._connect_out(self)
# activate the assignments
pipe1 = ExampleBufPipe()
pipe2 = ExampleBufPipe()
+ self.p.i_data = pipe1.stage.ispec()
+ self.n.o_data = pipe2.stage.ospec()
+
m.submodules.pipe1 = pipe1
m.submodules.pipe2 = pipe2
--- /dev/null
+""" key strategic example showing how to do multi-input fan-in into a
+ multi-stage pipeline, then multi-output fanout.
+
+ the multiplex ID from the fan-in is passed in to the pipeline, preserved,
+ and used as a routing ID on the fanout.
+"""
+
+from random import randint
+from math import log
+from nmigen import Module, Signal, Cat, Value
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+
+from nmigen_add_experiment import (FPADDMuxInOut,)
+
+
+class InputTest:
+ def __init__(self, dut):
+ self.dut = dut
+ self.di = {}
+ self.do = {}
+ self.tlen = 4
+ for mid in range(dut.num_rows):
+ self.di[mid] = {}
+ self.do[mid] = []
+ for i in range(self.tlen):
+ op1 = randint(0, 255)
+ op2 = randint(0, 255)
+ self.di[mid][i] = (op1, op2)
+ self.do[mid].append(op1 + op2)
+
+ def send(self, mid):
+ for i in range(self.tlen):
+ op1, op2 = self.di[mid][i]
+ rs = dut.p[mid]
+ yield rs.i_valid.eq(1)
+ yield rs.i_data.a.eq(op1)
+ yield rs.i_data.b.eq(op2)
+ yield rs.i_data.mid.eq(mid)
+ yield
+ o_p_ready = yield rs.o_ready
+ while not o_p_ready:
+ yield
+ o_p_ready = yield rs.o_ready
+
+ print ("send", mid, i, op1, op2, op1+op2)
+
+ yield rs.i_valid.eq(0)
+ # wait random period of time before queueing another value
+ for i in range(randint(0, 3)):
+ yield
+
+ yield rs.i_valid.eq(0)
+ yield
+
+ print ("send ended", mid)
+
+ ## wait random period of time before queueing another value
+ #for i in range(randint(0, 3)):
+ # yield
+
+ #send_range = randint(0, 3)
+ #if send_range == 0:
+ # send = True
+ #else:
+ # send = randint(0, send_range) != 0
+
+ def rcv(self, mid):
+ while True:
+ #stall_range = randint(0, 3)
+ #for j in range(randint(1,10)):
+ # stall = randint(0, stall_range) != 0
+ # yield self.dut.n[0].i_ready.eq(stall)
+ # yield
+ n = self.dut.n[mid]
+ yield n.i_ready.eq(1)
+ yield
+ o_n_valid = yield n.o_valid
+ i_n_ready = yield n.i_ready
+ if not o_n_valid or not i_n_ready:
+ continue
+
+ out_mid = yield n.o_data.mid
+ out_z = yield n.o_data.z
+
+ print ("recv", out_mid, out_z)
+
+ out_i = 0
+
+ # see if this output has occurred already, delete it if it has
+ assert mid == out_mid, "out_mid %d not correct %d" % (out_mid, mid)
+ assert self.do[mid][out_i] == out_z # pass-through data
+ del self.do[mid][out_i]
+
+ # check if there's any more outputs
+ if len(self.do[mid]) == 0:
+ break
+ print ("recv ended", mid)
+
+
+
+if __name__ == '__main__':
+ dut = FPADDMuxInOut(16, 2, 4)
+ vl = rtlil.convert(dut, ports=dut.ports())
+ with open("test_fpadd_pipe.il", "w") as f:
+ f.write(vl)
+ #run_simulation(dut, testbench(dut), vcd_name="test_inputgroup.vcd")
+
+ test = InputTest(dut)
+ run_simulation(dut, [test.rcv(1), test.rcv(0),
+ test.rcv(3), test.rcv(2),
+ test.send(0), test.send(1),
+ test.send(3), test.send(2),
+ ],
+ vcd_name="test_inoutmux_pipe.vcd")
+