2 Copyright (c) 2013, IIT Madras
5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
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.
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 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
14 // Copyright (c) 2013-2017 Bluespec, Inc. All Rights Reserved
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.
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.
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.
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, ...
53 // You can also connect each to a FIFOF:
54 // mkConnection (m1.txe, fifof)
55 // mkConnection (fifof, m2.rxe)
57 // ================================================================
58 // BSV library imports
62 import Connectable :: *;
64 // ================================================================
67 // This interface is used by the sender
69 interface TXu #(type t);
71 method Action enq (t x);
74 instance ToPut #(TXu #(t), t);
75 function Put #(t) toPut (TXu #(t) f_in);
77 method Action put (t x);
84 // This interface is exported by the sender
86 (* always_enabled, always_ready *)
87 interface TXe #(type t);
88 method Action notFull (Bool b);
89 method Action enq_rdy (Bool b);
94 // ----------------------------------------------------------------
95 // Connecting a TXe to an ordinary FIFOF
97 instance Connectable #(FIFOF #(t), TXe #(t));
98 module mkConnection #(FIFOF #(t) fifo, TXe #(t) txe)
100 (* fire_when_enabled, no_implicit_conditions *)
101 rule connect_notFull;
102 txe.notFull (fifo.notFull);
105 (* fire_when_enabled, no_implicit_conditions *)
107 txe.enq_rdy (fifo.notFull);
110 rule connect_ena_data (txe.enq_ena);
111 fifo.enq (txe.enq_data);
116 instance Connectable #(TXe #(t), FIFOF #(t));
117 module mkConnection #(TXe #(t) txe, FIFOF #(t) fifo)
119 mkConnection (fifo, txe);
123 // ----------------------------------------------------------------
124 // Transactor from TXu to TXe interface
126 interface TX #(type t);
127 interface TXu #(t) u;
128 interface TXe #(t) e;
131 module mkTX (TX #(t))
132 provisos (Bits #(t, tsz));
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 (?);
144 method Action enq (t x) if (w_rdy);
151 method Action notFull (Bool b);
155 method Action enq_rdy (Bool b);
169 // ================================================================
170 // RX (receiver side)
172 // This interface is used by the receiver
174 interface RXu #(type t);
175 method Bool notEmpty;
180 instance ToGet #(RXu #(t), t);
181 function Get #(t) toGet (RXu #(t) f_out);
182 return interface Get;
183 method ActionValue #(t) get;
191 // This interface is exported by the receiver
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);
201 // ----------------------------------------------------------------
202 // Connecting an ordinary FIFOF to an RXe
204 instance Connectable #(FIFOF #(t), RXe #(t));
205 module mkConnection #(FIFOF #(t) fifo, RXe #(t) rxe)
207 (* fire_when_enabled, no_implicit_conditions *)
208 rule connect_notEmpty;
209 rxe.notEmpty (fifo.notEmpty);
212 (* fire_when_enabled, no_implicit_conditions *)
214 rxe.first_deq_rdy (fifo.notEmpty);
218 let data = (fifo.notEmpty ? fifo.first : ?);
222 rule connect_ena (rxe.deq_ena);
228 instance Connectable #(RXe #(t), FIFOF #(t));
229 module mkConnection #(RXe #(t) rxe, FIFOF #(t) fifo)
231 mkConnection (fifo, rxe);
235 // ----------------------------------------------------------------
236 // Transactor from RXe to RXu interface
238 interface RX #(type t);
239 interface RXu #(t) u;
240 interface RXe #(t) e;
243 module mkRX (RX #(t))
244 provisos (Bits #(t, tsz));
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 (?);
252 method Bool notEmpty;
256 method t first if (w_rdy);
260 method Action deq () if (w_rdy);
266 method Action notEmpty (Bool b);
270 method Action first_deq_rdy (Bool b);
274 method Action first (t x);
284 // ================================================================
285 // Function to connect TXe to RXe, passing in the
286 // desired FIFOF constructor for the intermediate buffer
288 module mkChan #(module #(FIFOF #(t)) mkFIFOF,
293 let fifof <- mkFIFOF;
294 let empty_txe_to_fifof <- mkConnection (txe, fifof);
295 let empty_fifof_to_rxe <- mkConnection (fifof, rxe);
298 // ================================================================