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

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

Cosmetic

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