From 52d9f0f994d23271e31ac808548e638b0f3a0323 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Thu, 28 Mar 2019 03:39:57 +0000 Subject: [PATCH] add unit test for multi-in multi-out FPADDBasePipe --- src/add/nmigen_add_experiment.py | 29 ++++++-- src/add/singlepipe.py | 8 ++- src/add/test_buf_pipe.py | 3 + src/add/test_fpadd_pipe.py | 116 +++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 src/add/test_fpadd_pipe.py diff --git a/src/add/nmigen_add_experiment.py b/src/add/nmigen_add_experiment.py index 23f2386c..92588762 100644 --- a/src/add/nmigen_add_experiment.py +++ b/src/add/nmigen_add_experiment.py @@ -1846,6 +1846,9 @@ class FPADDStageIn: 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): @@ -1855,6 +1858,9 @@ class FPADDStageOut: 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 @@ -1878,14 +1884,23 @@ class FPAddBaseStage: 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 @@ -1902,7 +1917,7 @@ class FPAddInPassThruStage: 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 @@ -1911,6 +1926,8 @@ class FPADDInMuxPipe(PriorityCombPipeline): 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 = [] @@ -1938,7 +1955,7 @@ class FPAddOutPassThruStage: 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 @@ -1947,6 +1964,8 @@ class FPADDMuxOutPipe(MuxCombPipeline): 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] + \ @@ -1957,8 +1976,6 @@ class FPADDMuxOutPipe(MuxCombPipeline): return res - - class FPADDMuxInOut: """ Reservation-Station version of FPADD pipeline. diff --git a/src/add/singlepipe.py b/src/add/singlepipe.py index 493fa553..8ae9b806 100644 --- a/src/add/singlepipe.py +++ b/src/add/singlepipe.py @@ -207,6 +207,7 @@ class NextControl: 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), @@ -251,7 +252,7 @@ def eq(o, i): 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): @@ -397,6 +398,7 @@ class ControlBase: 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): @@ -451,12 +453,12 @@ class ControlBase: # 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 diff --git a/src/add/test_buf_pipe.py b/src/add/test_buf_pipe.py index e483e62f..f4764974 100644 --- a/src/add/test_buf_pipe.py +++ b/src/add/test_buf_pipe.py @@ -291,6 +291,9 @@ class ExampleBufPipe2(ControlBase): 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 diff --git a/src/add/test_fpadd_pipe.py b/src/add/test_fpadd_pipe.py new file mode 100644 index 00000000..577117d3 --- /dev/null +++ b/src/add/test_fpadd_pipe.py @@ -0,0 +1,116 @@ +""" 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") + -- 2.30.2