source: soft/giet_vm/giet_drivers/dma_driver.c @ 344

Last change on this file since 344 was 343, checked in by alain, 10 years ago

Introducing a _dma_physical_memcpy() in the DMA driver.

File size: 14.9 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File     : dma_driver.c
3// Date     : 23/11/2013
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
7// The dma_driver.c and dma_driver.h files are part ot the GIET-VM nano-kernel.
8// This driver supports the SoCLib vci_multi_dma component.
9//
10// It can exist several DMA controlers in the architecture (one per cluster),
11// and each controller can contain several channels.
12//
13// There is  (NB_CLUSTERS * NB_DMA_CHANNELS) channels, indexed by a global index:
14//        dma_id = cluster_xy * NB_DMA_CHANNELS + loc_id
15//
16// A DMA channel is a private ressource allocated to a given processor.
17// It is exclusively used by the kernet to speedup data transfers, and
18// there is no lock protecting exclusive access to the channel.
19// As the kernel uses a polling policy on the DMA_STATUS register to detect
20// transfer completion, the DMA IRQ is not used.
21//
22// The virtual base address of the segment associated to a channel is:
23//    SEG_DMA_BASE + cluster_xy * PERI_CLUSTER_INCREMENT + DMA_SPAN * channel_id
24//
25// The SEG_DMA_BASE virtual address mus be defined in the hard_config.h file.
26////////////////////////////////////////////////////////////////////////////////////
27
28#include <giet_config.h>
29#include <hard_config.h>
30#include <dma_driver.h>
31#include <tty_driver.h>
32#include <vmem.h>
33#include <utils.h>
34
35#if !defined(X_SIZE)
36# error: You must define X_SIZE in the hard_config.h file
37#endif
38
39#if !defined(Y_SIZE)
40# error: You must define X_SIZE in the hard_config.h file
41#endif
42
43#if !defined(X_WIDTH)
44# error: You must define X_WIDTH in the hard_config.h file
45#endif
46
47#if !defined(Y_WIDTH)
48# error: You must define X_WIDTH in the hard_config.h file
49#endif
50
51#if !defined(NB_DMA_CHANNELS)
52# error: You must define NB_DMA_CHANNELS in the hard_config.h file
53#endif
54
55#if !defined(SEG_DMA_BASE)
56# error: You must define SEG_DMA_BASE in the hard_config.h file
57#endif
58
59#if !defined(PERI_CLUSTER_INCREMENT)
60# error: You must define PERI_CLUSTER_INCREMENT in the hard_config.h file
61#endif
62
63extern unsigned int _ptabs_vaddr[];
64
65//////////////////////////////////////////////////////////////////////////////////
66// AS the GIET-VM uses a polling policy to detect transfer completion,
67// The DMA component initialisation must disable interrupts.
68// This function disables interrupts for one DMA channel in one cluster.
69// Returns 0 if success, returns > 0 if error.
70//////////////////////////////////////////////////////////////////////////////////
71unsigned int _dma_init( unsigned int cluster_xy,
72                        unsigned int channel_id )
73{
74#if NB_DMA_CHANNELS > 0
75
76    // parameters checking
77    unsigned int x = cluster_xy >> Y_WIDTH;
78    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
79    if (x >= X_SIZE)                    return 1; 
80    if (y >= Y_SIZE)                    return 1; 
81    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
82
83    // compute DMA base address
84    unsigned int* dma_address = (unsigned int*) ( SEG_DMA_BASE + 
85                                (cluster_xy * PERI_CLUSTER_INCREMENT) );
86
87    // disable interrupt for selected channel
88    dma_address[channel_id * DMA_SPAN + DMA_IRQ_DISABLE] = 1;           
89    return 0;
90#else
91    return 1;
92#endif
93}
94
95//////////////////////////////////////////////////////////////////////////////////
96// This function re-initialises one DMA channel in one cluster after a transfer
97// completion. It actually forces the channel to return in iDLE state.
98//////////////////////////////////////////////////////////////////////////////////
99unsigned int _dma_reset( unsigned int cluster_xy, 
100                         unsigned int channel_id ) 
101{
102#if NB_DMA_CHANNELS > 0
103
104    // parameters checking
105    unsigned int x = cluster_xy >> Y_WIDTH;
106    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
107    if (x >= X_SIZE)                    return 1; 
108    if (y >= Y_SIZE)                    return 1; 
109    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
110
111    // compute DMA base address
112    unsigned int* dma_address = (unsigned int*) ( SEG_DMA_BASE + 
113                                (cluster_xy * PERI_CLUSTER_INCREMENT) );
114
115    // reset selected channel
116    dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0;           
117    return 0;
118#else
119    return 1;
120#endif
121}
122
123//////////////////////////////////////////////////////////////////////////////////
124// This function returns the status of a DMA channel in a given cluster
125//////////////////////////////////////////////////////////////////////////////////
126unsigned int _dma_get_status( unsigned int cluster_xy, 
127                              unsigned int channel_id ) 
128{
129#if NB_DMA_CHANNELS > 0
130
131    // parameters checking
132    unsigned int x = cluster_xy >> Y_WIDTH;
133    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
134    if (x >= X_SIZE)                    return 1; 
135    if (y >= Y_SIZE)                    return 1; 
136    if (channel_id >= NB_DMA_CHANNELS)  return 1;
137
138    // compute DMA base address
139    unsigned int * dma_address = (unsigned int *) ( SEG_DMA_BASE + 
140                                 (cluster_xy * PERI_CLUSTER_INCREMENT) );
141
142    // get selected channel status
143    return dma_address[channel_id * DMA_SPAN + DMA_LEN];
144#else
145    return DMA_IDLE;
146#endif
147}
148
149//////////////////////////////////////////////////////////////////////////////////
150// This function sets the physical address (including 64 bits extension)
151// for the source and destination buffers in a DMA channel in a given cluster
152// and sets the transfer size to lauch the transfer.
153//////////////////////////////////////////////////////////////////////////////////
154void _dma_start_transfer( unsigned int       cluster_xy,  // DMA cluster
155                          unsigned int       channel_id,  // DMA channel
156                          unsigned long long dst_paddr,   // physical address
157                          unsigned long long src_paddr,   // physical address
158                          unsigned int       size )       // bytes
159{
160#if NB_DMA_CHANNELS > 0
161
162    // compute DMA base address
163    unsigned int * dma_address = (unsigned int *) ( SEG_DMA_BASE + 
164                                 (cluster_xy * PERI_CLUSTER_INCREMENT) );
165
166    // selected channel configuration and lauching
167    dma_address[channel_id * DMA_SPAN + DMA_SRC]     = (unsigned int)(src_paddr);
168    dma_address[channel_id * DMA_SPAN + DMA_SRC_EXT] = (unsigned int)(src_paddr>>32);
169    dma_address[channel_id * DMA_SPAN + DMA_DST]     = (unsigned int)(dst_paddr);
170    dma_address[channel_id * DMA_SPAN + DMA_DST_EXT] = (unsigned int)(dst_paddr>>32);
171    dma_address[channel_id * DMA_SPAN + DMA_LEN]     = (unsigned int)size;
172
173#endif
174}
175
176///////////////////////////////////////////////////////////////////////////////////
177// This function copies a source memory buffer to a destination memory buffer,
178// using directly physical addresses.
179// This blocking function is supposed to be used by the kernel only,
180// and uses a polling policy on DMA_STATUS register to detect completion.
181// Therefore, the DMA_IRQ is NOT used.
182// The source and destination buffers base addresses must be word aligned,
183// and the buffer's size must be multiple of 4.
184// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
185// message is displayed on TTY0, and the system crash.
186///////////////////////////////////////////////////////////////////////////////////
187void _dma_physical_copy( unsigned int       cluster_xy,  // DMA cluster
188                         unsigned int       channel_id,  // DMA channel
189                         unsigned long long dst_paddr,   // destination physical address
190                         unsigned long long src_paddr,   // source physical address
191                         unsigned int       size )       // bytes
192{
193#if NB_DMA_CHANNELS > 0
194
195    // check DMA channel parameters
196    unsigned int x = cluster_xy >> Y_WIDTH;
197    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
198    if ( (x >= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) )
199    {
200        _printf("\n[GIET ERROR] in _dma_physical_copy() : illegal DMA channel ");
201        _exit();
202    }
203
204    // check buffers alignment constraints
205    if ( (dst_paddr & 0x3)   || (src_paddr & 0x3) || (size & 0x3) )
206    {
207        _printf("\n[GIET ERROR] in _dma_physical_copy() : buffer unaligned\n");
208        _exit();
209    }
210
211#if GIET_DEBUG_DMA_DRIVER
212_printf("\n[DMA DEBUG] Start a dma_physical_copy on channel[%d,%d,%d] at cycle %d\n"
213        " - src_paddr   = %l\n"
214        " - dst_paddr   = %l\n"
215        " - bytes       = %x\n",
216        x, y, channel_id, _get_proctime(), src_paddr, dst_paddr, size );
217#endif
218
219    // dma channel configuration & lauching
220    _dma_start_transfer( cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
221
222    // scan dma channel status
223    unsigned int status = _dma_get_status( cluster_xy, channel_id );
224    while( (status != DMA_SUCCESS) && 
225           (status != DMA_READ_ERROR) &&
226           (status != DMA_WRITE_ERROR) )
227    {
228        status = _dma_get_status( cluster_xy, channel_id );
229
230#if GIET_DEBUG_DMA_DRIVER
231_printf("\n[DMA DEBUG] _dma_physical_copy() : ... waiting on DMA_STATUS register\n");
232#endif
233
234    }
235   
236    // analyse status
237    if( status != DMA_SUCCESS )
238    {
239        _printf("\n[GIET ERROR] in _dma_physical_copy() : DMA_STATUS = %x\n", status );
240        _exit();
241    }
242    // reset dma channel
243    _dma_reset( cluster_xy, channel_id );
244
245#if GIET_DEBUG_DMA_DRIVER
246_printf("\n[DMA DEBUG] _dma_physical_copy() completed at cycle %d\n", _get_proctime() );
247#endif
248
249#else // NB_DMA_CHANNELS == 0
250    _printf("\n[GIET ERROR] in _dma_physical_copy() : NB_DMA_CHANNELS == 0 / cycle %d\n",
251            _get_proctime );
252    _exit();
253#endif
254}
255
256///////////////////////////////////////////////////////////////////////////////////
257// This function copies a source memory buffer to a destination memory buffer,
258// making virtual to physical address translation: the MMU should be activated.
259// This blocking function is supposed to be used by the kernel only,
260// and uses a polling policy on DMA_STATUS register to detect completion.
261// Therefore, the DMA_IRQ is NOT used.
262// The source and destination buffers base addresses must be word aligned,
263// and the buffer's size must be multiple of 4.
264// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
265// message is displayed on TTY0, and the system crash.
266///////////////////////////////////////////////////////////////////////////////////
267void  _dma_copy( unsigned int cluster_xy,    // DMA cluster
268                 unsigned int channel_id,    // DMA channel
269                 unsigned int vspace_id,     // vspace index for v2p translation
270                 unsigned int dst_vaddr,     // dst_vaddr buffer vbase
271                 unsigned int src_vaddr,     // src_vaddr buffer vbase
272                 unsigned int size )         // bytes
273{
274#if NB_DMA_CHANNELS > 0
275
276    // check DMA channel parameters
277    unsigned int x = cluster_xy >> Y_WIDTH;
278    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
279    if ( (x >= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) )
280    {
281        _printf("\n[GIET ERROR] in _dma_copy() : illegal DMA channel ");
282        _exit();
283    }
284
285    // check buffers alignment constraints
286    if ( (dst_vaddr & 0x3)   || (src_vaddr & 0x3) || (size & 0x3) )
287    {
288        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
289        _exit();
290    }
291
292    unsigned int ko;
293    unsigned int ppn;
294    unsigned int flags;
295
296#if GIET_DEBUG_DMA_DRIVER
297_printf("\n[DMA DEBUG] Start a dma_copy on channel[%d,%d,%d] at cycle %d\n"
298        " - src_vaddr   = %x\n"
299        " - dst_vaddr   = %x\n"
300        " - bytes       = %x\n",
301        x, y, channel_id, _get_proctime(), src_vaddr, dst_vaddr, size );
302#endif
303
304    // checking alignment constraints
305    if ( (((unsigned int)dst_vaddr) & 0x3) ||
306         (((unsigned int)src_vaddr) & 0x3) ||
307         (size & 0x3) )
308    {
309        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
310        _exit();
311    }
312
313    // get vspace page table pointer
314    unsigned int pt =  _ptabs_vaddr[vspace_id];
315
316    // get src_paddr buffer physical addresse
317    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
318                         src_vaddr>>12,                  // vpn
319                         &ppn,                           // ppn
320                         &flags );                       // flags
321    if ( ko ) 
322    {
323        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
324        _exit();
325    }
326    unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | 
327                                   (unsigned long long)(src_vaddr & 0x00000FFF);
328
329    // get dst_paddr buffer physical addresse
330    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
331                         dst_vaddr>>12,                  // vpn
332                         &ppn,                           // ppn
333                         &flags );                       // flags
334    if ( ko ) 
335    {
336        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
337        _exit();
338    }
339    unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | 
340                                   (unsigned long long)(dst_vaddr & 0x00000FFF);
341
342#if GIET_DEBUG_DMA_DRIVER
343_printf(" - src_paddr   = %l\n"
344        " - dst_paddr   = %l\n",
345        src_paddr, dst_paddr );
346#endif
347
348    // dma channel configuration & lauching
349    _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
350
351    // scan dma channel status
352    unsigned int status = _dma_get_status( cluster_xy, channel_id );
353    while( (status != DMA_SUCCESS) && 
354           (status != DMA_READ_ERROR) &&
355           (status != DMA_WRITE_ERROR) )
356    {
357        status = _dma_get_status( cluster_xy, channel_id );
358
359#if GIET_DEBUG_DMA_DRIVER
360_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register\n");
361#endif
362
363    }
364   
365    // analyse status
366    if( status != DMA_SUCCESS )
367    {
368        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
369        _exit();
370    }
371    // reset dma channel
372    _dma_reset( cluster_xy, channel_id );
373
374#if GIET_DEBUG_DMA_DRIVER
375_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
376#endif
377
378#else // NB_DMA_CHANNELS == 0
379    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS == 0 / cycle %d\n",
380            _get_proctime );
381    _exit();
382#endif
383} // end _dma_copy
384
385///////////////////////////////////////////////////////////////////////////////
386// This ISR handles the IRQ generated by a DMA channel.
387///////////////////////////////////////////////////////////////////////////////
388void _dma_isr( unsigned int irq_type,
389               unsigned int irq_id,
390               unsigned int channel )
391{
392    _printf("\n[GIET ERROR] _dma_isr() not implemented / cycle %d\n",
393            _get_proctime() );
394    _exit();
395}
396
397
398
399
400// tab-width: 4
401// c-basic-offset: 4
402// c-file-offsets:((innamespace . 0)(inline-open . 0))
403// indent-tabs-mode: nil
404// End:
405// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
406
Note: See TracBrowser for help on using the repository browser.