TWIHS + XDMAC

Go To Last Post
47 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all. The problem occurred when reading data through the TWIHS using XDMAC. The program given below:

Initialization of TWIHS interface.

uint32_t twi0_master_setup(twi_master_options_t *p_opt)
{
 stop_sent=0;
 twi0_port_state=-1;

 read_packet=0;
 write_packet=0; 
 
 uint32_t ul_sysclk;
 // Get system clock. 
 ul_sysclk = sysclk_get_cpu_hz(); 
 p_opt->master_clk = ul_sysclk;
 p_opt->smbus      = 0;
 
 sysclk_enable_peripheral_clock(ID_TWIHS0); 
 
 // Configure and enable interrupt of twi.
 NVIC_EnableIRQ((IRQn_Type)twi_id);
 NVIC_SetPriority((IRQn_Type)twi_id, TWI0_IRQ_level);
 twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK); 
 
 
 uint32_t status= twihs_master_init(twi_ptr, p_opt);
 if(status==TWIHS_SUCCESS) twi0_port_state=TWI_INIT;

 return status;
}
 /*Enable XDMA interrupt */
 NVIC_ClearPendingIRQ(XDMAC_IRQn);
 NVIC_SetPriority( XDMAC_IRQn ,1);
 NVIC_EnableIRQ(XDMAC_IRQn);
 
 twi_master_options_t opt = { opt.speed = 400000};
 twi0_master_setup(&opt);
void twi_master_r(twihs_packet_t *p_packet)
{
 twi_ptr->TWIHS_SR;
 read_packet=p_packet;
 write_packet=0;
 /* Check argument */
 if ((read_packet->length == 0) || (read_packet->buffer==0))
 {
  TWI_status=TWIHS_INVALID_ARGUMENT;
  return;
 }

 /* Set read mode, slave address and 3 internal address byte lengths */
 twi_ptr->TWIHS_MMR = 0;
 twi_ptr->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(read_packet->chip) |
 ((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
 TWIHS_MMR_IADRSZ_Msk);

 /* Set internal address for remote chip */
 twi_ptr->TWIHS_IADR = 0;
 twi_ptr->TWIHS_IADR = twihs_mk_addr(read_packet->addr, read_packet->addr_length);
 
 twi0_port_state=TWI_RECEIVING;

 if (read_packet->length > 2) 
 { 
  //Initialize RX channel config 
  xdmac_TWI0_RX_channel_cfg.mbr_ubc =(XDMAC_UBC_NVIEW_NDV0 |
           XDMAC_UBC_NDE_FETCH_DIS | //Next Descriptor Disable
           XDMAC_UBC_NDEN_UPDATED) | //Destination parameters are updated when the descriptor is retrieved 
           (read_packet->length-1);  

  xdmac_TWI0_RX_channel_cfg.mbr_sa = (uint32_t)twi_ptr->TWIHS_RHR; //Source Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_da = (uint32_t)read_packet->buffer; //Destination Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN  | //(XDMAC_CC) Synchronized mode (Peripheral to Memory or Memory to Peripheral Transfer).
           XDMAC_CC_MEMSET_NORMAL_MODE | //(XDMAC_CC) Memset is not activated
           XDMAC_CC_MBSIZE_SINGLE  | //(XDMAC_CC) The memory burst size is set to one
           XDMAC_CC_DSYNC_PER2MEM  | //(XDMAC_CC) Peripheral to Memory transfer
           XDMAC_CC_CSIZE_CHK_1  | //(XDMAC_CC) 1 data transferred
           XDMAC_CC_DWIDTH_BYTE  | //(XDMAC_CC) The data size is set to 8 bits
           XDMAC_CC_SIF_AHB_IF1  | //(XDMAC_CC) The data is read through the system bus interface 1
           XDMAC_CC_DIF_AHB_IF0  | //(XDMAC_CC) The data is written through the system bus interface 0
           XDMAC_CC_SAM_FIXED_AM  | //(XDMAC_CC) The address remains unchanged.
           XDMAC_CC_DAM_INCREMENTED_AM | //(XDMAC_CC) The addressing mode is incremented (the increment size is set to the data size).
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS0_RX);    //(XDMAC_CC) Channel x Peripheral Identifier

  xdmac_TWI0_RX_channel_cfg.mbr_bc = 0 ;        //Block Control Member
  xdmac_TWI0_RX_channel_cfg.mbr_ds =  0;        //Data Stride Member
  xdmac_TWI0_RX_channel_cfg.mbr_sus = 0;        //Source Microblock Stride Member.
  xdmac_TWI0_RX_channel_cfg.mbr_dus = 0;        //Destination Microblock Stride Member.
  xdmac_configure_transfer(XDMAC, XDMA_TWI0_RX_CH, &xdmac_TWI0_RX_channel_cfg);
  
  xdmac_channel_set_descriptor_control(XDMAC, XDMA_TWI0_RX_CH, 0); 
   
  xdmac_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_enable(XDMAC, XDMA_TWI0_RX_CH);
 
  twihs_enable_interrupt(twi_ptr, TWIHS_IER_TXCOMP | TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST);
 }
 else
 {
  twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_RXRDY | TWIHS_IER_ARBLST);  
 }
 
 /* Send a START condition */
 if (read_packet->length == 1)
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
  stop_sent = 1;
 } else
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START;
  stop_sent = 0;
 } 
 
 TWI0_StartTimeOut=true;
 
}

The interrupt handler of XDMAC

void XDMAC_Handler(void)
{
 uint32_t dma_status;

 dma_status = xdmac_channel_get_interrupt_status(XDMAC, XDMA_TWI0_RX_CH);
 if (dma_status & XDMAC_CIS_BIS)                 (XDMAC_CIS) End of Block Interrupt Status Bit
 {
  XDMA_TWI0_ENDRX();
  XDMAC_TWIHS_RX_CIE_BIE_count++;
 }

 if (dma_status & XDMAC_CIE_DIE)                 //(XDMAC_CIE) End of Disable Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_DIE_count++;    
 }

 if (dma_status & XDMAC_CIE_FIE)     //(XDMAC_CIE) End of Flush Interrupt Enable Bit 
 {
  XDMAC_TWIHS_RX_CIE_FIE_count++;
 }

 if (dma_status & XDMAC_CIE_RBIE)    //(XDMAC_CIE) Read Bus Error Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_RBIE_count++;
 }

 if (dma_status & XDMAC_CIE_WBIE)    //(XDMAC_CIE) Write Bus Error Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_WBIE_count++;
 }

 if (dma_status & XDMAC_CIE_ROIE)    //(XDMAC_CIE) Request Overflow Error Interrupt Enable Bit 
 {
  XDMAC_TWIHS_RX_CIE_ROIE_count++;
 } 

}


void XDMA_TWI0_ENDRX()
{
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    XDMA_TWI0_ENDRX_count++;
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_RXRDY );
    twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
}

Read one byte of data, and the transfer stops.

Logic analyzer detects a NACK condition, but the interrupt does not occur.

What did I do wrong?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not study your code but I think your problem is that NACK condition is detected by the TWI controller and if you configure a NACK interrupt, e.g. using twihs_enable_interrupt(TWIM0, TWIHS_IER_NACK);
you must also setup an TWI interrupt handler to handle the NACK interrupt.

I tried to implement TWI with DMA and there are several posts from me to this subject in this forum. Maybe this can help you.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The interrupt handler TWIHS have available in my program.

void TWIHS0_Handler(void)
{
 uint8_t *r_buffer = (uint8_t *)read_packet->buffer;
 uint8_t *w_buffer = (uint8_t *)write_packet->buffer;
 // Read TWI status.
 TWI_ISR_status = twi_ptr->TWIHS_SR;
 TWI_ISR_mask=twi_ptr->TWIHS_IMR;
 
 if ((TWI_ISR_status & TWIHS_SR_NACK) && (TWI_ISR_mask & TWIHS_IMR_NACK))
 {
  TWI_status=TWIHS_RECEIVE_NACK;
  twi0_port_state=TWI_FAULT;
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount.NACK_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 if ((TWI_ISR_status & TWIHS_SR_ARBLST) && (TWI_ISR_mask & TWIHS_IMR_ARBLST))
 {
  TWI_status=TWIHS_ARBITRATION_LOST;
  twi0_port_state=TWI_FAULT;
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount.ARBITRATION_LOST_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 switch(twi0_port_state)
 {
  case TWI_RECEIVING:
  {
   if ((TWI_ISR_status & TWIHS_SR_OVRE) && (TWI_ISR_mask & TWIHS_IMR_OVRE))
   {
    TWI_status=TWIHS_RECEIVE_OVERRUN;
    twi0_port_state=TWI_FAULT;
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);    
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    DBC_u.DBC_S.TWI_ErrorCount.OverErr_Ctr++;
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    return;
   }   
   
 
   if ((TWI_ISR_status & TWIHS_SR_RXRDY)&& (TWI_ISR_mask & TWIHS_IMR_RXRDY))
   {
    if (!stop_sent) 
    {
     twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
     stop_sent = 1;
    } 
     twihs_disable_interrupt(twi_ptr, TWIHS_IDR_RXRDY);
     twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_TXCOMP );
     if(r_buffer!=0)
     {
       r_buffer[read_packet->length-1] = twi_ptr->TWIHS_RHR; 
     }
     else twi_ptr->TWIHS_RHR;    
     
   }//if ((TWI_ISR_status & TWI_SR_RXRDY)&& (TWI_ISR_mask & TWI_IMR_RXRDY))
 
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    twi_ptr->TWIHS_SR;
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    
   }   
  }//case TWI_RECEIVING:
  break;
  case TWI_TRANSMITTING:
  {  
   if ((TWI_ISR_status & TWIHS_SR_TXRDY)&& (TWI_ISR_mask & TWIHS_IMR_TXRDY))
   {
    twihs_disable_interrupt(twi_ptr, TWIHS_IDR_TXRDY);
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_TXCOMP);
   
    twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
    if(w_buffer!=0) twi_ptr->TWIHS_THR = w_buffer[write_packet->length-1];
    else            twi_ptr->TWIHS_THR =0;
   } 
   
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
   }  
     
  }//case TWI_TRANSMITTING:
  break;
 }//switch(twi0_port_state)

 
}

If you disconnect the external device from the port TWIHS an interrupt NACK occurs.

When reading data using XDMAC interrupt  does not occur and the transfer stops.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the problem here is configuring the DMA channel.

A similar program worked on ATSAM4E16E, but only with the PDC controller.

regjoe if you have a working program TWIHS + XDMAC, please put on the forum settings XDMAC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For unknown reason I cannot use the "add code" feature of this site anymore. It does not accept copy&paste. Sorry for bad readability of this post.

void twi_xdmac_rx_configure(Twihs *const p_twihs, uint8_t twi_xdmac_rx_ch_hwid, uint8_t xdmac_rx_ch, uint32_t *buffer, uint32_t size, uint8_t width)
{
    uint32_t xdmaint;

    xdmaint = (XDMAC_CIE_BIE |
        XDMAC_CIE_DIE   |
        XDMAC_CIE_FIE   |
        XDMAC_CIE_RBIE  |
        XDMAC_CIE_WBIE  |
        XDMAC_CIE_ROIE);

    /* Initialize channel config for receiver */
    xdmac_rx_cfg.mbr_ubc = size;

    xdmac_rx_cfg.mbr_da = (uint32_t)buffer;                    // get address of pointer to destination buffer
    xdmac_rx_cfg.mbr_sa = (uint32_t)&(p_twihs->TWIHS_RHR);    // get TWI receive register address

    xdmac_rx_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |
        XDMAC_CC_MBSIZE_SINGLE |
        XDMAC_CC_DSYNC_PER2MEM |
        XDMAC_CC_CSIZE_CHK_1 |
        ((width == 4) ? XDMAC_CC_DWIDTH_WORD : XDMAC_CC_DWIDTH_BYTE) |
        XDMAC_CC_SIF_AHB_IF1 |
        XDMAC_CC_DIF_AHB_IF0 |
        XDMAC_CC_SAM_FIXED_AM |
        XDMAC_CC_DAM_INCREMENTED_AM |
        XDMAC_CC_PERID(twi_xdmac_rx_ch_hwid);

    xdmac_rx_cfg.mbr_bc  = 0;
    xdmac_rx_cfg.mbr_ds  = 0;
    xdmac_rx_cfg.mbr_sus = 0;
    xdmac_rx_cfg.mbr_dus = 0;

    xdmac_configure_transfer(XDMAC, xdmac_rx_ch, &xdmac_rx_cfg);

    xdmac_channel_set_descriptor_control(XDMAC, xdmac_rx_ch, 0);

    xdmac_channel_enable_interrupt(XDMAC, xdmac_rx_ch, xdmaint);
    xdmac_channel_enable(XDMAC, xdmac_rx_ch);
    xdmac_enable_interrupt(XDMAC, xdmac_rx_ch);

#if 1
    /*Enable XDMAC interrupt */
    NVIC_EnableIRQ(XDMAC_IRQn);
#endif

}

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the help, but the problem is not initializing the DMA channel.

I redid the program, so that was the opportunity to work TWIHS with the DMA channel and without DMA channel.

#ifdef CONF_BOARD_TWIHS0
#define twi_ptr TWIHS0    
#define twi_id ID_TWIHS0      
#endif

#ifdef CONF_BOARD_TWIHS2
#define twi_ptr TWIHS2    
#define twi_id ID_TWIHS2      
#endif

static uint32_t read_packet_cnt=0;

//------------------------------------------------------------

void twi_master_r(twihs_packet_t *p_packet)
{
 twi_ptr->TWIHS_SR;
 read_packet=p_packet;
 write_packet=0;
 // Check argument 
 if ((read_packet->length == 0) || (read_packet->buffer==0))
 {
  TWI_status=TWIHS_INVALID_ARGUMENT;
  return;
 }

 // Set read mode, slave address and 3 internal address byte lengths 
 twi_ptr->TWIHS_MMR = 0;
 twi_ptr->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(read_packet->chip) |
 ((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
 TWIHS_MMR_IADRSZ_Msk);

 // Set internal address for remote chip 
 twi_ptr->TWIHS_IADR = 0;
 twi_ptr->TWIHS_IADR = twihs_mk_addr(read_packet->addr, read_packet->addr_length);
 
 twi_port_state=TWI_RECEIVING;
 read_packet_cnt=read_packet->length;
#ifdef CONF_BOARD_TWI_DMA_Enable
 if (read_packet->length > 2) 
 { 
  //Initialize RX channel config 
  xdmac_TWI0_RX_channel_cfg.mbr_ubc = (read_packet->length-2);  

  xdmac_TWI0_RX_channel_cfg.mbr_sa = (uint32_t)twi_ptr->TWIHS_RHR; //Source Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_da = (uint32_t)read_packet->buffer; //Destination Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN  | //(XDMAC_CC) Synchronized mode (Peripheral to Memory or Memory to Peripheral Transfer).
           XDMAC_CC_MEMSET_NORMAL_MODE | //(XDMAC_CC) Memset is not activated
           XDMAC_CC_MBSIZE_SINGLE  | //(XDMAC_CC) The memory burst size is set to one
           XDMAC_CC_DSYNC_PER2MEM  | //(XDMAC_CC) Peripheral to Memory transfer
           XDMAC_CC_CSIZE_CHK_1  | //(XDMAC_CC) 1 data transferred
           XDMAC_CC_DWIDTH_BYTE  | //(XDMAC_CC) The data size is set to 8 bits
           XDMAC_CC_SIF_AHB_IF1  | //(XDMAC_CC) The data is read through the system bus interface 1
           XDMAC_CC_DIF_AHB_IF0  | //(XDMAC_CC) The data is written through the system bus interface 0
           XDMAC_CC_SAM_FIXED_AM  | //(XDMAC_CC) The address remains unchanged.
           XDMAC_CC_DAM_INCREMENTED_AM | //(XDMAC_CC) The addressing mode is incremented (the increment size is set to the data size).
#ifdef CONF_BOARD_TWIHS0
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS0_RX);    //(XDMAC_CC) Channel x Peripheral Identifier
#endif
#ifdef CONF_BOARD_TWIHS2
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS2_RX);    //(XDMAC_CC) Channel x Peripheral Identifier
#endif


  xdmac_TWI0_RX_channel_cfg.mbr_bc = 0 ;        //Block Control Member
  xdmac_TWI0_RX_channel_cfg.mbr_ds =  0;        //Data Stride Member
  xdmac_TWI0_RX_channel_cfg.mbr_sus = 0;        //Source Microblock Stride Member.
  xdmac_TWI0_RX_channel_cfg.mbr_dus = 0;        //Destination Microblock Stride Member.
  xdmac_configure_transfer(XDMAC, XDMA_TWI0_RX_CH, &xdmac_TWI0_RX_channel_cfg);
  
  xdmac_channel_set_descriptor_control(XDMAC, XDMA_TWI0_RX_CH, 0); //Destination parameters are updated when the descriptor is retrieved.
   
  
      uint32_t xdmaint;

      xdmaint = (XDMAC_CIE_BIE |//End of Block Interrupt Enable Bit
      XDMAC_CIE_DIE   |
      XDMAC_CIE_FIE   |
      XDMAC_CIE_RBIE  |
      XDMAC_CIE_WBIE  |
      XDMAC_CIE_ROIE);

  xdmac_channel_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH, xdmaint );
  xdmac_channel_enable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH); 
 }


    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST);
#else
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST| TWIHS_IER_RXRDY ); 
#endif
 
 // Send a START condition 
 if (read_packet->length == 1)
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
  stop_sent = 1;
 } else
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START;
  stop_sent = 0;
 } 
 
 TWI0_StartTimeOut=true;
 
}
//--------------------------------------------------------------------------------
#ifdef CONF_BOARD_TWIHS0
void TWIHS0_Handler(void)
#endif
#ifdef CONF_BOARD_TWIHS2
void TWIHS2_Handler(void)
#endif
{
 uint8_t *r_buffer = (uint8_t *)read_packet->buffer;
 uint8_t *w_buffer = (uint8_t *)write_packet->buffer;
 // Read TWI status.
 TWI_ISR_status = twi_ptr->TWIHS_SR;
 TWI_ISR_mask=twi_ptr->TWIHS_IMR;
 
 if ((TWI_ISR_status & TWIHS_SR_NACK) && (TWI_ISR_mask & TWIHS_IMR_NACK))
 {
  TWI_status=TWIHS_RECEIVE_NACK;
  twi0_port_state=TWI_FAULT;
#ifdef CONF_BOARD_TWI_DMA_Enable
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
#endif  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount[CurTask].NACK_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 if ((TWI_ISR_status & TWIHS_SR_ARBLST) && (TWI_ISR_mask & TWIHS_IMR_ARBLST))
 {
  TWI_status=TWIHS_ARBITRATION_LOST;
  twi0_port_state=TWI_FAULT;

#ifdef CONF_BOARD_TWI_DMA_Enable
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);
#endif     
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);

  DBC_u.DBC_S.TWI_ErrorCount[CurTask].ARBITRATION_LOST_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 switch(twi_port_state)
 {
  case TWI_RECEIVING:
  {
   if ((TWI_ISR_status & TWIHS_SR_OVRE) && (TWI_ISR_mask & TWIHS_IMR_OVRE))
   {
    TWI_status=TWIHS_RECEIVE_OVERRUN;
    twi0_port_state=TWI_FAULT;
#ifdef CONF_BOARD_TWI_DMA_Enable
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH); 
#endif       
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    DBC_u.DBC_S.TWI_ErrorCount[CurTask].OverErr_Ctr++;
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    return;
   }   
   
 
   if ((TWI_ISR_status & TWIHS_SR_RXRDY)&& (TWI_ISR_mask & TWIHS_IMR_RXRDY))
   {
    if ((!stop_sent) && (read_packet_cnt==1))
    {
     twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
     stop_sent = 1;
     twihs_disable_interrupt(twi_ptr, TWIHS_IDR_RXRDY);
    } 
     
     if(r_buffer!=0)
     {
       r_buffer[read_packet->length-read_packet_cnt] = twi_ptr->TWIHS_RHR; 
       read_packet_cnt--;
     }
     else twi_ptr->TWIHS_RHR;  
     
     if(read_packet_cnt==0)
     {
      twihs_enable_interrupt(twi_ptr, TWIHS_IER_TXCOMP);  
      } 
     
   }//if ((TWI_ISR_status & TWI_SR_RXRDY)&& (TWI_ISR_mask & TWI_IMR_RXRDY))
 
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    twi_ptr->TWIHS_SR;
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    
   }   
  }//case TWI_RECEIVING:

  break;
 }//switch(twi_port_state)

 
}

//-------------------------------------------------------------------------------------------------------------
void XDMA_TWI0_ENDRX() //The function is called in the interrupt handler XDMAC
{
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    XDMA_TWI0_ENDRX_count++;
    
    read_packet_cnt=2;
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_RXRDY );    
}

When TWIHS0 works without a DMA channel, then the packet is read completely.

When TWIHS0 works with DMA channel, then read 2 bytes of the packet, and the transfer stops.

 

The TWIHS interrupt handler is written according to the datasheet:

Data Receive with the DMA in Master Mode

The DMA transfer size must be defined with the buffer size minus 2. The two remaining characters must be

managed without DMA to ensure that the exact number of bytes are received regardless of system bus latency

conditions encountered during the end of buffer transfer period.

1. Initialize the DMA (channels, memory pointers, size -2, etc.);

2. Configure the Master mode (DADR, CKDIV, MREAD = 1, etc.) or Slave mode.

3. Enable the DMA.

4. (Master Only) Write the START bit in the TWIHS_CR to start the transfer.

5. Wait for the DMA status flag indicating that the buffer transfer is complete.

6. Disable the DMA.

7. Wait for the RXRDY flag in the TWIHS_SR.

8. Set the STOP bit in TWIHS_CR.

9. Read the penultimate character in TWIHS_RHR.

10. Wait for the RXRDY flag in the TWIHS_SR.

11. Read the last character in TWIHS_RHR.

12. (Only if peripheral clock must be disabled) Wait for the TXCOMP flag to be raised in TWIHS_SR.

 

It is assumed that this is a bug in the microcontroller.

I tried to change the DMA channels, but the result is the same.

I tried to use TWIHS2, pre-configuring the pins of the microcontroller, but TWIHS2 does not work.

/** TWI2 pins definition */
#define TWIHS2_DATA_GPIO   PIO_PD27_IDX
#define TWIHS2_DATA_FLAGS  (IOPORT_MODE_MUX_C)
#define TWIHS2_CLK_GPIO    PIO_PD28_IDX
#define TWIHS2_CLK_FLAGS   (IOPORT_MODE_MUX_C)

#ifdef CONF_BOARD_TWIHS2
ioport_set_pin_peripheral_mode(TWIHS2_DATA_GPIO, TWIHS2_DATA_FLAGS);
ioport_set_pin_peripheral_mode(TWIHS2_CLK_GPIO, TWIHS2_CLK_FLAGS);
#endif

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MYA333 wrote:
It is assumed that this is a bug in the microcontroller.

What do you mean ? Any source for that assumption ?

The TWI + XDMAC implemention on the M7 MCU is not that powerful than for example the SPI + XDMAC implementation. TWI requires 'manual' programming of START and STOP condition therefore the hassle with xfer size less than 2 bytes.

Does DMA running with size > 2 bytes work on your side ?

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The packet size is 128 bytes.

As you can see from the program text, the conditions start and stop are set manually.

I mean, I probably got a defective MCU. I have previously worked with ATSAM4E16E and there TWI + PDC worked well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MYA333 wrote:
If you disconnect the external device from the port TWIHS an interrupt NACK occurs. When reading data using XDMAC (NACK?) interrupt does not occur and the transfer stops (successfully?).

 

Going  back to the start of your post... Is this still your (main) problem ?

 

If so I consider this as two different situations which I try to explain in "Dinglish" == German-English :)

 

If you disconnect the slave from the bus (or use an invalid address) SDA is left high which means that arbitration/addressing of a slave failed. This is considered as an error and must be handled. Here the master should get NACK interrupt and the receive DMA hangs up (if already started) and must be killed/disabled.

 

If the master reads data from the slave a NACK condition is generated by the master to signal the slave that the slave shall stop sending bytes. Therefore you get only a DMA done interrupt which means that all data has been received. This is not an error and therefore no NACK interrupt is fired.

 

I got this from an I2C manual considering master read:

 

The master will continue sending out the clock pulses, but will release the SDA line, so that the slave can
transmit data. At the end of every byte of data, the master will send an ACK to the slave, letting the slave
know that it is ready for more data. Once the master has received the number of bytes it is expecting, it
will send a NACK, signaling to the slave to halt communications and release the bus. The master will
follow this up with a STOP condition.

 

If you unplug the slave after successful addressing in the middle of receive the master will most probably not detect this kind of error and read 0xFFs. But I'm not sure about that.

 

What do you think ?

 

SAME newbie

Last Edited: Fri. Nov 10, 2017 - 11:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The slave is connected (FRAM). Without the use of a DMA channel, what was written is then read.

I tried the same program to download into another SAME70-XPLD board.

The result is similar: without DMA channel it works, with DMA channel does not work.

AFEC with the DMA channel working well.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MYA333 wrote:

When TWIHS0 works with DMA channel, then read 2 bytes of the packet, and the transfer stops.

So this is your latest problem ? The DMA read is stuck in the middle / after the 7th clock of the 3rd byte ? I think I have seen this situation once in my tests but I can't remember the cause of this problem and how I fixed this... How many bytes do you want to receive in the DMA job ? What about the DMA destination buffer ? Is it in internal SRAM or somewhere else ? Is the source / destination buffer address correct ? Can you check your config structure initialization ? Does it look like this ?

 

 

Is the data cache disabled or enabled ? Do you get any DMA interrupts or wait forever ?

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the help. There was an error initializing the DMA channel (there was no & operator).

xdmac_TWI0_RX_channel_cfg.mbr_sa = (uint32_t)&twi_ptr->TWIHS_RHR;     //Source Address Member

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now TWIHS works with XDMAC, but there was another problem: the first 12 bytes in the receive buffer are zero. The data received from the logic analyzer is not all zero.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can be a problem of the debugger or the MPU cache. Just see this problem in the debugger memory view or do you see the zeros if you printf the buffer content ?

 

Is the MPU cache enabled or disabled ? If it is enabled the data received from TWI is written by the DMA into SRAM and the MPU cache must be updated before the data can be processed. This is usually done in the ASF in the function xdmac_channel_enable() by calling SCB_CleanInvalidateDCache() function (this can be done better but does the job here).

 

If the cache is disabled the cache sync is not necessary and therefore I added a compiler switch to disable the cache sync if cache is disabled:

 

#ifdef CONF_BOARD_ENABLE_CACHE
    SCB_CleanInvalidateDCache();
#endif   

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

...and the cache is enabled in init.c by

 

#ifdef CONF_BOARD_ENABLE_CACHE
    /* Enabling the Cache */
    SCB_EnableICache();
    SCB_EnableDCache();
#endif  // CONF_BOARD_ENABLE_CACHE

 

...and in conf_board.h:

#define CONF_BOARD_ENABLE_CACHE

 

The MPU cache is disabled for my TWI tests:

 

//#define CONF_BOARD_ENABLE_CACHE

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The MPU cache is enable.

When I disabled the cache everything became OK.

Is it possible to use the cache, and what is needed for this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

with cache and especially xdmac you should not forget to insert memory barrieres into your code .... so before you start the transfer you should add DSB and ISB calls (included in ASF).

Reason: due to the architecture of this chip, data is not necessarily written into the memory locations before other actions are started ... e.g. config TWIHS, setup XDMAC, write data into the buffer to be written out of TWIHS, start XDMAC may or may NOT result into data correctly be transfered. in order to avoid this: insert DSB and ISB before starting XDMAC transfers

Same goes for receiving ... after XDMAC says its done, add DSB and ISB before accessing the receive buffer

Last Edited: Fri. Nov 24, 2017 - 06:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Global flush of D-cache is time consuming, too slow for time critical applications. Use SCB_InvalidateDCache_by_Addr() and SCB_CleanDCache_by_Addr() if you have smaller buffers. These functions are implemented in CMSIS 4.2. See AN-15679_SAMS70_E70_Cache_Coherence.pdf for details how to manage the D-cache.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi George,

 

I couldn't find the application note that your are referring to. Can you send us a link or check if the document title is really as you stated?

 

Regards

Markus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I put the wrong name to my last post :-) Anyhow, I'm still interested in learning more about the treatment of the Cache and I'm therefore keen on reading the quoted document.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No problem :)

 

Unfortunately I cannot provide a link to the document. Document title is "How to manage Cortex-M7 Cache Coherence on the Atmel SAM S70 / E70" and it seems that I downloaded it last year.

 

FYI: I read it first time just a couple of weeks before. Until that I was running the E70 always with cache disabled because of its drawbacks (debugger problems, necessary cache management if using DMA ...). But now I need full E70 performance and have to run it with cache enabled. Using the techniques of cache flush and update functions of CMSIS 4.2 as described in the document above, XDMAC works fine but now I'm stuck on using cache together with the GMAC. Just additional information if you are dealing with XMAC and GMA.

 

Let me know if I shall PM you the document (if this is possible here - never tried...).

 

 

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Could you please share the AN-15679_SAMS70_E70_Cache_Coherence.pdf file? It has long disappeared from the Internet. Can you send it to my e-mail: righteous@list.ru or upload it to any cloud storage and share the link in this chat?

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
Global flush of D-cache is time consuming, too slow for time critical applications. Use SCB_InvalidateDCache_by_Addr() and SCB_CleanDCache_by_Addr() if you have smaller buffers. These functions are implemented in CMSIS 4.2. See AN-15679_SAMS70_E70_Cache_Coherence.pdf for details how to manage the D-cache.

Could you please share the AN-15679_SAMS70_E70_Cache_Coherence.pdf file? It has long disappeared from the Internet. Can you send it to my e-mail: righteous@list.ru or upload it to any cloud storage and share the link in this chat?

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using linked lists for DMA communication you must provide a send and receive list.

After configuration of linked lists the lists in cache must be written to SRAM before DMA is started.

Also the initialized data buffers which are source of the DMA.

This is done via SCB_CleanDCache_by_Addr ().

After the DMA is done and data is sent and received you must force the cache to read the data from SRAM to cache via  SCB_InvalidateDCache_by_Addr().

 

And DMA buffers must be aligned:

// in Cortex-M7 size of cache line is fixed to 8 words (32 bytes),
// therefore the DMA buffer must be 32 bytes aligned
COMPILER_ALIGNED(32)

 

Attachment(s): 

SAME newbie

Last Edited: Sun. Nov 26, 2017 - 11:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:

If you are using linked lists for DMA communication you must provide a send and receive list.

After configuration of linked lists the lists in cache must be written to SRAM before DMA is started.

Also the initialized data buffers which are source of the DMA.

This is done via SCB_CleanDCache_by_Addr ().

After the DMA is done and data is sent and received you must force the cache to read the data from SRAM to cache via  SCB_InvalidateDCache_by_Addr().

Thank you very much indeed! At last somebody shed some more light on a very poorly described feature of the ARM processors and shared the original article, which was unavailable because of broken links.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
And DMA buffers must be aligned: // in Cortex-M7 size of cache line is fixed to 8 words (32 bytes), // therefore the DMA buffer must be 32 bytes aligned COMPILER_ALIGNED(32)
 

Could you please show the example code of how to provide the correct alignment? I don't understand that feature at all.

I need to transmit packets with size of 14 bytes by the QSPI (in SPI mode). What else should i do besides creating the linked list XDMAC transmission? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just put the keyword in front of the buffer declaration:

 

COMPILER_ALIGNED(32)

// array of linked list of DMA structures

static lld_view2 asDmaLinkListWr[DMA_CHANS];    

 

You can check the correct alignment in the .map file.

 

I'm not sure about your second question regarding QSPI. I do not use DMA for QSPI but may be this link can help:

http://community.atmel.com/forum/porting-spi-flash-spi-qspi

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
I'm not sure about your second question regarding QSPI. I do not use DMA for QSPI but may be this link can help: http://community.atmel.com/forum...

I have partially successful realization of QSPI transmission from memory to peripheral in SPI mode with XDMAC. The only problem which is still not solved that when i expect that the current transmission will be for data[n] buffer, but instead XDMAC transmitts data[n-1] or even data[n-2] which must being transmitted the step or two before. During each End of Block Interrupt of XDMAC i do some calculations of Data in the XDMAC_Handler function and then proceed the XDMAC transmittion of that new calculated Data, but instead of transmission of that new Data i see transmission of the Data which were calculated one or more steps before, like if there were a FIFO which stores a few transmissions. Could you please suggest me how to transmitt just calculated data?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As far as I understand you break up a long SPI master write into several chunks. After each chunk sent you get an block interrupt, calculate the remaining length and start a new DMA ?

So on each block interrupt you check the data which is currently transmitted ? How do you do that ? Trigger scope at each block interrupt and watch the data on SPI MOSI ? Sure there is a delay between DMA done and data on MOSI that might caused by a FIFO.

 

Is D-cache enabled or disabled ? Do you disable DMA after block interrupt / before next DMA init ?

SAME newbie

Last Edited: Sat. Dec 9, 2017 - 11:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
As far as I understand you break up a long SPI master write into several chunks.

No. Unfortunately i don't understand what is the chunk and how it could be used. I always work with option XDMAC_CC_CSIZE_CHK_1

regjoe wrote:
After each chunk sent you get an block interrupt, calculate the remaining length and start a new DMA ?

I enable two channels of XDMAC, the 0 channel is used to receive Audio Data from I2SC0 with 96kHz. The 1 channel is used to transmit processed Audio Data to the slave device, say DAC (actually it is DDS)

regjoe wrote:
So on each block interrupt you check the data which is currently transmitted ? How do you do that ? Trigger scope at each block interrupt and watch the data on SPI MOSI ?

Not quite yet. Yes i have logic analyzer but it is a little bit hard to read what data is send through QSPI at almost 75MHz even catching each transmission by the trigger. But i have spectrum analyzer, which is connected to the DDS output and i can see the signal which is generated by the DDS after each transmission. Of course i use Atmel ICE debugger and put breakpoints after each data transmission. And the data which is transmitted through QSPI and expected to be generated in DDS right after current XDMAC transmission, actually are transmitted on the next cycle. So i suppose there is a FIFO buffer in the XDMAC, which violates the coherency of transmissions in my device.

The XDMAC_Handler function is executed on each End of Block Interrupt when Audio Data is received by I2SC0 on XDMAC's Channel 0. In the XDMAC_Handler i suspend the XDMAC's Channel 1 transmission through QSPI, then do some processing of the received Audio Data and write processed data into the buffer for subsequent transmission through QSPI, at the end of the XDMAC_Handler i resume the work of the XDMAC's Channel 1. And i expect that right after resume i will see the transmission of that new data.

It seems that i begin to understand that when i suspend XDMAC's Channel 1 it still contain in it's FIFO buffer the data which were send in the previous cycle, and when i resume Channel 1 the XDMAC sends that previous data once again. I have tried before the resume to flush the XDMAC's Channel 1 FIFO with the command: XDMAC->XDMAC_GSWF = XDMAC_GSWF_SWF1 but it seems to have no effect

regjoe wrote:
Is D-cache enabled or disabled ?

I tried both D-cache enabled and disabled. D-cache enabled speeds up DMA transmissions (as it seen with  the logic analyzer) but causes problems of cache coherence which for some reason are not solved by executing SCB_CleanDCache_by_Addr(&QSPI_XDMAC_tx_data[0], 33) right before DMA transmission

Here the signals with D-cache disabled:

Here the signals with D-cache enabled:

We can see that with D-cache enabled the data transmission through QSPI occurs earlier and the latch up signal is also occurs faster then in the D-cache disabled mode.

But unfortunately i can not solve the problem of cache coherency so i will try to use TCM memory

regjoe wrote:
Do you disable DMA after block interrupt / before next DMA init ?

As i already said previously after End of Block interrupt in the XDMAC_Handler i suspend the XDMAC's Channel 1 by command XDMAC->XDMAC_GWS |= XDMAC_GWS_WS1;

And then at the end of the XDMAC_Handler i resume XDMAC's Channel 1 by the command XDMAC->XDMAC_GRWR |= XDMAC_GRWR_RWR1;

So i don't disable XDMAC on each End of Block Interrupt. I only suspend it

Last Edited: Sun. Dec 10, 2017 - 06:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So TWI receive, calculate and SPI send is synchronous and one done after each other or do you intend to do that all in parallel ?

How many bytes do you process in each cycle ? If you are dealing with some bytes it would probably better do avoid DMA at all.

I disable XDMAC interrupts and the DMA channel before initialization of the next DMA. Maybe suspend/resume is not suitable here.

I guess you have a single interrupt handler which is used to process both TWI and SPI XDMAC interrupts.

If not already done I suggest to test TWI receive DMA and SPI send DMAs separate in order to make sure that this works.

If this is OK you have to make sure that you are processing the correct interrupt source and do not mix up TWI and SPI interrupts.

If you have a logic analyzer I suggest to use GPIO pins to signal events e.g. TWI block xfer done, SPI block xfer done, TWI DMA disabled, SPI DMA disabled, DMA errors etc. to make sure that these events occur in the right sequence as you expect.

To make things easier I suggest to disable cache until everything works if possible.

I have no TCM here. Some say this works, some say it doesn't work. If it makes things complicated I would disable cache and use internal SRAM first.

If this works you can use TCM or configure SRAM in cached and uncached sections where you can place DMA transfer buffers in uncached section while keeping heavily used variables into cached section for example.

 

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
So TWI receive, calculate and SPI send is synchronous and one done after each other or do you intend to do that all in parallel ?

I use I2SC receiver, not TWI. And yes, the calculations and SPI transmissions are made between I2SCs RXRDY interrupts, which occur each period of 96kHz frame clock. So they are synchronous.

regjoe wrote:
How many bytes do you process in each cycle ?

I transmit 14 bytes in each SPI transmission

regjoe wrote:
I disable XDMAC interrupts and the DMA channel before initialization of the next DMA. Maybe suspend/resume is not suitable here.

Disabling and enabling XDMAC takes too much time and the remaining time is not enough for calculations and SPI transmission before the next I2SC interrupt will occur.

regjoe wrote:
I guess you have a single interrupt handler which is used to process both TWI and SPI XDMAC interrupts.

Yes XDMAC_Handler is one for all XDMAC channels, so to differentiate the source of the interrupt the XDMAC_Handler checks both channels interrupt status at it's very beginning.

regjoe wrote:
If you have a logic analyzer I suggest to use GPIO pins to signal events e.g.

The I2SCs interrupt occur almost synchronously with the falling edge of the Frame Sync signal, so there is no need to use any other GPIO to signalize the interrupt, though i did it as you said at the very beginning of work on the project.

regjoe wrote:
If it makes things complicated I would disable cache and use internal SRAM first.

I must put the receive and transmit data buffers into the SRAM and that will speed up the XDMAC's access to the data? I have no experience in memory allocation and storing data in different types of memory. So it will be great challenge for me. I would be very grateful if you could share some simple example of storing data in SRAM.
By the way when trying to enable non cacheable region in the memory using SAME70_XPLDs example projects i have errors. The project can not be build because there is no definition of one macro: INNER_OUTER_NORMAL_NOCACHE_TYPE( SHAREABLE )

Here is the part of the code of the ASFs init.c file

#ifdef MPU_HAS_NOCACHE_REGION

dw_region_base_addr =

        SRAM_NOCACHE_START_ADDRESS |

        MPU_REGION_VALID |

        MPU_NOCACHE_SRAM_REGION;

 

    dw_region_attr =

        MPU_AP_FULL_ACCESS    |

        INNER_OUTER_NORMAL_NOCACHE_TYPE( SHAREABLE ) |

        mpu_cal_mpu_region_size(NOCACHE_SRAM_REGION_SIZE) |

        MPU_REGION_ENABLE;

 

    mpu_set_region( dw_region_base_addr, dw_region_attr);

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So you always send/receive a fix number of bytes ? Then I think that this would be a perfect candidate for a linked list DMA. Nevertheless, I think although you always transfer fixed size and same buffers you have to (re-)configure the DMA between DMA disable/enable or DMA suspend/resume. As far as I remember without reconfiguration DMA didn't work on my side and I am curious to know how you manage this.

 

I don't understand why disable/enable should take more time than suspend/resume. Is it because you do not re-configure DMA or is it because you do an (implicit) cache flush when you enable DMA ? Note in ASF xdmac_channel_enable() function SCB_CleanInvalidateDCache() is called which takes a lot of time but this call is not necessary if you manually manage cache using the cache_invalidate/clean_by_addr() functions.

 

TCM should be faster than internal SRAM and TCM memory is not cached. IMHO if you have TCM and it works on your side then you can avoid cache management at all and you have the fastest memory access. But again I have no TCM here and cannot check DMA.

 

INNER_OUTER_NORMAL_NOCACHE_TYPE macro is missing in current ASF.

#ifdef MPU_HAS_NOCACHE_REGION

/**
 *  Internal SRAM third partition memory region --- Non-cacheable e.g. for GMAC
 */

#define INNER_OUTER_NORMAL_NOCACHE_TYPE(x)   (( 0x01 << MPU_RASR_TEX_Pos ) | ( DISABLE << MPU_RASR_C_Pos ) | ( DISABLE << MPU_RASR_B_Pos ) | ( x << MPU_RASR_S_Pos ))

    dw_region_base_addr =
        SRAM_NOCACHE_START_ADDRESS |
        MPU_REGION_VALID |
        MPU_NOCACHE_SRAM_REGION;

    dw_region_attr =
        MPU_AP_FULL_ACCESS    |
        INNER_OUTER_NORMAL_NOCACHE_TYPE( SHAREABLE ) |
        mpu_cal_mpu_region_size(NOCACHE_SRAM_REGION_SIZE) |
        MPU_REGION_ENABLE;

    mpu_set_region( dw_region_base_addr, dw_region_attr);
#endif

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
So you always send/receive a fix number of bytes ? Then I think that this would be a perfect candidate for a linked list DMA. Nevertheless, I think although you always transfer fixed size and same buffers you have to (re-)configure the DMA between DMA disable/enable or DMA suspend/resume. As far as I remember without reconfiguration DMA didn't work on my side and I am curious to know how you manage this.

Yes i use linked list DMA for both receiving data through I2SC and transmitting through QSPI like it done in many ASF examples. And there is no need to reconfigure XDMAC after each interrupt because the linked list "never ends"

regjoe wrote:
I don't understand why disable/enable should take more time than suspend/resume. Is it because you do not re-configure DMA or is it because you do an (implicit) cache flush when you enable DMA ?

It is my own experience which I had get at the very beginning of work on the project, disabling/enabling have taken too much time. Finally I need to rise the Frame Sync Clock up to 192kHz, and there will remain very little time to do all the calculations and SPI transmission. Choosing SAMV70 I hoped to configure QSPI to work in Quad SPI mode (utilizing all 4 data lines) to transmit data to the DDS at highest possible speed. Hope to do it later after solving current problems with D-cache and XDMAC's FIFO. Do you agree that suspending/resuming of the linked list DMA transmission is more simple and elegant way than disabling/reconfiguration/enabling of the DMA? Moreover suspending and resuming is made only for transmitting channel to prevent multiple transmissions of the same data which able to cause noise in the slave DDS.

Last Edited: Sun. Dec 10, 2017 - 05:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using suspend/resume would be elegant but as far as I remember endless DMA linked list did not work here on my side. I had no demo for that for E70.

Therefore I am using timer for data exchange of recv/send buffers and starting of DMA linked list.

I can use only internal SRAM and flash and therefore I have D- and I-cache enabled. This requires D-cache management if transceive buffer are in cached memory region.
First try with DMA disable/reconfigure/enable with cache took ca. 300us andwith cache management I have ca 25us setup time.

In this configuration calculation takes ca. 20us using heavily float and so I am using 20kHz timer which is enough for the moment but the higher the better.

I have no benchmark yet for transceive buffers in uncached iSRAM memory but I hope this can speed up things a little also using hard float.

I will check this as soon I have customer board here back again.

Again, TCM would be better than internal SRAM as far as I know.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
I have no benchmark yet for transceive buffers in uncached iSRAM memory

How can I put TX buffer into the non-cached SRAM? Could You please provide me with some example code?

regjoe wrote:
this can speed up things a little also using hard float.

I tried to use -mfloat-abi=hard option on the SAME70 and SAMV70, but the project could not be built with multiple errors like this: error: src/spi_xdmac_example.o uses VFP register arguments, SPI_XDMAC_EXAMPLE2.elf does not
On the 11-th page of the Application-note_How to Optimize Usage of SAM S70/E70/V7x Architecture there is a mention: softfp: to be used only if the user intends to use same binary on Cortex-M4F and CortexM7F based product
hard: in any other case

I'm not sure what does it mean, but it looks like CortexM7F can not use hard FPU? Where I'm mistaken?

Last Edited: Mon. Dec 11, 2017 - 02:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Read this here first (placing GMAC DMA descriptors and buffers into non cached region, should also work for your purpose)

http://community.atmel.com/forum/linker-problem-placing-variables-extra-memory-region

then in conf_board.h

#define CONF_BOARD_CONFIG_MPU_AT_INIT
#define CONF_ENABLE_EXCEPTIONS          // report exceptions
/* Exclude GMAC transmit/receive buffers from cache */
#define MPU_HAS_NOCACHE_REGION
#define NOCACHE_SRAM_REGION_SIZE  0x10000	/* 64kB non-cache region for GMAC buffers, overrides define in MODIFIED mpu.h */

In mpu.h

#if (defined MPU_HAS_NOCACHE_REGION) && !(defined NOCACHE_SRAM_REGION_SIZE)
#define NOCACHE_SRAM_REGION_SIZE            0x1000
#endif

In init.c as shown above in previous post:

#ifdef MPU_HAS_NOCACHE_REGION

/**
 *  Internal SRAM third partition memory region --- Non-cacheable e.g. for GMAC
 */

#define INNER_OUTER_NORMAL_NOCACHE_TYPE(x)   (( 0x01 << MPU_RASR_TEX_Pos ) | ( DISABLE << MPU_RASR_C_Pos ) | ( DISABLE << MPU_RASR_B_Pos ) | ( x << MPU_RASR_S_Pos ))

    dw_region_base_addr =
        SRAM_NOCACHE_START_ADDRESS |
        MPU_REGION_VALID |
        MPU_NOCACHE_SRAM_REGION;

    dw_region_attr =
        MPU_AP_FULL_ACCESS    |
        INNER_OUTER_NORMAL_NOCACHE_TYPE( SHAREABLE ) |
        mpu_cal_mpu_region_size(NOCACHE_SRAM_REGION_SIZE) |
        MPU_REGION_ENABLE;

    mpu_set_region( dw_region_base_addr, dw_region_attr);
#endif

In linker file (flash.ld)

MEMORY
{
    rom (rx)  : ....

/* 320kB SRAM (cached) + 64kB non-chached SRAM for GMAC DMA (aligned) */
   ram (rwx) : ORIGIN = 0x20400000, LENGTH = 0x00050000
   /* LENGTH must match NOCACHE_SRAM_REGION_SIZE! */
   ram_nocache (rwx): ORIGIN = 0x20450000, LENGTH = 0x10000
}

and

SECTIONS
{
    .text :
    .....
    
    
    /* region excluded from cache for unmanaged DMA e.g. GMAC DMA */
	.ram_nocache (NOLOAD):
    {
        . = ALIGN(8);
        _sram_nocache = .;
        *(.ram_nocache)
    } > ram_nocache

    . = ALIGN(4);
    _end = . ;
    _ram_end_ = ORIGIN(ram) + LENGTH(ram) + LENGTH(ram_nocache) - 1 ;
}

Check diff between placing variables in cached and uncached section in .map files to make sure that placement is correct.

See info in link above.

 

hard float:

I tried this and had (I think) the same errors as you had, therefore I am using this here instead (double float sw library, no FPU)

-mfloat-abi=softfp -mfpu=fpv5-d16

I've read that Atmel M7 cores have FPU on chip for double float calculation support but as far as I understand if you want to use FPU somewhere in your code you have to compile the entire project with the same compiler settings for all modules. I did not spent much time on investigations but I guess that you have to compile all 3rd party stuff e.g. the C runtime module (newlib) and probably others with FPU support enabled. Otherwise we get linker problems. newlib source code is not in ASF, this would mean: downloading source from newlib project, find suitable configuration settings for use in ASF and compiler settings. The advantage would be that you have the latest newlib code and best control of all resources. Maybe somebody in this forum has more infos on that subject ?

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot for the code examples! Soon i will share the results of it's utilizing

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
I tried this and had (I think) the same errors as you had, therefore I am using this here instead (double float sw library, no FPU)

I have just put flags -mfloat-abi=hard and -mfpu=fpv5-sp-d16  into both places ARM/GNU C Compiler=>Miscellaneous and ARM/GNU Linker=>Miscellaneous and successfully built the project!

 Nevertheless I did't notice any difference in CPU performance between softfp and hard mode!

Last Edited: Mon. Dec 11, 2017 - 05:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to this document "http://www.atmel.com/Images/Atme..." there are 2 things which must be done : a) compile with FPU instructions and b) enable FPU. Don't know what happens if FPU is not enabled but it seems that this is done in ASF for E70. In the startup file I found

#if __FPU_USED
	fpu_enable();
#endif

I compiled with compiler setting "-mfloat-abi=softfp -mfpu=fpv5-sp-d16"  and checked the assembler instructions by generating the assembler output file

arm-none-eabi-objdump.exe -l -S *.elf  1>asm.txt

Although using softfp the assembler output seems to contain FPU instructions !?

	psPara->lSegGain = (int32_t) (fM * fGainRes * psPara->lSegReso);
  42e8c4:	ed97 7a09 	vldr	s14, [r7, #36]	; 0x24
  42e8c8:	edd7 7a01 	vldr	s15, [r7, #4]
  42e8cc:	ee27 7a27 	vmul.f32	s14, s14, s15
  42e8d0:	69fb      	ldr	r3, [r7, #28]
  42e8d2:	691b      	ldr	r3, [r3, #16]
  42e8d4:	ee07 3a90 	vmov	s15, r3
  42e8d8:	eef8 7ae7 	vcvt.f32.s32	s15, s15
  42e8dc:	ee67 7a27 	vmul.f32	s15, s14, s15
  42e8e0:	eefd 7ae7 	vcvt.s32.f32	s15, s15
  42e8e4:	ee17 2a90 	vmov	r2, s15
  42e8e8:	69fb      	ldr	r3, [r7, #28]
  42e8ea:	615a      	str	r2, [r3, #20]

Any idea ?

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
Don't know what happens if FPU is not enabled but it seems that this is done in ASF for E70

I use floating point calculations and once tried to disabled FPU by commenting out the fpu_enable() function and it hanged the controller somewhere deep in it's assembler code.

regjoe wrote:
Although using softfp the assembler output seems to contain FPU instructions !?
 

I have the same result with compiler settings "-mfloat-abi=softfp -mfpu=fpv5-sp-d16". By the way, the assembler code can be seen in disassembler window when debugging.
I'm not ARM guru but if I understand correctly the "hard" mode differs from "softfp" mode not by instructions but by the unit which fetches these instructions. May be in "hard" mode it is made by the Floating Point Unit (special co-processor) and in "softfp" mode it is made by the CPU? Of course using of the FPU for math operations must free the CPU time.

 

UPDATE: 

Fast browsing of internet revealed this: https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
Where can be found the following:
-mfloat-abi=name
Specifies which floating-point ABI to use. Permissible values are: ‘soft’, ‘softfp’ and ‘hard’.
Specifying ‘soft’ causes GCC to generate output containing library calls for floating-point operations. ‘softfp’ allows the generation of code using hardware floating-point instructions, but still uses the soft-float calling conventions. ‘hard’ allows generation of floating-point instructions and uses FPU-specific calling conventions.
The default depends on the specific target configuration. Note that the hard-float and soft-float ABIs are not link-compatible; you must compile your entire program with the same ABI, and link with a compatible set of libraries.

Last Edited: Tue. Dec 12, 2017 - 12:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Last Edited: Wed. Dec 13, 2017 - 03:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

righteous wrote:
Note that the hard-float and soft-float ABIs are not link-compatible; you must compile your entire program with the same ABI, and link with a compatible set of libraries.

Yep, that's exactly my problem :) Will get into that after x-mas...

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have just investigated that there are two math libraries provided with the ASF: src\ASF\thirdparty\CMSIS\Lib\GCC\libarm_cortexM7lfsp_math.a
src\ASF\thirdparty\CMSIS\Lib\GCC\libarm_cortexM7lfsp_math_softfp.a
Reading them with notepad shows that the libarm_cortexM7lfsp_math.a was compiled with the "-mfloat-abi=hard -mfpu=fpv5-sp-d16" options and the libarm_cortexM7lfsp_math_softfp.a was compiled with the  "-mfloat-abi=softfp -mfpu=fpv5-sp-d16" options. So i hope that building project with either of two compiler and linker options will be correct because we have both variants of the math library!

Last Edited: Thu. Dec 14, 2017 - 01:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Returning to the problem of non cacheable SRAM. I have tried to implement your code in my project. There are some problems. The code builds up without errors but placing the QSPI_TX_buffer[]  into the non cacheable SRAM causes faulty transmissions through the QSPI (SPI mode). Placing other buffers into the SRAM do not cause faults: linklist_read[], linklist_write[], I2SC_RX_buffer[]. And only when QSPI_TX_buffer[] placed into the SRAM the problem arises. It can be seen by the logic analyzer and on the output spectrum. The the length of the transmitted packets still the same 14 bytes but they are filled with wrong data...It looks like the data bits are reordered, shifted or rewrote. Can it be caused by the intersection of the non cache SRAM section and the stack for example? May be this buffer must be protected by some option?

Any ideas?

Last Edited: Fri. Dec 15, 2017 - 03:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you use heap ? I think there might be a problem then if malloc is not correct implemented. Check the address of heap variables.

My GMAC DMA buffers are in non-cached SRAM and this is stable.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

regjoe wrote:
Do you use heap ?

No, i don't use heap. I don't need it. I only define buffers for received and transmitted data

Last Edited: Fri. Dec 15, 2017 - 04:56 PM