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

Last change on this file since 296 was 295, checked in by alain, 10 years ago

Introducing a major release, to suppoort the tsar_generic_leti platform
and the various (external or internal) peripherals configurations.
The map.xml format has been modified, in order to support the new
vci_iopic componentand a new policy for peripherals initialisation.
The IRQs are nom described in the XICU and IOPIC components
(and not anymore in the processors).
To enforce this major change, the map.xml file signature changed:
The signature value must be: 0xDACE2014

This new release has been tested on the tsar_generic_leti platform
for the following mappings:

  • 4c_4p_sort_leti
  • 4c_4p_sort_leti_ext
  • 4c_4p_transpose_leti
  • 4c_4p_transpose_leti_ext
  • 4c_1p_four_leti_ext
File size: 12.0 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 destnation memory buffer,
177// using the distributed DMA. As it makes virtual to physical address translation,
178// the MMU should be activated. As there is one DMA channel per processor,
179// the DMA cluster and channel indexes are obtained from the processor index.
180// The source and destination buffers base addresses must be word aligned,
181// and the buffer's size must be multiple of 4.
182// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
183// message is displayed on TTY0, and the system crash.
184///////////////////////////////////////////////////////////////////////////////////
185// Note: this blocking function is supposed to be used by the kernel only,
186// and uses a polling policy on DMA_STATUS register to detect completion.
187// Therefore, the DMA_IRQ is NOT used.
188///////////////////////////////////////////////////////////////////////////////////
189inline void  _dma_copy( unsigned int vspace_id, // vspace index for V2P
190                        void*        dest,      // dest buffer vbase
191                        const void*  source,    // source buffer vbase
192                        unsigned int size )     // bytes
193{
194#if NB_DMA_CHANNELS > 0
195
196    unsigned int procid    = _get_procid();
197    unsigned int cluster_xy = procid/NB_PROCS_MAX;
198    unsigned int x          = cluster_xy >> Y_WIDTH;
199    unsigned int y          = cluster_xy & ((1<<Y_WIDTH)-1);
200    unsigned int channel_id = procid%NB_PROCS_MAX;
201
202    unsigned int ko;
203    unsigned int ppn;
204    unsigned int flags;
205
206#if GIET_DEBUG_DMA_DRIVER
207unsigned int x          = cluster_xy >> Y_WIDTH;
208unsigned int y          = cluster_xy & ((1<<Y_WIDTH)-1);
209
210_printf("\n[DMA DEBUG] Processor[%d,%d,%d] enters _dma_copy() at cycle %d\n"
211        " - vspace_id  = %d\n"
212        " - cluster_xy = %x\n"
213        " - channel_id = %d\n"
214        " - dest       = %x\n"
215        " - source     = %x\n"
216        " - bytes      = %x\n",
217        x, y, channel_id, _get_proctime(), vspace_id, cluster_xy,
218        (unsigned int)dest, (unsigned int)source, size );
219#endif
220
221    // checking alignment constraints
222    if ( (((unsigned int)dest) & 0x3)   ||
223         (((unsigned int)source) & 0x3) ||
224         (size & 0x3) )
225    {
226        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
227        _exit();
228    }
229
230    // get vspace 0 page table pointer
231    unsigned int pt =  _ptabs_vaddr[vspace_id];
232
233    // get source buffer physical addresse
234    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
235                         ((unsigned int)source)>>12,     // vpn
236                         &ppn,                           // ppn
237                         &flags );                       // flags
238    if ( ko ) 
239    {
240        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
241        _exit();
242    }
243    unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | 
244                                   ((unsigned int)source & 0x00000FFF);
245
246    // get dest buffer physical addresse
247    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
248                         ((unsigned int)dest)>>12,       // vpn
249                         &ppn,                           // ppn
250                         &flags );                       // flags
251    if ( ko ) 
252    {
253        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
254        _exit();
255    }
256    unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | 
257                                   ((unsigned int)dest & 0x00000FFF);
258
259#if GIET_DEBUG_DMA_DRIVER
260_printf(" - src_paddr  = %llx\n"
261        " - dst_paddr  = %llx\n",
262        src_paddr, dst_paddr );
263#endif
264
265    // invalidate L1 cache if no hardware cache coherence
266    if ( GIET_NO_HARD_CC ) _dcache_buf_invalidate( dest, size );
267
268    // dma channel configuration & lauching
269    ko = _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
270    if ( ko ) 
271    {
272        _printf("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n");
273        _exit();
274    }
275
276    // scan dma channel status
277    unsigned int status = _dma_get_status( cluster_xy, channel_id );
278    while( (status != DMA_SUCCESS) && 
279           (status != DMA_READ_ERROR) &&
280           (status != DMA_WRITE_ERROR) )
281    {
282        status = _dma_get_status( cluster_xy, channel_id );
283
284#if GIET_DEBUG_DMA_DRIVER
285_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n");
286#endif
287
288    }
289   
290    // analyse status
291    if( status != DMA_SUCCESS )
292    {
293        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
294        _exit();
295    }
296    // reset dma channel
297    _dma_reset( cluster_xy, channel_id );
298
299#if GIET_DEBUG_DMA_DRIVER
300_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
301#endif
302
303#else // NB_DMA_CHANNELS == 0
304    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n");
305    _exit();
306#endif
307} // end _dma_copy
308
309
310
311
312// tab-width: 4
313// c-basic-offset: 4
314// c-file-offsets:((innamespace . 0)(inline-open . 0))
315// indent-tabs-mode: nil
316// End:
317// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
318
Note: See TracBrowser for help on using the repository browser.