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

Last change on this file since 317 was 313, checked in by alain, 10 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
RevLine 
[258]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:
[263]14//        dma_id = cluster_xy * NB_DMA_CHANNELS + loc_id
[258]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//
[263]24//    seg_dma_base + cluster_xy * vseg_cluster_increment + DMA_SPAN * channel_id
[258]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
[263]34#if !defined(X_SIZE)
35# error: You must define X_SIZE in the hard_config.h file
[258]36#endif
37
[263]38#if !defined(Y_SIZE)
39# error: You must define X_SIZE in the hard_config.h file
[258]40#endif
41
[263]42#if !defined(X_WIDTH)
43# error: You must define X_WIDTH in the hard_config.h file
[258]44#endif
45
[263]46#if !defined(Y_WIDTH)
47# error: You must define X_WIDTH in the hard_config.h file
[258]48#endif
49
[263]50#if !defined(NB_DMA_CHANNELS)
51# error: You must define NB_DMA_CHANNELS in the hard_config.h file
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]62unsigned int _dma_init( unsigned int cluster_xy,
[258]63                        unsigned int channel_id )
64{
65#if NB_DMA_CHANNELS > 0
[263]66
[258]67    // parameters checking
[263]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; 
[258]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 + 
[263]76                                (cluster_xy * (unsigned int)&vseg_cluster_increment));
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]90unsigned int _dma_reset( unsigned int cluster_xy, 
[258]91                         unsigned int channel_id ) 
92{
93#if NB_DMA_CHANNELS > 0
[263]94
[258]95    // parameters checking
[263]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; 
[258]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 + 
[263]104                                (cluster_xy * (unsigned int)&vseg_cluster_increment));
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]117unsigned int _dma_get_status( unsigned int cluster_xy, 
[258]118                              unsigned int channel_id ) 
119{
120#if NB_DMA_CHANNELS > 0
[263]121
[258]122    // parameters checking
[263]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; 
[258]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 + 
[263]131                                 (cluster_xy * (unsigned int)&vseg_cluster_increment));
[258]132
133    // get selected channel status
134    return dma_address[channel_id * DMA_SPAN + DMA_LEN];
135#else
[267]136    return DMA_IDLE;
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]145unsigned int _dma_start_transfer( unsigned int       cluster_xy,
[258]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
[263]152
[258]153    // parameters checking
[263]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; 
[258]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 + 
[263]162                                 (cluster_xy * (unsigned int)&vseg_cluster_increment));
[258]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///////////////////////////////////////////////////////////////////////////////////
[298]176// This function copies a source memory buffer to a destination memory buffer,
[258]177// using the distributed DMA. As it makes virtual to physical address translation,
[298]178// the MMU should be activated.
179// This driver makes the assumption that each processor has a private DMA channel:
[258]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
[313]197    unsigned int procid     = _get_procid();
[263]198    unsigned int cluster_xy = procid/NB_PROCS_MAX;
[258]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
[295]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"
[313]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 );
[258]216#endif
217
218    // checking alignment constraints
219    if ( (((unsigned int)dest) & 0x3)   ||
220         (((unsigned int)source) & 0x3) ||
221         (size & 0x3) )
222    {
[295]223        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
[258]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    {
[295]237        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
[258]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    {
[295]250        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
[258]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
[313]257_printf(" - src_paddr   = %l\n"
258        " - dst_paddr   = %l\n",
[295]259        src_paddr, dst_paddr );
[258]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
[263]266    ko = _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
[258]267    if ( ko ) 
268    {
[295]269        _printf("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n");
[258]270        _exit();
271    }
272
273    // scan dma channel status
[263]274    unsigned int status = _dma_get_status( cluster_xy, channel_id );
[258]275    while( (status != DMA_SUCCESS) && 
276           (status != DMA_READ_ERROR) &&
277           (status != DMA_WRITE_ERROR) )
278    {
[263]279        status = _dma_get_status( cluster_xy, channel_id );
[258]280
281#if GIET_DEBUG_DMA_DRIVER
[295]282_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n");
[258]283#endif
284
285    }
286   
287    // analyse status
288    if( status != DMA_SUCCESS )
289    {
[295]290        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
[258]291        _exit();
292    }
293    // reset dma channel
[263]294    _dma_reset( cluster_xy, channel_id );
[258]295
296#if GIET_DEBUG_DMA_DRIVER
[295]297_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
[258]298#endif
299
300#else // NB_DMA_CHANNELS == 0
[295]301    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n");
[258]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.