/* ================================================================== >>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< ------------------------------------------------------------------ Copyright (c) 2019-2020 by Lattice Semiconductor Corporation ALL RIGHTS RESERVED ------------------------------------------------------------------ IMPORTANT: THIS FILE IS USED BY OR GENERATED BY the LATTICE PROPELâ„¢ DEVELOPMENT SUITE, WHICH INCLUDES PROPEL BUILDER AND PROPEL SDK. Lattice grants permission to use this code pursuant to the terms of the Lattice Propel License Agreement. DISCLAIMER: LATTICE MAKES NO WARRANTIES ON THIS FILE OR ITS CONTENTS, WHETHER EXPRESSED, IMPLIED, STATUTORY, OR IN ANY PROVISION OF THE LATTICE PROPEL LICENSE AGREEMENT OR COMMUNICATION WITH LICENSEE, AND LATTICE SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. LATTICE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED HEREIN WILL MEET LICENSEE 'S REQUIREMENTS, OR THAT LICENSEE' S OPERATION OF ANY DEVICE, SOFTWARE OR SYSTEM USING THIS FILE OR ITS CONTENTS WILL BE UNINTERRUPTED OR ERROR FREE, OR THAT DEFECTS HEREIN WILL BE CORRECTED. LICENSEE ASSUMES RESPONSIBILITY FOR SELECTION OF MATERIALS TO ACHIEVE ITS INTENDED RESULTS, AND FOR THE PROPER INSTALLATION, USE, AND RESULTS OBTAINED THEREFROM. LICENSEE ASSUMES THE ENTIRE RISK OF THE FILE AND ITS CONTENTS PROVING DEFECTIVE OR FAILING TO PERFORM PROPERLY AND IN SUCH EVENT, LICENSEE SHALL ASSUME THE ENTIRE COST AND RISK OF ANY REPAIR, SERVICE, CORRECTION, OR ANY OTHER LIABILITIES OR DAMAGES CAUSED BY OR ASSOCIATED WITH THE SOFTWARE.IN NO EVENT SHALL LATTICE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS FILE OR ITS CONTENTS, EVEN IF LATTICE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. LATTICE 'S SOLE LIABILITY, AND LICENSEE' S SOLE REMEDY, IS SET FORTH ABOVE. LATTICE DOES NOT WARRANT OR REPRESENT THAT THIS FILE, ITS CONTENTS OR USE THEREOF DOES NOT INFRINGE ON THIRD PARTIES' INTELLECTUAL PROPERTY RIGHTS, INCLUDING ANY PATENT. IT IS THE USER' S RESPONSIBILITY TO VERIFY THE USER SOFTWARE DESIGN FOR CONSISTENCY AND FUNCTIONALITY THROUGH THE USE OF FORMAL SOFTWARE VALIDATION METHODS. ------------------------------------------------------------------ ================================================================== */ #include "sgdma.h" // Global variable of buffer descriptor pointer // Serve as placeholder to keep track of starting buffer descriptor address static mm2s_desc_t * mm_multi_bd_confg_ptr; static s2mm_desc_t * s_multi_bd_confg_ptr; /** * sgdma_init * @brief Initialize the single and multi descriptor instances. * * @param *this_sgdma This structure pointer initilaize with the sgdma IP base address and number of descriptor value, have the m2s and s2mm BD address. * @param base_addr This parameter specify the sgdma IP base address. * * @return None. */ void sgdma_init(sgdma_instance_t * this_sgdma, unsigned int base_addr) { unsigned int idx = 0; this_sgdma->base_addr = base_addr; mm_multi_bd_confg_ptr = this_sgdma->mm2s_bd_addr; s_multi_bd_confg_ptr = this_sgdma->s2mm_bd_addr; } /** * sgdma_reset * @brief In this API reset the MM2S ctrl and S2MM cntrl register. * * @param *this_sgdma This structure pointer has the info of sgdma IP base address. * * @return none. */ void sgdma_reset(sgdma_instance_t * this_sgdma) { sgdma_ctrl_reg_t * mm2s_cntl_reg = (sgdma_ctrl_reg_t *)(this_sgdma->base_addr + MM2S_CTRL); sgdma_ctrl_reg_t * s2mm_cntl_reg = (sgdma_ctrl_reg_t *)(this_sgdma->base_addr + S2MM_CTRL); mm2s_cntl_reg->reset = SGDMA_RESET; s2mm_cntl_reg->reset = SGDMA_RESET; } /** * sgdma_irq_mask * @brief Mask and unmask the mm2s and s2mm interrupt request. * * @param *this_sgdma This structure pointer has the info of sgdma IP base address. * @param sgdma_cntl_type This parameter get the mm2s control offset and s2mm control offset. * @param mask_value This parameter describe mask and unmask value. * * @return None. */ void sgdma_irq_mask(sgdma_instance_t * this_sgdma, control_type_t sgdma_cntl_type, irq_mask_t mask_value) { sgdma_ctrl_reg_t * cntl_reg = (sgdma_ctrl_reg_t *)(this_sgdma->base_addr + sgdma_cntl_type); cntl_reg->cmpl_irq_mask = mask_value; } /** * get_sgdma_reg_status * @brief Get the mm2s or s2mm register status, bd len err, xfer_cmpl, xfer_err information. * * @param *this_sgdma This structure pointer has the info of sgdma IP base address. * @param sgdma_sts_type This parameter get the mm2s status offset and s2mm status offset as a parameter. * * @return This API return the mm2s status register address. */ sgdma_sts_reg_t * get_sgdma_reg_status(sgdma_instance_t * this_sgdma, status_type_t sgdma_sts_type) { sgdma_sts_reg_t *sts_reg = (sgdma_sts_reg_t *)(this_sgdma->base_addr + sgdma_sts_type); return sts_reg; } /** * get_mm2s_bd_status * @brief Get the mm2s BD status transffered len err, cmpl, slave_err, descriptor error information. * * @param idx This parameter indicates the index of the multiple buffer descriptor sequences. * * @return This API return the mm2s status register value. */ unsigned int get_mm2s_bd_status(int idx) { mm2s_desc_t * mm_multi_bd_confg_local_ptr = mm_multi_bd_confg_ptr; mm_multi_bd_confg_local_ptr += idx; return mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_status; } /** * get_s2mm_bd_status * @brief Get the s2mm BD status transffered len err, cmpl, slave_err, descriptor error information. * * @param idx This parameter indicates the index of the multiple buffer descriptor sequences. * * @return This API return the s2mm status register value. */ unsigned int get_s2mm_bd_status(int idx) { s2mm_desc_t * s_multi_bd_confg_local_ptr= s_multi_bd_confg_ptr; s_multi_bd_confg_local_ptr += idx; return s_multi_bd_confg_local_ptr->s_bd_ext.s_status; } /** * mm2s_get_desc_complete_bit * @brief Get the mm2s descriptor's status complete bit and error information. * * @param idx This parameter specifies the index for multiple buffer descriptor sequences. * * @return This API returns 0 on sucess and 1 for failure. */ unsigned int mm2s_get_desc_complete_bit(sgdma_instance_t * this_sgdma, unsigned int idx, unsigned char * isComplete, unsigned char * isError) { if(this_sgdma == IS_NULL) return FAILURE; if(idx >= this_sgdma->num_of_mm2s_desc) return FAILURE; mm2s_desc_t * mm_multi_bd_confg_local_ptr = mm_multi_bd_confg_ptr; mm_multi_bd_confg_local_ptr += idx; *isComplete = mm_multi_bd_confg_local_ptr->mm_bd.mm_status_cmpl; *isError = mm_multi_bd_confg_local_ptr->mm_bd.mm_status_bd_len_err | mm_multi_bd_confg_local_ptr->mm_bd.mm_status_transffered_len_err | mm_multi_bd_confg_local_ptr->mm_bd.mm_status_axi_slave_err | mm_multi_bd_confg_local_ptr->mm_bd.mm_status_axi_desc_err; return SUCCESS; } /** * mm2s_buf_desc_dma * @brief Performs a mm2s_buf_desc_dma API configure single and multiple buffer descriptor to update the registers value and trigger the DMA. * * @param *this_sgdma This structure pointer parameter has the info of base address of mm2s buffer, length and number of descriptor. * length is specify the buffer length and num_of_mm2s_desc is specify the single or multiple buffer descriptor value. * * @return This API returns 0 on success 1 on failure. */ unsigned int mm2s_buf_desc_dma(sgdma_instance_t * this_sgdma) { unsigned char isComplete = 0; unsigned char isError = 0; unsigned int idx = 0; unsigned int fp = 0; volatile unsigned int * mm_multi_wr_reg_value = (unsigned int *)(this_sgdma->base_addr + MM2S_CURDESC); mm2s_ctrl_reg_t * cntl_reg = (mm2s_ctrl_reg_t *)(this_sgdma->base_addr + MM2S_CTRL); mm2s_desc_t * mm_multi_bd_confg_local_ptr = mm_multi_bd_confg_ptr; for(idx = 0; idx < this_sgdma->num_of_mm2s_desc; idx++) { // The `mm2s_buffer_address` should point to an array that stores all the scatter memory locations. mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_buffer_addr = this_sgdma->mm2s_buffer_address[idx]; mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_buffer_msb_addr = 0; switch(this_sgdma->axi4_mm_data_width) { case 64: mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_buffer_addr &= 0xFFFFFFF8; break; case 128: mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_buffer_addr &= 0xFFFFFFF0; break; case 32: default: mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_buffer_addr &= 0xFFFFFFFC; break; } //Control fp = this_sgdma->fp; if (this_sgdma->per_mm2s_desc_length[idx] < MAX_TRANSFER_SIZE) { if(idx == (this_sgdma->num_of_mm2s_desc - 1)) { mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_control = (DESC_LAST << NXT_BIT_POSITION)|(fp << FP_BIT_POSITION)|(BUFFER_SIZE_MASK & this_sgdma->per_mm2s_desc_length[idx]); } else { mm_multi_bd_confg_local_ptr->mm_bd_ext.mm_control = (DESC_NEXT << NXT_BIT_POSITION)|(fp << FP_BIT_POSITION)|(BUFFER_SIZE_MASK & this_sgdma->per_mm2s_desc_length[idx]); } } else { return FAILURE; } mm_multi_bd_confg_local_ptr++; } //MM2S_CURDESC *mm_multi_wr_reg_value = (unsigned int)mm_multi_bd_confg_ptr; //DMA trigger cntl_reg->mm_request = DMA_TRIGGER; if(this_sgdma->blocking_mm2s == true) { idx = 0; for(idx = 0; idx < this_sgdma->num_of_mm2s_desc; idx++) { mm2s_get_desc_complete_bit(this_sgdma, idx, &isComplete, &isError); while(!isComplete | isError); } } return SUCCESS; } /** * s2mm_get_desc_complete_bit * @brief Get the s2mm descriptor's status complete bit and error information. * * @param idx This parameter specifies the index for multiple buffer descriptor sequences. * * @return This API returns 0 on sucess and 1 for failure. */ unsigned int s2mm_get_desc_complete_bit(sgdma_instance_t * this_sgdma, unsigned int idx, unsigned char * isComplete, unsigned char * isError) { if(this_sgdma == IS_NULL) return FAILURE; if(idx >= this_sgdma->num_of_s2mm_desc) return FAILURE; s2mm_desc_t * s_multi_bd_confg_local_ptr= s_multi_bd_confg_ptr; s_multi_bd_confg_local_ptr += idx; *isComplete = s_multi_bd_confg_local_ptr->s_bd.s_status_cmpl; *isError = s_multi_bd_confg_local_ptr->s_bd.s_status_bd_len_err | s_multi_bd_confg_local_ptr->s_bd.s_status_transffered_len_err | s_multi_bd_confg_local_ptr->s_bd.s_status_axi_slave_err | s_multi_bd_confg_local_ptr->s_bd.s_status_axi_desc_err; return SUCCESS; } /** * s2mm_buf_desc_dma * @brief Performs a s2mm_buf_desc_dma API configure single and multiple buffer descriptor to update the registers value and trigger the DMA. * * @param *this_sgdma This structure pointer parameter has the info of base address of s2mm buffer, length and number of descriptor. * length is specify the buffer length and num_of_s2mm_desc is specify the single or multiple buffer descriptor value. * * @return This API returns 0 on success and 1 for failure. */ unsigned int s2mm_buf_desc_dma(sgdma_instance_t * this_sgdma) { unsigned char isComplete = 0; unsigned char isError = 0; unsigned int idx = 0; volatile unsigned int * s_multi_wr_reg_value = (unsigned int *)(this_sgdma->base_addr + S2MM_CURDESC); volatile unsigned int * s_multi_rd_sts_reg_value = (unsigned int *)(this_sgdma->base_addr + S2MM_STS); s2mm_ctrl_reg_t * cntl_reg = (s2mm_ctrl_reg_t *)(this_sgdma->base_addr + S2MM_CTRL); s2mm_desc_t * s_multi_bd_confg_local_ptr= s_multi_bd_confg_ptr; for(idx = 0; idx < this_sgdma->num_of_s2mm_desc; idx++) { s_multi_bd_confg_local_ptr->s_bd_ext.s_buffer_addr = this_sgdma->s2mm_buffer_address[idx]; s_multi_bd_confg_local_ptr->s_bd_ext.s_buffer_msb_addr = 0; switch(this_sgdma->axi4_mm_data_width) { case 64: s_multi_bd_confg_local_ptr->s_bd_ext.s_buffer_addr &= 0xFFFFFFF8; break; case 128: s_multi_bd_confg_local_ptr->s_bd_ext.s_buffer_addr &= 0xFFFFFFF0; break; case 32: default: s_multi_bd_confg_local_ptr->s_bd_ext.s_buffer_addr &= 0xFFFFFFFC; break; } //Control if (this_sgdma->per_s2mm_desc_length[idx] < MAX_TRANSFER_SIZE) { if(idx == (this_sgdma->num_of_s2mm_desc - 1)) { s_multi_bd_confg_local_ptr->s_bd_ext.s_control = (DESC_LAST << NXT_BIT_POSITION)|(BUFFER_SIZE_MASK & this_sgdma->per_s2mm_desc_length[idx]); } else { s_multi_bd_confg_local_ptr->s_bd_ext.s_control = (DESC_NEXT << NXT_BIT_POSITION)|(BUFFER_SIZE_MASK & this_sgdma->per_s2mm_desc_length[idx]); } } else { return FAILURE; } s_multi_bd_confg_local_ptr++; } //S2MM_CURDESC *s_multi_wr_reg_value = (unsigned int)s_multi_bd_confg_ptr; //DMA trigger cntl_reg->s_request = DMA_TRIGGER; if(this_sgdma->blocking_s2mm == true) { for(idx = 0; idx <= this_sgdma->num_of_s2mm_desc; idx++) { s2mm_get_desc_complete_bit(this_sgdma, idx, &isComplete, &isError); while(!isComplete | isError); } } return SUCCESS; } /** * sgdma_register_read * @brief This API read from a specific mm2s or s2mm register based on the index value. * * @param *this_sgdma This structure pointer has the info of sgdma IP base address. * @param index It contains the offset value of particular register. * @param *reg_data Pointer to the variable where data to be stored. * * @return This API returns 0 on success and 1 for failure. */ unsigned int sgdma_register_read(sgdma_instance_t *this_sgdma, sgdma_reg_type_t index, unsigned int *reg_data) { if(this_sgdma == IS_NULL) { return FAILURE; } volatile unsigned int base_addr = (unsigned int)(this_sgdma->base_addr); volatile unsigned int *read_reg = (unsigned int *)(base_addr + index); *reg_data = *read_reg; return SUCCESS; } /** * sgdma_register_write * @brief This API writes data to a specific mm2s or s2mm register based on the index value. * * @param *this_sgdma This structure pointer has the info of sgdma IP base address. * @param index It contains the offset value of particular register. * @param reg_data It contains data that is write on the register address. * * @return This API returns 0 on success and 1 for failure. */ unsigned int sgdma_register_write(sgdma_instance_t *this_sgdma, sgdma_reg_type_t index, unsigned int reg_data) { if(this_sgdma == IS_NULL) { return FAILURE; } volatile unsigned int base_addr = (unsigned int)(this_sgdma->base_addr); volatile unsigned int *write_reg = (unsigned int *)(base_addr + index); *write_reg = reg_data; return SUCCESS; }