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

Last change on this file since 313 was 313, checked in by alain, 11 years ago

The main modif is in TTY driver: The hard lock implemented in the
vci_multi_tty component is not used anymore, because this (non cacheable)
lock was not scalable... It has been replaced by a software (cacheable) lock
that has a better scalability in case of hardware cache coherence.
(see the _tty_get_lock() and _tty_release_lock() functions)

File size: 11.8 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, and there is no DMA_ISR.
21////////////////////////////////////////////////////////////////////////////////////
22// The virtual base address of the segment associated to a channel is:
23//
24//    seg_dma_base + cluster_xy * vseg_cluster_increment + DMA_SPAN * channel_id
25//
26////////////////////////////////////////////////////////////////////////////////////
27
28#include <giet_config.h>
29#include <dma_driver.h>
30#include <utils.h>
31#include <tty_driver.h>
32#include <vmem.h>
33
34#if !defined(X_SIZE)
35# error: You must define X_SIZE in the hard_config.h file
36#endif
37
38#if !defined(Y_SIZE)
39# error: You must define X_SIZE in the hard_config.h file
40#endif
41
42#if !defined(X_WIDTH)
43# error: You must define X_WIDTH in the hard_config.h file
44#endif
45
46#if !defined(Y_WIDTH)
47# error: You must define X_WIDTH in the hard_config.h file
48#endif
49
50#if !defined(NB_DMA_CHANNELS)
51# error: You must define NB_DMA_CHANNELS in the hard_config.h file
52#endif
53
54extern unsigned int _ptabs_vaddr[];
55
56//////////////////////////////////////////////////////////////////////////////////
57// AS the GIET-VM uses a polling policy to detect transfer completion,
58// The DMA component initialisation must disable interrupts.
59// This function disables interrupts for one DMA channel in one cluster.
60// Returns 0 if success, returns > 0 if error.
61//////////////////////////////////////////////////////////////////////////////////
62unsigned int _dma_init( unsigned int cluster_xy,
63                        unsigned int channel_id )
64{
65#if NB_DMA_CHANNELS > 0
66
67    // parameters checking
68    unsigned int x = cluster_xy >> Y_WIDTH;
69    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
70    if (x >= X_SIZE)                    return 1; 
71    if (y >= Y_SIZE)                    return 1; 
72    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
73
74    // compute DMA base address
75    unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + 
76                                (cluster_xy * (unsigned int)&vseg_cluster_increment));
77
78    // disable interrupt for selected channel
79    dma_address[channel_id * DMA_SPAN + DMA_IRQ_DISABLE] = 1;           
80    return 0;
81#else
82    return 1;
83#endif
84}
85
86//////////////////////////////////////////////////////////////////////////////////
87// This function re-initialises one DMA channel in one cluster after a transfer
88// completion. It actually forces the channel to return in iDLE state.
89//////////////////////////////////////////////////////////////////////////////////
90unsigned int _dma_reset( unsigned int cluster_xy, 
91                         unsigned int channel_id ) 
92{
93#if NB_DMA_CHANNELS > 0
94
95    // parameters checking
96    unsigned int x = cluster_xy >> Y_WIDTH;
97    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
98    if (x >= X_SIZE)                    return 1; 
99    if (y >= Y_SIZE)                    return 1; 
100    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
101
102    // compute DMA base address
103    unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + 
104                                (cluster_xy * (unsigned int)&vseg_cluster_increment));
105
106    // reset selected channel
107    dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0;           
108    return 0;
109#else
110    return 1;
111#endif
112}
113
114//////////////////////////////////////////////////////////////////////////////////
115// This function returns the status of a DMA channel in a given cluster
116//////////////////////////////////////////////////////////////////////////////////
117unsigned int _dma_get_status( unsigned int cluster_xy, 
118                              unsigned int channel_id ) 
119{
120#if NB_DMA_CHANNELS > 0
121
122    // parameters checking
123    unsigned int x = cluster_xy >> Y_WIDTH;
124    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
125    if (x >= X_SIZE)                    return 1; 
126    if (y >= Y_SIZE)                    return 1; 
127    if (channel_id >= NB_DMA_CHANNELS)  return 1;
128
129    // compute DMA base address
130    unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + 
131                                 (cluster_xy * (unsigned int)&vseg_cluster_increment));
132
133    // get selected channel status
134    return dma_address[channel_id * DMA_SPAN + DMA_LEN];
135#else
136    return DMA_IDLE;
137#endif
138}
139
140//////////////////////////////////////////////////////////////////////////////////
141// This function sets the physical address (including 64 bits extension)
142// for the source and destination buffers in a DMA channel in a given cluster
143// and sets the transfer size to lauch the transfer.
144//////////////////////////////////////////////////////////////////////////////////
145unsigned int _dma_start_transfer( unsigned int       cluster_xy,
146                                  unsigned int       channel_id,
147                                  unsigned long long dst_paddr,   // physical address
148                                  unsigned long long src_paddr,   // physical address
149                                  unsigned int       size )       // bytes
150{
151#if NB_DMA_CHANNELS > 0
152
153    // parameters checking
154    unsigned int x = cluster_xy >> Y_WIDTH;
155    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
156    if (x >= X_SIZE)                    return 1; 
157    if (y >= Y_SIZE)                    return 1; 
158    if (channel_id >= NB_DMA_CHANNELS)  return 1;
159
160    // compute DMA base address
161    unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + 
162                                 (cluster_xy * (unsigned int)&vseg_cluster_increment));
163
164    // selected channel configuration and lauching
165    dma_address[channel_id * DMA_SPAN + DMA_SRC]     = (unsigned int)(src_paddr);
166    dma_address[channel_id * DMA_SPAN + DMA_SRC_EXT] = (unsigned int)(src_paddr>>32);
167    dma_address[channel_id * DMA_SPAN + DMA_DST]     = (unsigned int)(dst_paddr);
168    dma_address[channel_id * DMA_SPAN + DMA_DST_EXT] = (unsigned int)(dst_paddr>>32);
169    dma_address[channel_id * DMA_SPAN + DMA_LEN]     = (unsigned int)size;
170    return 0;
171#else
172    return 1;
173#endif
174}
175///////////////////////////////////////////////////////////////////////////////////
176// This function copies a source memory buffer to a destination memory buffer,
177// using the distributed DMA. As it makes virtual to physical address translation,
178// the MMU should be activated.
179// This driver makes the assumption that each processor has a private DMA channel:
180// the DMA cluster and channel indexes are obtained from the processor index.
181// The source and destination buffers base addresses must be word aligned,
182// and the buffer's size must be multiple of 4.
183// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
184// message is displayed on TTY0, and the system crash.
185///////////////////////////////////////////////////////////////////////////////////
186// Note: this blocking function is supposed to be used by the kernel only,
187// and uses a polling policy on DMA_STATUS register to detect completion.
188// Therefore, the DMA_IRQ is NOT used.
189///////////////////////////////////////////////////////////////////////////////////
190inline void  _dma_copy( unsigned int vspace_id, // vspace index for V2P
191                        void*        dest,      // dest buffer vbase
192                        const void*  source,    // source buffer vbase
193                        unsigned int size )     // bytes
194{
195#if NB_DMA_CHANNELS > 0
196
197    unsigned int procid     = _get_procid();
198    unsigned int cluster_xy = procid/NB_PROCS_MAX;
199    unsigned int channel_id = procid%NB_PROCS_MAX;
200
201    unsigned int ko;
202    unsigned int ppn;
203    unsigned int flags;
204
205#if GIET_DEBUG_DMA_DRIVER
206unsigned int x          = cluster_xy >> Y_WIDTH;
207unsigned int y          = cluster_xy & ((1<<Y_WIDTH)-1);
208
209_printf("\n[DMA DEBUG] Processor[%d,%d,%d] enters _dma_copy() at cycle %d\n"
210        " - vspace_id   = %d\n"
211        " - src_vaddr   = %x\n"
212        " - dst_vaddr   = %x\n"
213        " - bytes       = %x\n",
214        x, y, channel_id, _get_proctime(), vspace_id, 
215        (unsigned int)source, (unsigned int)dest, size );
216#endif
217
218    // checking alignment constraints
219    if ( (((unsigned int)dest) & 0x3)   ||
220         (((unsigned int)source) & 0x3) ||
221         (size & 0x3) )
222    {
223        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
224        _exit();
225    }
226
227    // get vspace 0 page table pointer
228    unsigned int pt =  _ptabs_vaddr[vspace_id];
229
230    // get source buffer physical addresse
231    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
232                         ((unsigned int)source)>>12,     // vpn
233                         &ppn,                           // ppn
234                         &flags );                       // flags
235    if ( ko ) 
236    {
237        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
238        _exit();
239    }
240    unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | 
241                                   ((unsigned int)source & 0x00000FFF);
242
243    // get dest buffer physical addresse
244    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
245                         ((unsigned int)dest)>>12,       // vpn
246                         &ppn,                           // ppn
247                         &flags );                       // flags
248    if ( ko ) 
249    {
250        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
251        _exit();
252    }
253    unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | 
254                                   ((unsigned int)dest & 0x00000FFF);
255
256#if GIET_DEBUG_DMA_DRIVER
257_printf(" - src_paddr   = %l\n"
258        " - dst_paddr   = %l\n",
259        src_paddr, dst_paddr );
260#endif
261
262    // invalidate L1 cache if no hardware cache coherence
263    if ( GIET_NO_HARD_CC ) _dcache_buf_invalidate( dest, size );
264
265    // dma channel configuration & lauching
266    ko = _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
267    if ( ko ) 
268    {
269        _printf("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n");
270        _exit();
271    }
272
273    // scan dma channel status
274    unsigned int status = _dma_get_status( cluster_xy, channel_id );
275    while( (status != DMA_SUCCESS) && 
276           (status != DMA_READ_ERROR) &&
277           (status != DMA_WRITE_ERROR) )
278    {
279        status = _dma_get_status( cluster_xy, channel_id );
280
281#if GIET_DEBUG_DMA_DRIVER
282_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n");
283#endif
284
285    }
286   
287    // analyse status
288    if( status != DMA_SUCCESS )
289    {
290        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
291        _exit();
292    }
293    // reset dma channel
294    _dma_reset( cluster_xy, channel_id );
295
296#if GIET_DEBUG_DMA_DRIVER
297_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
298#endif
299
300#else // NB_DMA_CHANNELS == 0
301    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n");
302    _exit();
303#endif
304} // end _dma_copy
305
306
307
308
309// tab-width: 4
310// c-basic-offset: 4
311// c-file-offsets:((innamespace . 0)(inline-open . 0))
312// indent-tabs-mode: nil
313// End:
314// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
315
Note: See TracBrowser for help on using the repository browser.