add ClintBase
[shakti-core.git] / src / lib / TxRx.bsv
1 /*
2 Copyright (c) 2013, IIT Madras
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 * Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
13 */
14 // Copyright (c) 2013-2017 Bluespec, Inc. All Rights Reserved
15
16 package TxRx;
17
18 // ================================================================
19 // This package allows a separately synthesized module to have direct
20 // access to an external communication channel via FIFOF-like interfaces
21 // (methods: notFull, enq, notEmpty, first, deq),
22 // without the overhead or latency of an internal FIFOs.
23
24 // THE PROBLEM: suppose we want module M1 to send messages to M2
25 // Inside M1, we can instantiate a FIFO f1, and enqueue into it,
26 // and return g1 = toGet (f1) as part of M1's interface.
27 // Inside M2, we can instantiate a FIFO f2, and dequeue from it,
28 // and return p2 = toPut (f2) as part of M2's interface.
29 // Outside, we connect them with mkConnection(g1,p2).
30 // Problem 1: there are 2 ticks of latency in the communication
31 // Problem 2: there are at least 2 state elements to hold messages
32 // Ideally, we'd like to instantiate a FIFO externally,
33 // and pass it as a parameter to M1 and M2
34 // so that M1 can enq to it, and M2 can dequeue from it.
35 // But then M1 and M2 cannot be separately synthesized.
36
37 // This package provides a solution.
38 // Terminology: the channel has a tail (enq side) and a head (deq side).
39 // Inside M1 (for outgoing data):
40 // Instantiate: TX #(t) tx <- mkTX;
41 // Use tx.u to send data (methods enq, notFull).
42 // Export tx.e as part of M1's interface.
43 // Inside M2 (for incoming data):
44 // Instantiate: RX #(t) rx <- mkRX;
45 // Use rx.u to receive data (methods first, deq, notEmpty).
46 // Export rx.e as part of M2's interface.
47 //
48 // Outside, use 'mkChan (buffer_fifof, m1.txe, m2.rxe)' to make an external
49 // communication channel, passing in a module with a FIFOF
50 // interface to instantiate the intermediate buffer.
51 // The buffer could be mkFIFOF, mkPipelineFIFOF, mkSizedFIFOF, ...
52 //
53 // You can also connect each to a FIFOF:
54 // mkConnection (m1.txe, fifof)
55 // mkConnection (fifof, m2.rxe)
56
57 // ================================================================
58 // BSV library imports
59
60 import FIFOF :: *;
61 import GetPut :: *;
62 import Connectable :: *;
63
64 // ================================================================
65 // TX (sender side)
66
67 // This interface is used by the sender
68
69 interface TXu #(type t);
70 method Bool notFull;
71 method Action enq (t x);
72 endinterface
73
74 instance ToPut #(TXu #(t), t);
75 function Put #(t) toPut (TXu #(t) f_in);
76 return interface Put;
77 method Action put (t x);
78 f_in.enq (x);
79 endmethod
80 endinterface;
81 endfunction
82 endinstance
83
84 // This interface is exported by the sender
85
86 (* always_enabled, always_ready *)
87 interface TXe #(type t);
88 method Action notFull (Bool b);
89 method Action enq_rdy (Bool b);
90 method Bool enq_ena;
91 method t enq_data;
92 endinterface
93
94 // ----------------------------------------------------------------
95 // Connecting a TXe to an ordinary FIFOF
96
97 instance Connectable #(FIFOF #(t), TXe #(t));
98 module mkConnection #(FIFOF #(t) fifo, TXe #(t) txe)
99 (Empty);
100 (* fire_when_enabled, no_implicit_conditions *)
101 rule connect_notFull;
102 txe.notFull (fifo.notFull);
103 endrule
104
105 (* fire_when_enabled, no_implicit_conditions *)
106 rule connect_rdy;
107 txe.enq_rdy (fifo.notFull);
108 endrule
109
110 rule connect_ena_data (txe.enq_ena);
111 fifo.enq (txe.enq_data);
112 endrule
113 endmodule
114 endinstance
115
116 instance Connectable #(TXe #(t), FIFOF #(t));
117 module mkConnection #(TXe #(t) txe, FIFOF #(t) fifo)
118 (Empty);
119 mkConnection (fifo, txe);
120 endmodule
121 endinstance
122
123 // ----------------------------------------------------------------
124 // Transactor from TXu to TXe interface
125
126 interface TX #(type t);
127 interface TXu #(t) u;
128 interface TXe #(t) e;
129 endinterface
130
131 module mkTX (TX #(t))
132 provisos (Bits #(t, tsz));
133
134 Wire #(Bool) w_notFull <- mkBypassWire;
135 Wire #(Bool) w_rdy <- mkBypassWire;
136 Wire #(Bool) w_ena <- mkDWire (False);
137 Wire #(t) w_data <- mkDWire (?);
138
139 interface TXu u;
140 method Bool notFull;
141 return w_notFull;
142 endmethod
143
144 method Action enq (t x) if (w_rdy);
145 w_ena <= True;
146 w_data <= x;
147 endmethod
148 endinterface
149
150 interface TXe e;
151 method Action notFull (Bool b);
152 w_notFull <= b;
153 endmethod
154
155 method Action enq_rdy (Bool b);
156 w_rdy <= b;
157 endmethod
158
159 method Bool enq_ena;
160 return w_ena;
161 endmethod
162
163 method t enq_data;
164 return w_data;
165 endmethod
166 endinterface
167 endmodule: mkTX
168
169 // ================================================================
170 // RX (receiver side)
171
172 // This interface is used by the receiver
173
174 interface RXu #(type t);
175 method Bool notEmpty;
176 method t first;
177 method Action deq;
178 endinterface
179
180 instance ToGet #(RXu #(t), t);
181 function Get #(t) toGet (RXu #(t) f_out);
182 return interface Get;
183 method ActionValue #(t) get;
184 f_out.deq;
185 return f_out.first;
186 endmethod
187 endinterface;
188 endfunction
189 endinstance
190
191 // This interface is exported by the receiver
192
193 (* always_enabled, always_ready *)
194 interface RXe #(type t);
195 method Action notEmpty (Bool b);
196 method Action first_deq_rdy (Bool b);
197 method Action first (t x);
198 method Bool deq_ena;
199 endinterface
200
201 // ----------------------------------------------------------------
202 // Connecting an ordinary FIFOF to an RXe
203
204 instance Connectable #(FIFOF #(t), RXe #(t));
205 module mkConnection #(FIFOF #(t) fifo, RXe #(t) rxe)
206 (Empty);
207 (* fire_when_enabled, no_implicit_conditions *)
208 rule connect_notEmpty;
209 rxe.notEmpty (fifo.notEmpty);
210 endrule
211
212 (* fire_when_enabled, no_implicit_conditions *)
213 rule connect_rdy;
214 rxe.first_deq_rdy (fifo.notEmpty);
215 endrule
216
217 rule connect_first;
218 let data = (fifo.notEmpty ? fifo.first : ?);
219 rxe.first (data);
220 endrule
221
222 rule connect_ena (rxe.deq_ena);
223 fifo.deq;
224 endrule
225 endmodule
226 endinstance
227
228 instance Connectable #(RXe #(t), FIFOF #(t));
229 module mkConnection #(RXe #(t) rxe, FIFOF #(t) fifo)
230 (Empty);
231 mkConnection (fifo, rxe);
232 endmodule
233 endinstance
234
235 // ----------------------------------------------------------------
236 // Transactor from RXe to RXu interface
237
238 interface RX #(type t);
239 interface RXu #(t) u;
240 interface RXe #(t) e;
241 endinterface
242
243 module mkRX (RX #(t))
244 provisos (Bits #(t, tsz));
245
246 Wire #(Bool) w_notEmpty <- mkBypassWire;
247 Wire #(Bool) w_rdy <- mkBypassWire;
248 Wire #(Bool) w_ena <- mkDWire (False);
249 Wire #(t) w_data <- mkDWire (?);
250
251 interface RXu u;
252 method Bool notEmpty;
253 return w_notEmpty;
254 endmethod
255
256 method t first if (w_rdy);
257 return w_data;
258 endmethod
259
260 method Action deq () if (w_rdy);
261 w_ena <= True;
262 endmethod
263 endinterface
264
265 interface RXe e;
266 method Action notEmpty (Bool b);
267 w_notEmpty <= b;
268 endmethod
269
270 method Action first_deq_rdy (Bool b);
271 w_rdy <= b;
272 endmethod
273
274 method Action first (t x);
275 w_data <= x;
276 endmethod
277
278 method Bool deq_ena;
279 return w_ena;
280 endmethod
281 endinterface
282 endmodule: mkRX
283
284 // ================================================================
285 // Function to connect TXe to RXe, passing in the
286 // desired FIFOF constructor for the intermediate buffer
287
288 module mkChan #(module #(FIFOF #(t)) mkFIFOF,
289 TXe #(t) txe,
290 RXe #(t) rxe)
291 (Empty);
292
293 let fifof <- mkFIFOF;
294 let empty_txe_to_fifof <- mkConnection (txe, fifof);
295 let empty_fifof_to_rxe <- mkConnection (fifof, rxe);
296 endmodule: mkChan
297
298 // ================================================================
299
300 endpackage