/////////////////////////////////////////////////////////////////////////////////// // File : dma_driver.c // Date : 23/11/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // The dma_driver.c and dma_driver.h files are part ot the GIET-VM nano-kernel. // This driver supports the SoCLib vci_multi_dma component. // // It can exist several DMA controlers in the architecture (one per cluster), // and each controller can contain several channels. // // There is (NB_CLUSTERS * NB_DMA_CHANNELS) channels, indexed by a global index: // dma_id = cluster_xy * NB_DMA_CHANNELS + loc_id // // A DMA channel is a private ressource allocated to a given processor. // It is exclusively used by the kernet to speedup data transfers, and // there is no lock protecting exclusive access to the channel. // As the kernel uses a polling policy on the DMA_STATUS register to detect // transfer completion, the DMA IRQ is not used, and there is no DMA_ISR. //////////////////////////////////////////////////////////////////////////////////// // The virtual base address of the segment associated to a channel is: // // seg_dma_base + cluster_xy * vseg_cluster_increment + DMA_SPAN * channel_id // //////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #if !defined(X_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(Y_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(X_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(Y_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(NB_DMA_CHANNELS) # error: You must define NB_DMA_CHANNELS in the hard_config.h file #endif extern unsigned int _ptabs_vaddr[]; ////////////////////////////////////////////////////////////////////////////////// // AS the GIET-VM uses a polling policy to detect transfer completion, // The DMA component initialisation must disable interrupts. // This function disables interrupts for one DMA channel in one cluster. // Returns 0 if success, returns > 0 if error. ////////////////////////////////////////////////////////////////////////////////// unsigned int _dma_init( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // compute DMA base address unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); // disable interrupt for selected channel dma_address[channel_id * DMA_SPAN + DMA_IRQ_DISABLE] = 1; return 0; #else return 1; #endif } ////////////////////////////////////////////////////////////////////////////////// // This function re-initialises one DMA channel in one cluster after a transfer // completion. It actually forces the channel to return in iDLE state. ////////////////////////////////////////////////////////////////////////////////// unsigned int _dma_reset( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // compute DMA base address unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); // reset selected channel dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0; return 0; #else return 1; #endif } ////////////////////////////////////////////////////////////////////////////////// // This function returns the status of a DMA channel in a given cluster ////////////////////////////////////////////////////////////////////////////////// unsigned int _dma_get_status( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // compute DMA base address unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); // get selected channel status return dma_address[channel_id * DMA_SPAN + DMA_LEN]; #else return DMA_IDLE; #endif } ////////////////////////////////////////////////////////////////////////////////// // This function sets the physical address (including 64 bits extension) // for the source and destination buffers in a DMA channel in a given cluster // and sets the transfer size to lauch the transfer. ////////////////////////////////////////////////////////////////////////////////// unsigned int _dma_start_transfer( unsigned int cluster_xy, unsigned int channel_id, unsigned long long dst_paddr, // physical address unsigned long long src_paddr, // physical address unsigned int size ) // bytes { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // compute DMA base address unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + (cluster_xy * (unsigned int)&vseg_cluster_increment)); // selected channel configuration and lauching dma_address[channel_id * DMA_SPAN + DMA_SRC] = (unsigned int)(src_paddr); dma_address[channel_id * DMA_SPAN + DMA_SRC_EXT] = (unsigned int)(src_paddr>>32); dma_address[channel_id * DMA_SPAN + DMA_DST] = (unsigned int)(dst_paddr); dma_address[channel_id * DMA_SPAN + DMA_DST_EXT] = (unsigned int)(dst_paddr>>32); dma_address[channel_id * DMA_SPAN + DMA_LEN] = (unsigned int)size; return 0; #else return 1; #endif } /////////////////////////////////////////////////////////////////////////////////// // This function copies a source memory buffer to a destination memory buffer, // using the distributed DMA. As it makes virtual to physical address translation, // the MMU should be activated. // This driver makes the assumption that each processor has a private DMA channel: // the DMA cluster and channel indexes are obtained from the processor index. // The source and destination buffers base addresses must be word aligned, // and the buffer's size must be multiple of 4. // In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error // message is displayed on TTY0, and the system crash. /////////////////////////////////////////////////////////////////////////////////// // Note: this blocking function is supposed to be used by the kernel only, // and uses a polling policy on DMA_STATUS register to detect completion. // Therefore, the DMA_IRQ is NOT used. /////////////////////////////////////////////////////////////////////////////////// inline void _dma_copy( unsigned int vspace_id, // vspace index for V2P void* dest, // dest buffer vbase const void* source, // source buffer vbase unsigned int size ) // bytes { #if NB_DMA_CHANNELS > 0 unsigned int procid = _get_procid(); unsigned int cluster_xy = procid/NB_PROCS_MAX; unsigned int channel_id = procid%NB_PROCS_MAX; unsigned int ko; unsigned int ppn; unsigned int flags; #if GIET_DEBUG_DMA_DRIVER unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<>12, // vpn &ppn, // ppn &flags ); // flags if ( ko ) { _printf("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n"); _exit(); } unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | ((unsigned int)source & 0x00000FFF); // get dest buffer physical addresse ko = _v2p_translate( (page_table_t*)pt, // page table pointer ((unsigned int)dest)>>12, // vpn &ppn, // ppn &flags ); // flags if ( ko ) { _printf("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n"); _exit(); } unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | ((unsigned int)dest & 0x00000FFF); #if GIET_DEBUG_DMA_DRIVER _printf(" - src_paddr = %llx\n" " - dst_paddr = %llx\n", src_paddr, dst_paddr ); #endif // invalidate L1 cache if no hardware cache coherence if ( GIET_NO_HARD_CC ) _dcache_buf_invalidate( dest, size ); // dma channel configuration & lauching ko = _dma_start_transfer( cluster_xy, channel_id, dst_paddr, src_paddr, size ); if ( ko ) { _printf("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n"); _exit(); } // scan dma channel status unsigned int status = _dma_get_status( cluster_xy, channel_id ); while( (status != DMA_SUCCESS) && (status != DMA_READ_ERROR) && (status != DMA_WRITE_ERROR) ) { status = _dma_get_status( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _printf("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n"); #endif } // analyse status if( status != DMA_SUCCESS ) { _printf("\n[GIET ERROR] in _dma_copy() : DMA_STATUS = %x\n", status ); _exit(); } // reset dma channel _dma_reset( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _printf("\n[DMA DEBUG] _dma_copy() completed at cycle %d\n", _get_proctime() ); #endif #else // NB_DMA_CHANNELS == 0 _printf("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n"); _exit(); #endif } // end _dma_copy // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4