add dma peripheral
[shakti-peripherals.git] / src / peripherals / dma / DMAv2.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 package DMAv2;
15
16 // ================================================================
17 // Copyright (c) Bluespec, Inc., 2007-2011 All Rights Reserved
18
19 import FIFO :: * ;
20 import FIFOF :: * ;
21 import Vector :: * ;
22 import FShow::*;
23 import GetPut::*;
24 import DefaultValue ::*;
25 import AXI4_Types :: *;
26 import AXI4_Fabric :: *;
27 import Semi_FIFOF :: *;
28
29 `include "ARM.defines"
30
31 // ================================================================
32 // DMA requests and responses parameters
33
34 `define Req_Info_sz 10
35 `define Req_Addr_sz 32
36 `define Req_Data_sz 32
37
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;
45
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.
52
53 function Reg#(Bit#(n)) regAToRegBitN( Reg#(a_type) rin )
54 provisos ( Bits#( a_type, asize),
55 Add#(asize,xxx,n) ) ;
56
57 return
58 interface Reg
59 method Bit#(n) _read ();
60 a_type tmp = rin._read() ;
61 return zeroExtend (pack( tmp )) ;
62 endmethod
63 method Action _write( Bit#(n) din );
64 rin._write( unpack( truncate(din) )) ;
65 endmethod
66 endinterface ;
67 endfunction
68
69 // ================================================================
70 // The DMA interface has two sub-interfaces
71 // A AXI4 Slave interface for config
72 // A AXI4 Master interface for data transfers
73
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;
77 endinterface
78
79
80 typedef UInt#(16) DMACounts ;
81 // The number of channels is a parameter.
82 // typedef 2 NumChannels;
83
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.
89
90 // Several configuration registers are included, and connected to the
91 // config socket/fifo.
92
93
94 module mkDMA( DmaC #(numChannels) );
95
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;
103
104
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));
109
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));
115
116 // To distinguish the ports for reads and writes, we need 2 bits
117 Vector#(numChannels,Reg#(Bit#(2))) portSrcDestRs <- replicateM( mkReg(0)) ;
118
119 // The destination address
120 Vector#(numChannels, Reg#(Req_Addr)) destAddrRs <- replicateM(mkReg(0));
121
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)) ;
127
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 )) ;
135
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
140 // (interfaces)
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,
144 Integer chanNum
145 );
146 return
147 rules
148
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.
156
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);
160
161 // Enqueue the Write destination address
162 destAddrFs[chanNum].enq( destAddrRs[chanNum] ) ;
163
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);
170 endrule
171
172
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) ;
177
178 // Need to consider what to do if the response is an error or
179 // fail but we will keep it simple for now
180
181 // Pass the read data to the write "side" of the dma
182 responseDataFs[chanNum].enq( resp.rdata ) ;
183 $display ("DMA[%0d]: finishRead", chanNum);
184 endrule
185
186
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
190 // reading more.
191 rule startWrite;
192 // Generate a Write
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};
196
197 // enqueue the request.
198 xactor.i_wr_data.enq(write_data);
199 xactor.i_wr_addr.enq(write_addr);
200
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);
205 endrule
206
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);
212 endrule
213
214 endrules;
215 endfunction
216
217 function Rules generateTransferDoneRules( Integer chanNum );
218 return
219 rules
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);
228 endrule
229 endrules ;
230 endfunction
231
232 // Generate the rules, place them in priority order
233 //
234 Rules ruleset = emptyRules;
235
236 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
237 ruleset = rJoinDescendingUrgency (ruleset,
238 generatePortDMARules( m_xactor, ch));
239
240 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
241 ruleset = rJoinDescendingUrgency (ruleset,
242 generateTransferDoneRules(ch));
243
244 // Add the rules to this module
245 //
246 addRules (ruleset);
247
248
249 /// Rules and other code to interface config port /////////////
250
251 // Add a zero-size register as a default for invalid addresses
252 Reg#(Bit#(0)) nullReg <- mkReg( ? ) ;
253
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.
259
260 function Reg#(Req_Data) selectReg( Req_Addr addr ) ;
261 Bit#(8) taddr = truncate( addr ) ;
262 Bit#(4) channelSel = truncate ( addr >> 8 ) ;
263 return
264 case ( taddr )
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 ) ;
271 endcase ;
272 endfunction
273
274
275 // A rule for writing into configuration registers
276 rule writeConfig;
277 let write_addr <- pop_o(s_xactor.o_wr_addr);
278 let write_data <- pop_o(s_xactor.o_wr_data);
279
280 $display ("DMA[%0d] writeConfig: ", (write_addr.awaddr[11:8]), fshow (write_addr));
281
282 // Select and write the register
283 let thisReg = selectReg(write_addr.awaddr);
284 thisReg <= write_data.wdata;
285
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);
289 endrule
290
291 // A rule for reading a configuration register
292 rule readConfig;
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);
300 endrule
301
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.
304
305 ////////////////////////////////////////////////////////////////
306 ////////////////////////////////////////////////////////////////
307 //
308 // Create the interfaces by connecting the axi side interfaces
309 // of the transactors to it.
310
311 interface cfg= s_xactor.axi_side;
312 interface mmu= m_xactor.axi_side;
313 endmodule
314
315 endpackage
316
317
318