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
24 import DefaultValue ::*;
25 import AXI4_Types :: *;
26 import AXI4_Fabric :: *;
27 import Semi_FIFOF :: *;
29 `include "ARM.defines"
31 // ================================================================
32 // DMA requests and responses parameters
34 `define Req_Info_sz 10
35 `define Req_Addr_sz 32
36 `define Req_Data_sz 32
38 typedef Bit#(`Req_Info_sz) Req_Info;
39 typedef Bit#(`Req_Addr_sz) Req_Addr;
40 typedef Bit#(`Req_Data_sz) Req_Data;
41 //The Req and Resp are of the same width in the current design
42 //typedef Bit#(10) RespInfo;
43 //typedef Bit#(1) RespAddr;
44 //typedef Bit#(32) RespData;
46 // ----------------------------------------------------------------
47 // At times it is best to consider registers as completely homogeneous,
48 // so that they can be accessed as a bit pattern with no internal
49 // structure. These functions convert reg interfaces based on a
50 // structured type to reg interfaces based on a bit pattern of at
51 // least the same width.
53 function Reg#(Bit#(n)) regAToRegBitN( Reg#(a_type) rin )
54 provisos ( Bits#( a_type, asize),
59 method Bit#(n) _read ();
60 a_type tmp = rin._read() ;
61 return zeroExtend (pack( tmp )) ;
63 method Action _write( Bit#(n) din );
64 rin._write( unpack( truncate(din) )) ;
69 // ================================================================
70 // The DMA interface has two sub-interfaces
71 // A AXI4 Slave interface for config
72 // A AXI4 Master interface for data transfers
74 interface DmaC #(numeric type numChannels);
75 interface AXI4_Master_IFC#(`Req_Addr_sz,`Req_Data_sz,`Req_Info_sz) mmu;
76 interface AXI4_Slave_IFC#(`Req_Addr_sz,`Req_Data_sz,`Req_Info_sz) cfg;
80 typedef UInt#(16) DMACounts ;
81 // The number of channels is a parameter.
82 // typedef 2 NumChannels;
84 // For the module, we add additional configuration registers to control
85 // which port the transfer reads from and writes to.
86 // The majority of the design remains the same, additional fifos, and
87 // interface must be added, as well as adding new rules to control
88 // which port is read or written.
90 // Several configuration registers are included, and connected to the
91 // config socket/fifo.
94 module mkDMA( DmaC #(numChannels) );
96 // The DMA contains one master interface, and one slave interface. The processor sends
97 // request through the slave interface to set the config registers.
98 // The DMA's master initiates a request to one of the peripherals through one of the
99 // channels. The response is taken (through response sub-interface of DMA's Master interface
100 // and returned to the processor (through response sub-interface of the DMA's Slave interface
101 AXI4_Slave_Xactor_IFC #(`Req_Addr_sz,`Req_Data_sz,`Req_Info_sz) s_xactor <- mkAXI4_Slave_Xactor;
102 AXI4_Master_Xactor_IFC #(`Req_Addr_sz,`Req_Data_sz,`Req_Info_sz) m_xactor <- mkAXI4_Master_Xactor;
105 ////////////////////////////////////////////////////////////////
106 // We will need some registers to control the DMA transfer
107 // A Bit to signal if the transfer is enabled
108 Vector#(numChannels, Reg#(Bool)) dmaEnabledRs <- replicateM(mkReg(False));
110 // The read address and other stuff needed to generate a read
111 Vector#(numChannels, Reg#(Req_Addr)) readAddrRs <- replicateM(mkReg(0));
112 Vector#(numChannels, Reg#(DMACounts)) readCntrRs <- replicateM(mkReg(0));
113 Vector#(numChannels, Reg#(DMACounts)) currentReadRs <- replicateM(mkReg(0));
114 Vector#(numChannels, Reg#(DMACounts)) currentWriteRs <- replicateM(mkReg(0));
116 // To distinguish the ports for reads and writes, we need 2 bits
117 Vector#(numChannels,Reg#(Bit#(2))) portSrcDestRs <- replicateM( mkReg(0)) ;
119 // The destination address
120 Vector#(numChannels, Reg#(Req_Addr)) destAddrRs <- replicateM(mkReg(0));
122 // Use a FIFO to pass the read response to the write "side",
123 // thus allowing pending transations and concurrency.
124 // FIFOs can be replicated as well.
125 Vector#(numChannels,FIFO#(Req_Data))
126 responseDataFs <- replicateM( mkSizedFIFO(2)) ;
128 // We also want to pass the destination address for each read over
129 // to the write "side"
130 // The depth of this fifo limits the number of outstanding reads
131 // which may be pending before the write. The maximum outstanding
132 // reads depends on the overall latency of the read requests.
133 Vector#(numChannels,FIFO#(Req_Addr))
134 destAddrFs <- replicateM( mkSizedFIFO( 4 )) ;
136 /// DMA rules //////////////////////////////////////////////////
137 // We define a function inside the module so it can access some
138 // of the registers without passing too many arguments.
139 // The function takes as arguments the conditions and fifos
141 // And returns a set a rules.
142 // The rule are identical to the set used in the one mmu port case.
143 function Rules generatePortDMARules (AXI4_Master_Xactor_IFC#(`Req_Addr_sz,`Req_Data_sz,`Req_Info_sz) xactor,
149 // To start a read, when the dma is enabled and there are data to
150 // move, and we are in the right state
151 rule startRead (dmaEnabledRs[chanNum]._read &&
152 readCntrRs[chanNum] > currentReadRs[chanNum] );
153 // Create a read request, and enqueue it
154 // Since there can be multiple pending requests, either read or
155 // writes, we use the `Req_Info field to mark these.
157 let read_request = AXI4_Rd_Addr {araddr: readAddrRs[chanNum]._read, arprot: 0,
158 aruser: fromInteger(0 + 2*chanNum), arlen: 0, arsize: 2, arburst: 'b0}; // arburst: 00-FIXED 01-INCR 10-WRAP
159 xactor.i_rd_addr.enq(read_request);
161 // Enqueue the Write destination address
162 destAddrFs[chanNum].enq( destAddrRs[chanNum] ) ;
164 // Some house keeping -- increment the read address,
165 // decrement the counter.
166 readAddrRs[chanNum] <= readAddrRs[chanNum] + 4 ;
167 currentReadRs[chanNum] <= currentReadRs[chanNum] + 1 ;
168 destAddrRs[chanNum] <= destAddrRs[chanNum] + 4 ;
169 $display ("DMA[%0d] startRead", chanNum);
173 // We finish the read when we see the correct respInfo on the mmu response fifo
174 rule finishRead ( xactor.o_rd_data.first.ruser == fromInteger(0 + 2*chanNum) );
175 // grab the data from the mmu reponse fifo
176 let resp <- pop_o(xactor.o_rd_data) ;
178 // Need to consider what to do if the response is an error or
179 // fail but we will keep it simple for now
181 // Pass the read data to the write "side" of the dma
182 responseDataFs[chanNum].enq( resp.rdata ) ;
183 $display ("DMA[%0d]: finishRead", chanNum);
187 // This rule start the write process
188 // Note that this rule conflicts with rule startRead, so we make
189 // this rule be more urgent. I.e., finish writing before you start
193 let write_data = AXI4_Wr_Data {wdata: responseDataFs[chanNum].first, wstrb: 0, wlast:True};
194 let write_addr = AXI4_Wr_Addr {awaddr: destAddrFs[chanNum].first, awprot:0,
195 awuser:fromInteger(1 + 2*chanNum), awlen: 0, awsize: 2, awburst: 0};
197 // enqueue the request.
198 xactor.i_wr_data.enq(write_data);
199 xactor.i_wr_addr.enq(write_addr);
201 // Some other house keeping - removing the data from the fifos
202 destAddrFs[chanNum].deq;
203 responseDataFs[chanNum].deq;
204 $display ("DMA[%0d] startWrite", chanNum);
207 // This rule waits for the write to finish
208 rule finishWrite((xactor.o_wr_resp.first.buser == fromInteger(1 + 2*chanNum)) );
209 xactor.o_wr_resp.deq ; // take the response data and finish
210 currentWriteRs[chanNum]._write (currentWriteRs[chanNum] + 1);
211 $display ("DMA[%0d]: finishWrite", chanNum);
217 function Rules generateTransferDoneRules( Integer chanNum );
220 // Conditions to mark when transfer is done
221 rule markTransferDone (dmaEnabledRs[chanNum]._read &&
222 (currentWriteRs[chanNum]._read == readCntrRs[chanNum]._read) &&
223 (currentReadRs[chanNum]._read == readCntrRs[chanNum]._read) ) ;
224 dmaEnabledRs[chanNum]._write (False) ;
225 currentWriteRs[chanNum] <= 0 ;
226 currentReadRs[chanNum] <= 0 ;
227 $display ("DMA[%0d]: transfer done", chanNum);
232 // Generate the rules, place them in priority order
234 Rules ruleset = emptyRules;
236 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
237 ruleset = rJoinDescendingUrgency (ruleset,
238 generatePortDMARules( m_xactor, ch));
240 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
241 ruleset = rJoinDescendingUrgency (ruleset,
242 generateTransferDoneRules(ch));
244 // Add the rules to this module
249 /// Rules and other code to interface config port /////////////
251 // Add a zero-size register as a default for invalid addresses
252 Reg#(Bit#(0)) nullReg <- mkReg( ? ) ;
254 // For ease of development we want all registers to look like 32
255 // bit resister-- the data size of the config socket.
256 // Create function to map from address to specific registers
257 // For the multi channel DMA, split the 12 bit address into 2
258 // fields, 4 bits to select the channel, 8 for the register.
260 function Reg#(Req_Data) selectReg( Req_Addr addr ) ;
261 Bit#(8) taddr = truncate( addr ) ;
262 Bit#(4) channelSel = truncate ( addr >> 8 ) ;
265 8'h00 : return regAToRegBitN( readAddrRs[channelSel] ) ;
266 8'h04 : return regAToRegBitN( readCntrRs[channelSel] ) ;
267 8'h08 : return regAToRegBitN( destAddrRs[channelSel] ) ;
268 8'h0C : return regAToRegBitN( dmaEnabledRs[channelSel] ) ;
269 8'h10 : return regAToRegBitN( portSrcDestRs[channelSel] ) ;
270 default: return regAToRegBitN( nullReg ) ;
275 // A rule for writing into configuration registers
277 let write_addr <- pop_o(s_xactor.o_wr_addr);
278 let write_data <- pop_o(s_xactor.o_wr_data);
280 $display ("DMA[%0d] writeConfig: ", (write_addr.awaddr[11:8]), fshow (write_addr));
282 // Select and write the register
283 let thisReg = selectReg(write_addr.awaddr);
284 thisReg <= write_data.wdata;
286 // Now generate the response and enqueue
287 let resp = AXI4_Wr_Resp { bresp: AXI4_OKAY, buser: write_addr.awuser };
288 s_xactor.i_wr_resp.enq(resp);
291 // A rule for reading a configuration register
293 let read_addr <- pop_o(s_xactor.o_rd_addr);
294 // Select the register
295 let thisReg = selectReg(read_addr.araddr) ;
296 // Now generate the response and enqueue
297 let resp = AXI4_Rd_Data {rresp: AXI4_OKAY, rdata: thisReg,
298 rlast:True, ruser: read_addr.aruser};
299 s_xactor.i_rd_data.enq(resp);
302 //Note that unknownConfig rule is not needed here because all the FIFOs will
303 //be empty and hence neither writeConfig nor readConfig will fire.
305 ////////////////////////////////////////////////////////////////
306 ////////////////////////////////////////////////////////////////
308 // Create the interfaces by connecting the axi side interfaces
309 // of the transactors to it.
311 interface cfg= s_xactor.axi_side;
312 interface mmu= m_xactor.axi_side;