1 """ Combinatorial Multi-input multiplexer block conforming to Pipeline API
5 from nmigen
import Signal
, Cat
, Const
, Mux
, Module
, Array
6 from nmigen
.cli
import verilog
, rtlil
7 from nmigen
.lib
.coding
import PriorityEncoder
8 from nmigen
.hdl
.rec
import Record
, Layout
10 from collections
.abc
import Sequence
12 from example_buf_pipe
import eq
, NextControl
, PrevControl
, ExampleStage
16 """ Common functions for Pipeline API
18 def __init__(self
, stage
, in_multi
=None, p_len
=1):
19 """ pass in a "stage" which may be either a static class or a class
20 instance, which has four functions (one optional):
21 * ispec: returns input signals according to the input specification
22 * ispec: returns output signals to the output specification
23 * process: takes an input instance and returns processed data
24 * setup: performs any module linkage if the stage uses one.
27 * add i_data member to PrevControl and
28 * add o_data member to NextControl
32 # set up input and output IO ACK (prev/next ready/valid)
34 for i
in range(p_len
):
35 p
.append(PrevControl(in_multi
))
37 self
.n
= NextControl()
39 def connect_to_next(self
, nxt
, p_idx
=0):
40 """ helper function to connect to the next stage data/valid/ready.
42 return self
.n
.connect_to_next(nxt
.p
[p_idx
])
44 def connect_in(self
, prev
, idx
=0, prev_idx
=None):
45 """ helper function to connect stage to an input source. do not
46 use to connect stage-to-stage!
49 return self
.p
[idx
].connect_in(prev
.p
)
50 return self
.p
[idx
].connect_in(prev
.p
[prev_idx
])
52 def connect_out(self
, nxt
):
53 """ helper function to connect stage to an output source. do not
54 use to connect stage-to-stage!
57 return self
.n
.connect_out(nxt
.n
)
58 return self
.n
.connect_out(nxt
.n
)
60 def set_input(self
, i
, idx
=0):
61 """ helper function to set the input data
63 return eq(self
.p
[idx
].i_data
, i
)
67 for i
in range(len(self
.p
)):
68 res
+= [self
.p
[i
].i_valid
, self
.p
[i
].o_ready
,
69 self
.p
[i
].i_data
]# XXX need flattening!]
70 res
+= [self
.n
.i_ready
, self
.n
.o_valid
,
71 self
.n
.o_data
] # XXX need flattening!]
76 class CombMultiInPipeline(PipelineBase
):
77 """ A multi-input Combinatorial block conforming to the Pipeline API
81 p.i_data : StageInput, shaped according to ispec
83 p.o_data : StageOutput, shaped according to ospec
85 r_data : input_shape according to ispec
86 A temporary (buffered) copy of a prior (valid) input.
87 This is HELD if the output is not ready. It is updated
91 def __init__(self
, stage
, p_len
, p_mux
):
92 PipelineBase
.__init
__(self
, stage
, p_len
=p_len
)
95 # set up the input and output data
96 for i
in range(p_len
):
97 self
.p
[i
].i_data
= stage
.ispec() # input type
98 self
.n
.o_data
= stage
.ospec()
100 def elaborate(self
, platform
):
103 m
.submodules
+= self
.p_mux
105 # need an array of buffer registers conforming to *input* spec
111 for i
in range(p_len
):
112 r
= self
.stage
.ispec() # input type
114 data_valid
.append(Signal(name
="data_valid", reset_less
=True))
115 p_i_valid
.append(Signal(name
="p_i_valid", reset_less
=True))
116 n_i_readyn
.append(Signal(name
="n_i_readyn", reset_less
=True))
117 if hasattr(self
.stage
, "setup"):
118 self
.stage
.setup(m
, r
)
120 r_data
= Array(r_data
)
121 p_i_valid
= Array(p_i_valid
)
122 n_i_readyn
= Array(n_i_readyn
)
123 data_valid
= Array(data_valid
)
125 mid
= self
.p_mux
.m_id
126 for i
in range(p_len
):
127 m
.d
.comb
+= data_valid
[i
].eq(0)
128 m
.d
.comb
+= n_i_readyn
[i
].eq(1)
129 m
.d
.comb
+= p_i_valid
[i
].eq(0)
130 m
.d
.comb
+= self
.p
[i
].o_ready
.eq(0)
131 m
.d
.comb
+= p_i_valid
[mid
].eq(self
.p_mux
.active
)
132 m
.d
.comb
+= self
.p
[mid
].o_ready
.eq(~data_valid
[mid
] | self
.n
.i_ready
)
133 m
.d
.comb
+= n_i_readyn
[mid
].eq(~self
.n
.i_ready
& data_valid
[mid
])
134 anyvalid
= Signal(i
, reset_less
=True)
136 for i
in range(p_len
):
137 av
.append(data_valid
[i
])
139 m
.d
.comb
+= self
.n
.o_valid
.eq(anyvalid
.bool())
140 m
.d
.comb
+= data_valid
[mid
].eq(p_i_valid
[mid
] | \
141 (n_i_readyn
[mid
] & data_valid
[mid
]))
143 for i
in range(p_len
):
144 vr
= Signal(reset_less
=True)
145 m
.d
.comb
+= vr
.eq(self
.p
[i
].i_valid
& self
.p
[i
].o_ready
)
147 m
.d
.comb
+= eq(r_data
[i
], self
.p
[i
].i_data
)
149 m
.d
.comb
+= eq(self
.n
.o_data
, self
.stage
.process(r_data
[mid
]))
154 class InputPriorityArbiter
:
155 def __init__(self
, pipe
, num_rows
):
157 self
.num_rows
= num_rows
158 self
.mmax
= int(log(self
.num_rows
) / log(2))
159 self
.m_id
= Signal(self
.mmax
, reset_less
=True) # multiplex id
160 self
.active
= Signal(reset_less
=True)
162 def elaborate(self
, platform
):
165 assert len(self
.pipe
.p
) == self
.num_rows
, \
166 "must declare input to be same size"
167 pe
= PriorityEncoder(self
.num_rows
)
168 m
.submodules
.selector
= pe
170 # connect priority encoder
172 for i
in range(self
.num_rows
):
173 p_i_valid
= Signal(reset_less
=True)
174 m
.d
.comb
+= p_i_valid
.eq(self
.pipe
.p
[i
].i_valid_logic())
175 in_ready
.append(p_i_valid
)
176 m
.d
.comb
+= pe
.i
.eq(Cat(*in_ready
)) # array of input "valids"
177 m
.d
.comb
+= self
.active
.eq(~pe
.n
) # encoder active (one input valid)
178 m
.d
.comb
+= self
.m_id
.eq(pe
.o
) # output one active input
183 return [self
.m_id
, self
.active
]
187 class ExamplePipeline(CombMultiInPipeline
):
188 """ an example of how to use the combinatorial pipeline.
191 def __init__(self
, p_len
=2):
192 p_mux
= InputPriorityArbiter(self
, p_len
)
193 CombMultiInPipeline
.__init
__(self
, ExampleStage
, p_len
, p_mux
)
196 if __name__
== '__main__':
198 dut
= ExamplePipeline()
199 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
200 with
open("test_combpipe.il", "w") as f
: