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

Last change on this file since 331 was 320, checked in by alain, 10 years ago

All drivers have been modified to use only the information
contained in the hard_config.h file

File size: 12.3 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
[320]20// transfer completion, the DMA IRQ is not used.
21//
[258]22// The virtual base address of the segment associated to a channel is:
[320]23//    SEG_DMA_BASE + cluster_xy * vseg_cluster_increment + DMA_SPAN * channel_id
[258]24//
[320]25// The SEG_DMA_BASE virtual address mus be defined in the hard_config.h file.
[258]26////////////////////////////////////////////////////////////////////////////////////
27
28#include <giet_config.h>
[320]29#include <hard_config.h>
[258]30#include <dma_driver.h>
31#include <tty_driver.h>
32#include <vmem.h>
[320]33#include <utils.h>
[258]34
[263]35#if !defined(X_SIZE)
36# error: You must define X_SIZE in the hard_config.h file
[258]37#endif
38
[263]39#if !defined(Y_SIZE)
40# error: You must define X_SIZE in the hard_config.h file
[258]41#endif
42
[263]43#if !defined(X_WIDTH)
44# error: You must define X_WIDTH in the hard_config.h file
[258]45#endif
46
[263]47#if !defined(Y_WIDTH)
48# error: You must define X_WIDTH in the hard_config.h file
[258]49#endif
50
[263]51#if !defined(NB_DMA_CHANNELS)
52# error: You must define NB_DMA_CHANNELS in the hard_config.h file
[258]53#endif
54
[320]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(VSEG_CLUSTER_INCREMENT)
60# error: You must define VSEG_CLUSTER_INCREMENT in the hard_config.h file
61#endif
62
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]71unsigned int _dma_init( unsigned int cluster_xy,
[258]72                        unsigned int channel_id )
73{
74#if NB_DMA_CHANNELS > 0
[263]75
[258]76    // parameters checking
[263]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; 
[258]81    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
82
83    // compute DMA base address
[320]84    unsigned int* dma_address = (unsigned int*) ( SEG_DMA_BASE + 
85                                (cluster_xy * VSEG_CLUSTER_INCREMENT) );
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]99unsigned int _dma_reset( unsigned int cluster_xy, 
[258]100                         unsigned int channel_id ) 
101{
102#if NB_DMA_CHANNELS > 0
[263]103
[258]104    // parameters checking
[263]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; 
[258]109    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
110
111    // compute DMA base address
[320]112    unsigned int* dma_address = (unsigned int*) ( SEG_DMA_BASE + 
113                                (cluster_xy * VSEG_CLUSTER_INCREMENT) );
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]126unsigned int _dma_get_status( unsigned int cluster_xy, 
[258]127                              unsigned int channel_id ) 
128{
129#if NB_DMA_CHANNELS > 0
[263]130
[258]131    // parameters checking
[263]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; 
[258]136    if (channel_id >= NB_DMA_CHANNELS)  return 1;
137
138    // compute DMA base address
[320]139    unsigned int * dma_address = (unsigned int *) ( SEG_DMA_BASE + 
140                                 (cluster_xy * VSEG_CLUSTER_INCREMENT) );
[258]141
142    // get selected channel status
143    return dma_address[channel_id * DMA_SPAN + DMA_LEN];
144#else
[267]145    return DMA_IDLE;
[258]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//////////////////////////////////////////////////////////////////////////////////
[263]154unsigned int _dma_start_transfer( unsigned int       cluster_xy,
[258]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
[263]161
[258]162    // parameters checking
[263]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; 
[258]167    if (channel_id >= NB_DMA_CHANNELS)  return 1;
168
169    // compute DMA base address
[320]170    unsigned int * dma_address = (unsigned int *) ( SEG_DMA_BASE + 
171                                 (cluster_xy * VSEG_CLUSTER_INCREMENT) );
[258]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///////////////////////////////////////////////////////////////////////////////////
[298]185// This function copies a source memory buffer to a destination memory buffer,
[258]186// using the distributed DMA. As it makes virtual to physical address translation,
[298]187// the MMU should be activated.
188// This driver makes the assumption that each processor has a private DMA channel:
[258]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
[313]206    unsigned int procid     = _get_procid();
[263]207    unsigned int cluster_xy = procid/NB_PROCS_MAX;
[258]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
[295]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"
[313]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 );
[258]225#endif
226
227    // checking alignment constraints
228    if ( (((unsigned int)dest) & 0x3)   ||
229         (((unsigned int)source) & 0x3) ||
230         (size & 0x3) )
231    {
[295]232        _printf("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
[258]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    {
[295]246        _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
[258]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    {
[295]259        _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
[258]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
[313]266_printf(" - src_paddr   = %l\n"
267        " - dst_paddr   = %l\n",
[295]268        src_paddr, dst_paddr );
[258]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
[263]275    ko = _dma_start_transfer(  cluster_xy, channel_id, dst_paddr, src_paddr, size ); 
[258]276    if ( ko ) 
277    {
[295]278        _printf("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n");
[258]279        _exit();
280    }
281
282    // scan dma channel status
[263]283    unsigned int status = _dma_get_status( cluster_xy, channel_id );
[258]284    while( (status != DMA_SUCCESS) && 
285           (status != DMA_READ_ERROR) &&
286           (status != DMA_WRITE_ERROR) )
287    {
[263]288        status = _dma_get_status( cluster_xy, channel_id );
[258]289
290#if GIET_DEBUG_DMA_DRIVER
[295]291_printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n");
[258]292#endif
293
294    }
295   
296    // analyse status
297    if( status != DMA_SUCCESS )
298    {
[295]299        _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status );
[258]300        _exit();
301    }
302    // reset dma channel
[263]303    _dma_reset( cluster_xy, channel_id );
[258]304
305#if GIET_DEBUG_DMA_DRIVER
[295]306_printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() );
[258]307#endif
308
309#else // NB_DMA_CHANNELS == 0
[295]310    _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n");
[258]311    _exit();
312#endif
313} // end _dma_copy
314
[320]315///////////////////////////////////////////////////////////////////////////////
316// This ISR handles the IRQ generated by a DMA channel.
317///////////////////////////////////////////////////////////////////////////////
318void _dma_isr( unsigned int irq_type,
319               unsigned int irq_id,
320               unsigned int channel )
321{
322    _printf("\n[GIET ERROR] _dma_isr() not implemented\n");
323    _exit();
324}
[258]325
326
327
[320]328
[258]329// tab-width: 4
330// c-basic-offset: 4
331// c-file-offsets:((innamespace . 0)(inline-open . 0))
332// indent-tabs-mode: nil
333// End:
334// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
335
Note: See TracBrowser for help on using the repository browser.