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

Last change on this file since 407 was 345, checked in by cfuguet, 11 years ago

giet_vm optimizations:

  • Several modifications in GIET_VM in order to support compilation with GCC optimizations (-O2) activated.
  • Adding missing volatile in some global variables.
  • Using ioread and iowrite utility functions in peripheral drivers which prevent GCC to remove writes or reads in hardware memory mapped registers.
  • Code refactoring of stdio printf functions. Now, shr_printf and tty_printf function reuse the same function body. The only difference is that shr_printf wraps printf function call with TTY get lock and release lock.
File size: 15.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.
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#include <io.h>
35
36#if !defined(X_SIZE)
37# error: You must define X_SIZE in the hard_config.h file
38#endif
39
40#if !defined(Y_SIZE)
41# error: You must define X_SIZE in the hard_config.h file
42#endif
43
44#if !defined(X_WIDTH)
45# error: You must define X_WIDTH in the hard_config.h file
46#endif
47
48#if !defined(Y_WIDTH)
49# error: You must define X_WIDTH in the hard_config.h file
50#endif
51
52#if !defined(NB_DMA_CHANNELS)
53# error: You must define NB_DMA_CHANNELS in the hard_config.h file
54#endif
55
56#if !defined(SEG_DMA_BASE)
57# error: You must define SEG_DMA_BASE in the hard_config.h file
58#endif
59
60#if !defined(PERI_CLUSTER_INCREMENT)
61# error: You must define PERI_CLUSTER_INCREMENT in the hard_config.h file
62#endif
63
64extern volatile unsigned int _ptabs_vaddr[];
65
66///////////////////////////////////////////////////////////////////////////////
67// This low level function returns the value contained in register "index"
68// in the DMA component contained in cluster "cluster_xy"
69///////////////////////////////////////////////////////////////////////////////
70static
71unsigned int _dma_get_register( unsigned int cluster_xy, // cluster index
72                                unsigned int channel_id, // channel index
73                                unsigned int index )     // register index
74{
75    unsigned int vaddr =
76        SEG_DMA_BASE + 
77        (cluster_xy * PERI_CLUSTER_INCREMENT) +
78        (channel_id * DMA_SPAN) +
79        (index << 2);
80
81    return ioread32( (void*)vaddr );
82}
83
84///////////////////////////////////////////////////////////////////////////////
85// This low level function sets a new value in register "index"
86// in the DMA component contained in cluster "cluster_xy"
87///////////////////////////////////////////////////////////////////////////////
88static
89void _dma_set_register( unsigned int cluster_xy,       // cluster index
90                        unsigned int channel_id,       // channel index
91                        unsigned int index,            // register index
92                        unsigned int value )           // value to be written
93{
94    unsigned int vaddr =
95        SEG_DMA_BASE + 
96        (cluster_xy * PERI_CLUSTER_INCREMENT) +
97        (channel_id * DMA_SPAN) +
98        (index << 2);
99
100    iowrite32( (void*)vaddr, value );
101}
102
103//////////////////////////////////////////////////////////////////////////////////
104// AS the GIET-VM uses a polling policy to detect transfer completion,
105// The DMA component initialisation must disable interrupts.
106// This function disables interrupts for one DMA channel in one cluster.
107// Returns 0 if success, returns > 0 if error.
108//////////////////////////////////////////////////////////////////////////////////
109unsigned int _dma_init( unsigned int cluster_xy,
110                        unsigned int channel_id )
111{
112#if NB_DMA_CHANNELS > 0
113
114    // parameters checking
115    unsigned int x = cluster_xy >> Y_WIDTH;
116    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
117    if (x >= X_SIZE)                    return 1; 
118    if (y >= Y_SIZE)                    return 1; 
119    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
120
121    // disable interrupt for selected channel
122    _dma_set_register(cluster_xy, channel_id, DMA_IRQ_DISABLE, 1);
123    return 0;
124#else
125    return 1;
126#endif
127}
128
129//////////////////////////////////////////////////////////////////////////////////
130// This function re-initialises one DMA channel in one cluster after a transfer
131// completion. It actually forces the channel to return in iDLE state.
132//////////////////////////////////////////////////////////////////////////////////
133unsigned int _dma_reset( unsigned int cluster_xy, 
134                         unsigned int channel_id ) 
135{
136#if NB_DMA_CHANNELS > 0
137
138    // parameters checking
139    unsigned int x = cluster_xy >> Y_WIDTH;
140    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
141    if (x >= X_SIZE)                    return 1; 
142    if (y >= Y_SIZE)                    return 1; 
143    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
144
145    // reset selected channel
146    _dma_set_register(cluster_xy, channel_id, DMA_RESET, 0);
147    return 0;
148#else
149    return 1;
150#endif
151}
152
153//////////////////////////////////////////////////////////////////////////////////
154// This function returns the status of a DMA channel in a given cluster
155//////////////////////////////////////////////////////////////////////////////////
156unsigned int _dma_get_status( unsigned int cluster_xy, 
157                              unsigned int channel_id ) 
158{
159#if NB_DMA_CHANNELS > 0
160
161    // parameters checking
162    unsigned int x = cluster_xy >> Y_WIDTH;
163    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
164    if (x >= X_SIZE)                    return 1; 
165    if (y >= Y_SIZE)                    return 1; 
166    if (channel_id >= NB_DMA_CHANNELS)  return 1;
167
168    // get selected channel status
169    return _dma_get_register(cluster_xy, channel_id, DMA_LEN);
170#else
171    return DMA_IDLE;
172#endif
173}
174
175//////////////////////////////////////////////////////////////////////////////////
176// This function sets the physical address (including 64 bits extension)
177// for the source and destination buffers in a DMA channel in a given cluster
178// and sets the transfer size to lauch the transfer.
179//////////////////////////////////////////////////////////////////////////////////
180void _dma_start_transfer( unsigned int       cluster_xy,  // DMA cluster
181                          unsigned int       channel_id,  // DMA channel
182                          unsigned long long dst_paddr,   // physical address
183                          unsigned long long src_paddr,   // physical address
184                          unsigned int       size )       // bytes
185{
186#if NB_DMA_CHANNELS > 0
187
188    // selected channel configuration and lauching
189    _dma_set_register(cluster_xy, channel_id, DMA_SRC,
190            (unsigned int)(src_paddr));
191    _dma_set_register(cluster_xy, channel_id, DMA_SRC_EXT,
192            (unsigned int)(src_paddr>>32));
193    _dma_set_register(cluster_xy, channel_id, DMA_DST,
194            (unsigned int)(dst_paddr));
195    _dma_set_register(cluster_xy, channel_id, DMA_DST_EXT,
196            (unsigned int)(dst_paddr>>32));
197    _dma_set_register(cluster_xy, channel_id, DMA_LEN,
198            (unsigned int)size);
199
200#endif
201}
202
203///////////////////////////////////////////////////////////////////////////////////
204// This function copies a source memory buffer to a destination memory buffer,
205// using directly physical addresses.
206// This blocking function is supposed to be used by the kernel only,
207// and uses a polling policy on DMA_STATUS register to detect completion.
208// Therefore, the DMA_IRQ is NOT used.
209// The source and destination buffers base addresses must be word aligned,
210// and the buffer's size must be multiple of 4.
211// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
212// message is displayed on TTY0, and the system crash.
213///////////////////////////////////////////////////////////////////////////////////
214void _dma_physical_copy( unsigned int       cluster_xy,  // DMA cluster
215                         unsigned int       channel_id,  // DMA channel
216                         unsigned long long dst_paddr,   // destination physical address
217                         unsigned long long src_paddr,   // source physical address
218                         unsigned int       size )       // bytes
219{
220#if NB_DMA_CHANNELS > 0
221
222    // check DMA channel parameters
223    unsigned int x = cluster_xy >> Y_WIDTH;
224    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
225    if ( (x >= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) )
226    {
227        _printf("\n[GIET ERROR] in _dma_physical_copy() : illegal DMA channel ");
228        _exit();
229    }
230
231    // check buffers alignment constraints
232    if ( (dst_paddr & 0x3)   || (src_paddr & 0x3) || (size & 0x3) )
233    {
234        _printf("\n[GIET ERROR] in _dma_physical_copy() : buffer unaligned\n");
235        _exit();
236    }
237
238#if GIET_DEBUG_DMA_DRIVER
239_printf("\n[DMA DEBUG] Start a dma_physical_copy on channel[%d,%d,%d] at cycle %d\n"
240        " - src_paddr   = %l\n"
241        " - dst_paddr   = %l\n"
242        " - bytes       = %x\n",
243        x, y, channel_id, _get_proctime(), src_paddr, dst_paddr, size );
244#endif
245
246    // dma channel configuration & lauching
247    _dma_start_transfer( cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
248
249    // scan dma channel status
250    unsigned int status = _dma_get_status( cluster_xy, channel_id );
251    while( (status != DMA_SUCCESS) && 
252           (status != DMA_READ_ERROR) &&
253           (status != DMA_WRITE_ERROR) )
254    {
255        status = _dma_get_status( cluster_xy, channel_id );
256
257#if GIET_DEBUG_DMA_DRIVER
258_printf("\n[DMA DEBUG] _dma_physical_copy() : ... waiting on DMA_STATUS register\n");
259#endif
260
261    }
262   
263    // analyse status
264    if( status != DMA_SUCCESS )
265    {
266        _printf("\n[GIET ERROR] in _dma_physical_copy() : DMA_STATUS = %x\n", status );
267        _exit();
268    }
269    // reset dma channel
270    _dma_reset( cluster_xy, channel_id );
271
272#if GIET_DEBUG_DMA_DRIVER
273_printf("\n[DMA DEBUG] _dma_physical_copy() completed at cycle %d\n", _get_proctime() );
274#endif
275
276#else // NB_DMA_CHANNELS == 0
277    _printf("\n[GIET ERROR] in _dma_physical_copy() : NB_DMA_CHANNELS == 0 / cycle %d\n",
278            _get_proctime );
279    _exit();
280#endif
281}
282
283///////////////////////////////////////////////////////////////////////////////////
284// This function copies a source memory buffer to a destination memory buffer,
285// making virtual to physical address translation: the MMU should be activated.
286// This blocking function is supposed to be used by the kernel only,
287// and uses a polling policy on DMA_STATUS register to detect completion.
288// Therefore, the DMA_IRQ is NOT used.
289// The source and destination buffers base addresses must be word aligned,
290// and the buffer's size must be multiple of 4.
291// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
292// message is displayed on TTY0, and the system crash.
293///////////////////////////////////////////////////////////////////////////////////
294void  _dma_copy( unsigned int cluster_xy,    // DMA cluster
295                 unsigned int channel_id,    // DMA channel
296                 unsigned int vspace_id,     // vspace index for v2p translation
297                 unsigned int dst_vaddr,     // dst_vaddr buffer vbase
298                 unsigned int src_vaddr,     // src_vaddr buffer vbase
299                 unsigned int size )         // bytes
300{
301#if NB_DMA_CHANNELS > 0
302
303    // check DMA channel parameters
304    unsigned int x = cluster_xy >> Y_WIDTH;
305    unsigned int y = cluster_xy & ((1<<Y_WIDTH)-1);
306    if ( (x >= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) )
307    {
308        _printf("\n[GIET ERROR] in _dma_copy() : illegal DMA channel ");
309        _exit();
310    }
311
312    // check buffers alignment constraints
313    if ( (dst_vaddr & 0x3)   || (src_vaddr & 0x3) || (size & 0x3) )
314    {
315        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
316        _exit();
317    }
318
319    unsigned int ko;
320    unsigned int ppn;
321    unsigned int flags;
322
323#if GIET_DEBUG_DMA_DRIVER
324_printf("\n[DMA DEBUG] Start a dma_copy on channel[%d,%d,%d] at cycle %d\n"
325        " - src_vaddr   = %x\n"
326        " - dst_vaddr   = %x\n"
327        " - bytes       = %x\n",
328        x, y, channel_id, _get_proctime(), src_vaddr, dst_vaddr, size );
329#endif
330
331    // checking alignment constraints
332    if ( (((unsigned int)dst_vaddr) & 0x3) ||
333         (((unsigned int)src_vaddr) & 0x3) ||
334         (size & 0x3) )
335    {
336        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
337        _exit();
338    }
339
340    // get vspace page table pointer
341    unsigned int pt = _ptabs_vaddr[vspace_id];
342
343    // get src_paddr buffer physical addresse
344    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
345                         src_vaddr>>12,                  // vpn
346                         &ppn,                           // ppn
347                         &flags );                       // flags
348    if ( ko ) 
349    {
350        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
351        _exit();
352    }
353    unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | 
354                                   (unsigned long long)(src_vaddr & 0x00000FFF);
355
356    // get dst_paddr buffer physical addresse
357    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
358                         dst_vaddr>>12,                  // vpn
359                         &ppn,                           // ppn
360                         &flags );                       // flags
361    if ( ko ) 
362    {
363        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
364        _exit();
365    }
366    unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | 
367                                   (unsigned long long)(dst_vaddr & 0x00000FFF);
368
369#if GIET_DEBUG_DMA_DRIVER
370_printf(" - src_paddr   = %l\n"
371        " - dst_paddr   = %l\n",
372        src_paddr, dst_paddr );
373#endif
374
375    // dma channel configuration & lauching
376    _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
377
378    // scan dma channel status
379    unsigned int status = _dma_get_status( cluster_xy, channel_id );
380    while( (status != DMA_SUCCESS) && 
381           (status != DMA_READ_ERROR) &&
382           (status != DMA_WRITE_ERROR) )
383    {
384        status = _dma_get_status( cluster_xy, channel_id );
385
386#if GIET_DEBUG_DMA_DRIVER
387_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register\n");
388#endif
389
390    }
391   
392    // analyse status
393    if( status != DMA_SUCCESS )
394    {
395        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
396        _exit();
397    }
398    // reset dma channel
399    _dma_reset( cluster_xy, channel_id );
400
401#if GIET_DEBUG_DMA_DRIVER
402_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
403#endif
404
405#else // NB_DMA_CHANNELS == 0
406    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS == 0 / cycle %d\n",
407            _get_proctime );
408    _exit();
409#endif
410} // end _dma_copy
411
412///////////////////////////////////////////////////////////////////////////////
413// This ISR handles the IRQ generated by a DMA channel.
414///////////////////////////////////////////////////////////////////////////////
415void _dma_isr( unsigned int irq_type,
416               unsigned int irq_id,
417               unsigned int channel )
418{
419    _printf("\n[GIET ERROR] _dma_isr() not implemented / cycle %d\n",
420            _get_proctime() );
421    _exit();
422}
423
424
425
426
427// tab-width: 4
428// c-basic-offset: 4
429// c-file-offsets:((innamespace . 0)(inline-open . 0))
430// indent-tabs-mode: nil
431// End:
432// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
433
Note: See TracBrowser for help on using the repository browser.