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 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
16 // ================================================================
17 // Copyright (c) Bluespec, Inc., 2007-2011 All Rights Reserved
25 import DefaultValue ::*;
27 `include "ARM.defines"
29 // ================================================================
30 // DMA requests and responses
35 typedef enum { NOP, WR, RD } ReqOp
38 instance FShow #(ReqOp);
39 function Fmt fshow (ReqOp op);
41 NOP : return fshow ("NOP");
42 WR : return fshow ("WR");
43 RD : return fshow ("RD");
48 typedef Bit#(10) ReqInfo;
49 typedef Bit#(32) ReqAddr;
50 typedef Bit#(32) ReqData;
60 instance FShow #(Socket_Req);
61 function Fmt fshow (Socket_Req req);
62 return (fshow ("Socket_Req{")
66 ( (req.reqOp() != NOP)
67 ? $format (", %h, %h, %h}", req.reqInfo(), req.reqAddr(), req.reqData())
74 // ================================================================
77 typedef enum { NOP, OK } RespOp
80 instance FShow #(RespOp);
81 function Fmt fshow (RespOp op);
83 NOP : return fshow ("NOP");
84 OK : return fshow ("OK");
89 typedef Bit#(10) RespInfo;
90 typedef Bit#(1) RespAddr;
91 typedef Bit#(32) RespData;
102 instance FShow #(Socket_Resp);
103 function Fmt fshow (Socket_Resp resp);
104 return (fshow ("Socket_Resp{")
106 fshow (resp.respOp())
108 ( (resp.respOp() != NOP)
109 ? $format (", %h, %h, %h", resp.respInfo(), resp.respAddr(), resp.respData())
116 // ----------------------------------------------------------------
117 // At times it is best to consider registers as completely homogeneous,
118 // so that they can be accessed as a bit pattern with no internal
119 // structure. These functions convert reg interfaces based on a
120 // structured type to reg interfaces based on a bit pattern of at
121 // least the same width.
123 function Reg#(Bit#(n)) regAToRegBitN( Reg#(a_type) rin )
124 provisos ( Bits#( a_type, asize),
125 Add#(asize,xxx,n) ) ;
129 method Bit#(n) _read ();
130 a_type tmp = rin._read() ;
131 return zeroExtend (pack( tmp )) ;
133 method Action _write( Bit#(n) din );
134 rin._write( unpack( truncate(din) )) ;
150 // ================================================================
151 // The DMA interface has two sub-interfaces
152 // A TLM Receive interface (slave) for config
153 // A TLM Send interface (master) for data transfers
155 interface DmaC #(numeric type numChannels);
156 interface AXI4_Lite_Master_IFC#(`PADDR, `Reg_width, 0) cfg;
157 interface TLMRecvIFC#(`ARM_RR) cfg;
158 interface TLMSendIFC#(`ARM_RR) mmu;
162 typedef UInt#(16) DMACounts ;
163 // The number of channels is a parameter.
164 // typedef 2 NumChannels;
166 // For the module, we add additional configuration registers to control
167 // which port the transfer reads from and writes to.
168 // The majority of the design remains the same, additional fifos, and
169 // interface must be added, as well as adding new rules to control
170 // which port is read or written.
172 // Several configuration registers are included, and connected to the
173 // config socket/fifo.
176 module mkDMA( DmaC #(numChannels) );
178 // For each socket, we will need 2 fifos, one for request, and 1
179 // for response. These fifos provide the interface for the
181 ////////////////////////////////////////////////////////////////
182 // The fifos for the config port -- these are 1 element pipeline fifos.
183 (* doc = "a fifo to hold incoming configuration requests" *)
184 FIFOF#(Socket_Req) cnfReqF <- mkFIFOF ;
185 (* doc = "a fifo to hold outgoing configuration responses" *)
186 FIFOF#(Socket_Resp) cnfRespF <- mkFIFOF;
189 ////////////////////////////////////////////////////////////////
190 // The fifos for the MMU port
191 (* doc = "a fifo to hold outgoing requests towards the memory" *)
192 FIFOF#(Socket_Req) mmuReqF <- mkFIFOF;
193 (* doc = "a fifo to hold incoming responses from the memory" *)
194 FIFOF#(Socket_Resp) mmuRespF <- mkFIFOF;
197 ////////////////////////////////////////////////////////////////
198 // The fifos for the config port -- these are 1 element pipeline fifos.
199 // FIFOF#(Socket_Req) cnfReqF <- mkGSizedFIFOF(True, False, 2) ;
200 // FIFOF#(Socket_Resp) cnfRespF <- mkGSizedFIFOF(False, True, 2) ;
202 // // The fifos for the MMU
203 // FIFOF#(Socket_Req) mmuReqF <- mkGSizedFIFOF(False, True, 2);
204 // FIFOF#(Socket_Resp) mmuRespF <- mkGSizedFIFOF(True, False, 2);
206 // // The fifos for the MMU2
207 // FIFOF#(Socket_Req) mmu2ReqF <- mkGSizedFIFOF(False, True, 2);
208 // FIFOF#(Socket_Resp) mmu2RespF <- mkGSizedFIFOF(True, False, 2);
210 ////////////////////////////////////////////////////////////////
211 // We will need some registers to control the DMA transfer
212 // A Bit to signal if the transfer is enabled
213 Vector#(numChannels, Reg#(Bool)) dmaEnabledRs <- replicateM(mkReg(False));
215 // The read address and other stuff needed to generate a read
216 Vector#(numChannels, Reg#(ReqAddr)) readAddrRs <- replicateM(mkReg(0));
217 Vector#(numChannels, Reg#(DMACounts)) readCntrRs <- replicateM(mkReg(0));
218 Vector#(numChannels, Reg#(DMACounts)) currentReadRs <- replicateM(mkReg(0));
219 Vector#(numChannels, Reg#(DMACounts)) currentWriteRs <- replicateM(mkReg(0));
221 // To distinguish the ports for reads and writes, we need 2 bits
222 Vector#(numChannels,Reg#(Bit#(2))) portSrcDestRs <- replicateM( mkReg(0)) ;
224 // The destination address
225 Vector#(numChannels, Reg#(ReqAddr)) destAddrRs <- replicateM(mkReg(0));
227 // Use a FIFO to pass the read response to the write "side",
228 // thus allowing pending transations and concurrency.
229 // FIFOs can be replicated as well.
230 Vector#(numChannels,FIFO#(ReqData))
231 responseDataFs <- replicateM( mkSizedFIFO(2)) ;
233 // We also want to pass the destination address for each read over
234 // to the write "side"
235 // The depth of this fifo limits the number of outstanding reads
236 // which may be pending before the write. The maximum outstanding
237 // reads depends on the overall latency of the read requests.
238 Vector#(numChannels,FIFO#(ReqAddr))
239 destAddrFs <- replicateM( mkSizedFIFO( 4 )) ;
241 /// DMA rules //////////////////////////////////////////////////
242 // We define a function inside the module so it can access some
243 // of the registers without passing too many arguments.
244 // The function takes as arguments the conditions and fifos
246 // And returns a set a rules.
247 // The rule are identical to the set used in the one mmu port case.
248 function Rules generatePortDMARules (FIFOF#(Socket_Req) requestF,
249 FIFOF#(Socket_Resp) responseF,
255 // To start a read, when the dma is enabled and there are data to
256 // move, and we are in the right state
257 rule startRead (dmaEnabledRs[chanNum]._read &&
258 readCntrRs[chanNum] > currentReadRs[chanNum] );
259 // Create a read request, and enqueue it
260 // Since there can be multiple pending requests, either read or
261 // writes, we use the reqInfo field to mark these.
263 //let wa= AXI4_Lite_Wr_Addr {awaddr: reqs.first.reqAddr, awprot:0, awuser:0, awlen: 0, awsize: 3, awburst: 'b01};
264 let req = Socket_Req {reqAddr : readAddrRs[chanNum]._read,
267 reqInfo : fromInteger(0 + 2*chanNum)};
268 requestF.enq( req ) ;
270 // Enqueue the Write destination address
271 destAddrFs[chanNum].enq( destAddrRs[chanNum] ) ;
273 // Some house keeping -- increment the read address,
274 // decrement the counter.
275 readAddrRs[chanNum] <= readAddrRs[chanNum] + 4 ;
276 currentReadRs[chanNum] <= currentReadRs[chanNum] + 1 ;
277 destAddrRs[chanNum] <= destAddrRs[chanNum] + 4 ;
278 $display ("DMA[%0d] startRead", chanNum);
282 // We finish the read when we see the correct respInfo on the mmu response fifo
283 rule finishRead ( responseF.first.respInfo == fromInteger(0 + 2*chanNum) );
284 // grab the data from the mmu reponse fifo
285 Socket_Resp resp = responseF.first ;
288 // Need to consider what to do if the response is an error or
289 // fail but we will keep it simple for now
291 // Pass the read data to the write "side" of the dma
292 responseDataFs[chanNum].enq( resp.respData ) ;
293 $display ("DMA[%0d]: finishRead", chanNum);
296 // This rule start the write process
297 // Note that this rule conflicts with rule startRead, so we make
298 // this rule be more urgent. I.e., finish writing before you start
303 let wreq = Socket_Req {reqAddr : destAddrFs[chanNum].first,
304 reqData : responseDataFs[chanNum].first,
306 reqInfo : fromInteger(1 + 2*chanNum) } ;
308 // enqueue the request.
309 requestF.enq( wreq ) ;
311 // Some other house keeping - removing the data from the fifos
312 destAddrFs[chanNum].deq;
313 responseDataFs[chanNum].deq;
314 $display ("DMA[%0d] startWrite", chanNum);
317 // This rule waits for the write to finish
318 rule finishWrite((responseF.first.respInfo == fromInteger(1 + 2*chanNum)) );
319 responseF.deq ; // take the response data and finish
320 currentWriteRs[chanNum]._write (currentWriteRs[chanNum] + 1);
321 $display ("DMA[%0d]: finishWrite", chanNum);
328 function Rules generateTransferDoneRules( Integer chanNum );
331 // Conditions to mark when transfer is done
332 rule markTransferDone (dmaEnabledRs[chanNum]._read &&
333 (currentWriteRs[chanNum]._read == readCntrRs[chanNum]._read) &&
334 (currentReadRs[chanNum]._read == readCntrRs[chanNum]._read) ) ;
335 dmaEnabledRs[chanNum]._write (False) ;
336 currentWriteRs[chanNum] <= 0 ;
337 currentReadRs[chanNum] <= 0 ;
338 $display ("DMA[%0d]: transfer done", chanNum);
343 // Generate the rules, place them in priority order
345 Rules ruleset = emptyRules;
347 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
348 ruleset = rJoinDescendingUrgency (ruleset,
349 generatePortDMARules( mmuReqF, mmuRespF, ch));
351 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
352 ruleset = rJoinDescendingUrgency (ruleset,
353 generateTransferDoneRules(ch));
355 // Add the rules to this module
363 // // Now we can generate the rules which can be manipulated further.
364 // Rules r01 = generatePortDMARules( portSrcDestRs[0]._read[0] == 0,
365 // portSrcDestRs[0]._read[1] == 0,
366 // mmu1ReqF, mmu1RespF, 0 ) ;
367 // Rules r02 = generatePortDMARules( portSrcDestRs[0]._read[0] == 1,
368 // portSrcDestRs[0]._read[1] == 1,
369 // mmu2ReqF, mmu2RespF, 0 ) ;
370 // Rules r11 = generatePortDMARules( portSrcDestRs[1]._read[0] == 0,
371 // portSrcDestRs[1]._read[1] == 0,
372 // mmu1ReqF, mmu1RespF, 1 ) ;
373 // Rules r12 = generatePortDMARules( portSrcDestRs[1]._read[0] == 1,
374 // portSrcDestRs[1]._read[1] == 1,
375 // mmu2ReqF, mmu2RespF, 1 ) ;
376 // Rules td0 = generateTransferDoneRules(0) ;
377 // Rules td1 = generateTransferDoneRules(1) ;
379 // // The set of rules above create a conflict because 2 startRead
380 // // conflict. To specify priority of the DMA channels, we relate
381 // // the urgency of the rules by joining the rules with following
382 // // rule functions.
383 // Rules r1 = rJoinDescendingUrgency( r01, r11) ;
384 // Rules r2 = rJoinDescendingUrgency( r02, r12) ;
386 // r2 = rJoinDescendingUrgency( r1, r2 ) ;
387 // r2 = rJoinDescendingUrgency( r2, td0 ) ;
388 // r2 = rJoinDescendingUrgency( r2, td1 ) ;
393 /// Rules and other code to interface config port /////////////
395 // Add a zero-size register as a default for invalid addresses
396 Reg#(Bit#(0)) nullReg <- mkReg( ? ) ;
398 // For ease of development we want all registers to look like 32
399 // bit resister-- the data size of the config socket.
400 // Create function to map from address to specific registers
401 // For the multi channel DMA, split the 12 bit address into 2
402 // fields, 4 bits to select the channel, 8 for the register.
404 function Reg#(ReqData) selectReg( ReqAddr addr ) ;
405 Bit#(8) taddr = truncate( addr ) ;
406 Bit#(4) channelSel = truncate ( addr >> 8 ) ;
409 8'h00 : return regAToRegBitN( readAddrRs[channelSel] ) ;
410 8'h04 : return regAToRegBitN( readCntrRs[channelSel] ) ;
411 8'h08 : return regAToRegBitN( destAddrRs[channelSel] ) ;
412 8'h0C : return regAToRegBitN( dmaEnabledRs[channelSel] ) ;
413 8'h10 : return regAToRegBitN( portSrcDestRs[channelSel] ) ;
414 default: return regAToRegBitN( nullReg ) ;
419 // A rule for writing to a registers
420 rule writeConfig ( cnfReqF.first.reqOp == WR ) ;
421 let req = cnfReqF.first ;
424 $display ("DMA[%0d] writeConfig: ", (req.reqAddr[11:8]), fshow (req));
426 // Select and write the register
427 let thisReg = selectReg( req.reqAddr ) ;
428 thisReg <= req.reqData ;
430 // Now generate the response and enqueue
431 let resp = Socket_Resp {respOp : OK,
434 respInfo : req.reqInfo,
435 respData : req.reqData } ;
436 cnfRespF.enq( resp ) ;
439 // A rule for reading a configuration register
440 rule readConfig ( cnfReqF.first.reqOp == RD ) ;
441 let req = cnfReqF.first ;
444 // Select the register
445 let thisReg = selectReg( req.reqAddr ) ;
447 // Now generate the response and enqueue
448 let resp = Socket_Resp {respOp : OK,
451 respInfo : req.reqInfo,
452 respData : thisReg } ;
453 cnfRespF.enq( resp ) ;
456 (* descending_urgency =
457 "writeConfig, readConfig, unknownConfig" *)
458 rule unknownConfig ( True ) ;
459 let req = cnfReqF.first ;
462 // Select the register
463 let thisReg = selectReg( req.reqAddr ) ;
465 // Now generate the response and enqueue
466 let resp = Socket_Resp {respOp : NOP,
469 respInfo : req.reqInfo,
470 respData : thisReg } ;
471 cnfRespF.enq( resp ) ;
474 function AXI4_Lite_Master_Xactor_IFC#(wd_addr,wd_dara,wd_user)
475 fifos_to_AXI4_Lite_ifc (FIFOF#(Socket_Req) reqs,
476 FIFOF#(Socket_Resp) resps);
478 (interface AXI4_Lite_Master_Xactor_IFC#(wd_addr,wd_data,wd_user)
479 //interface AXI4_Lite_Master_IFC #(wd_addr, wd_data, wd_user) axi_side;
480 interface FIFOF_I #(AXI4_Lite_Wr_Addr #(wd_addr, wd_user))
481 i_wr_addr= fifos_to_AXI4_Lite_Wr_Addr(reqs);
482 interface FIFOF_I #(AXI4_Lite_Wr_Data #(wd_data))
483 i_wr_data= fifos_to_AXI4_Lite_Wr_Data(reqs);
484 interface FIFOF_O #(AXI4_Lite_Wr_Resp #(wd_user))
485 o_wr_resp= fifos_to_AXI4_Lite_Wr_Resp(resps);
487 interface FIFOF_I #(AXI4_Lite_Rd_Addr #(wd_addr, wd_user));
488 i_rd_addr= fifos_to_AXI4_Lite_Rd_Addr(reqs);
489 interface FIFOF_O #(AXI4_Lite_Rd_Data #(wd_data, wd_user))
490 o_rd_data= fifos_to_AXI4_Lite_Rd_Data(resps);
494 function FIFO_I#(AXI4_Lite_Wr_Addr #(wd_addr, wd_user))
495 fifos_to_AXI4_Lite_Wr_Addr#(FIFOF#(Socket_Req) reqs);
496 return interface FIFO_I
497 method Action enq(din);
498 let x= Socket_Req {reqOp: defaultValue,
499 reqInfo: defaultValue,
501 reqData: defaultValue};
505 method Bool notFull()= reqs.notFull;
510 ////////////////////////////////////////////////////////////////
511 ////////////////////////////////////////////////////////////////
513 // Create the interfaces by connecting the fifo interfaces to the
516 interface TLMRecvIFC cfg;
518 method ActionValue#(BusResponse) get;
520 return socketRespToBusResp(cnfRespF.first());
524 method Action put(x) = cnfReqF.enq(busReqToSocketReq(x));
531 AXI4_Lite_Master_Xactor_IFC #(`PADDR,`Reg_width,0) m_xactor <- mkAXI4_Lite_Master_Xactor;
532 interface AXI4_Lite_Master_IFC#(`PADDR, `Reg_width, 0) cfg;
536 interface TLMSendIFC mmu;
538 method ActionValue#(BusRequest) get;
540 return socketReqToBusReq(mmuReqF.first());
544 method Action put(x) = mmuRespF.enq(busRespToSocketResp(x));
550 // ================================================================
552 function BusResponse socketRespToBusResp(Socket_Resp tmp);
553 BusResponse r = defaultValue;
554 r.error = !(tmp.respOp==OK);
555 r.data = tmp.respData;
556 r.write = (tmp.reqOp==WR);
557 r.id = unpack(truncate(tmp.respInfo));
561 function BusRequest socketReqToBusReq(Socket_Req tmp);
562 BusRequest r = defaultValue;
564 r.address = tmp.reqAddr;
565 r.data = tmp.reqData;
566 r.write = (tmp.reqOp == WR);
567 r.id = unpack(truncate(tmp.reqInfo));
571 function Socket_Req busReqToSocketReq(BusRequest x);
572 Socket_Req r = unpack(0);
573 r.reqOp = x.write ? WR : RD;
574 r.reqAddr = x.address;
576 r.reqInfo = pack(extend(x.id));
581 function Socket_Resp busRespToSocketResp(BusResponse x);
582 Socket_Resp r = unpack(0);
583 r.respOp = x.error ? NOP : OK;
585 r.reqOp = x.write ? WR : RD;
586 r.respInfo = pack(extend(x.id));