This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/AM3359: Ethernet CPDMA transmit stall under heavy load

Part Number: AM3359


Tool/software: Code Composer Studio

Hello experts,

I have a problem with a stalling CPDMA/Tx interrupt when sending Ethernet data on a Sitara AM3359 custom board with custom firmware RTOS based.
Under heavy load the CPDMA stops sending and I don't get the Tx interrupt anymore. My test condition is about 200 ping consoles sending 1472 bytes each to the port. After some minutes or hours, sending stops but receiving goes on. I am not able to restart a transmission. The last package was finished by the DMA (e.g. HDP=0x4A102A8C, flags=0xE0000071 and CP=0x4A102A78, flags=0x1000003C, next=0x0) and the DMA_STAT is idle 0x80000000. Looks like I have a race condition here, but I followed the instruction of the TRM about the misqueued handling. I cannot see any error condition of the DMA.
When this happens I already tried a DMA restart by writing the TX_CP and TX_HDP again, disable and enable TX_EN, writing EOI_TX_PULSE again. No success.

My question: In which register can I see, why the CPDMA does not start again?

Best Regards,
Stephan


  • Hi,

    What software is this? RTOS? Can you provide more details?
  • Hi Biser,

    the RTOS is a FreeRTOS V8.2.0.

    Best Regards,
    Stephan
  • Sorry, this is not supported by TI. I can only ask the factory team to comment on your hardware question:

    "In which register can I see, why the CPDMA does not start again?"
  • Thank you Biser, I already know the modus operandi. I just mentioned the RTOS, because the task switching can cause race conditions. My problem is now, that I don't know why the CPDMA does not start again and  which register I have to evaluate.

    Best Regards,
    Stephan

  • From a4e50455d7725c40f547b1395333cd923067f2f2 Mon Sep 17 00:00:00 2001
    From: Grygorii Strashko <grygorii.strashko@ti.com>
    Date: Thu, 1 Sep 2016 20:37:07 +0300
    Subject: [PATCH v2 1/7] net: ethernet: ti: cpdma: am437x: allow descs to be
     plased in ddr
    
    It's observed that cpsw/cpdma is not working properly when CPPI
    descriptors are placed in DDR instead of internal CPPI RAM on am437x
    SoC:
    - rx/tx silently stops processing packets;
    - or - after boot it's working for sometime, but stuck once Network
    load is increased (ping is working, but iperf is not).
    (The same issue has not been reproduced on am335x and am57xx).
    
    It seems that write to HDP register processed faster by interconnect
    than writing of descriptor memory buffer in DDR, which is probably
    caused by store buffer / write buffer differences as these functions
    are implemented differently across devices. So, to fix this i come up
    with two minimal, required changes:
    
    1) all accesses to the channel register HDP/CP/RXFREE registers should
    be done using sync IO accessors readl()/writel(), because all previous
    memory writes writes have to be completed before starting channel
    (write to HDP) or completing desc processing.
    
    2) the change 1 only doesn't work on am437x and additional reading of
    desc's field is required right after the new descriptor was filled
    with data and before pointer on it will be stored in
    prev_desc->hw_next field or HDP register.
    
    In addition, to above changes this patch eliminates all relaxed ordering
    I/O accessors in this driver as suggested by David Miller to avoid such
    kind of issues in the future, but with one exception - relaxed IO accessors
    will still be used to fill desc in cpdma_chan_submit(), which is safe as
    there is read barrier at the end of write sequence, and because sync IO
    accessors usage here will affect on net performance.
    
    Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
    ---
     drivers/net/ethernet/ti/davinci_cpdma.c | 40 ++++++++++++++++++---------------
     1 file changed, 22 insertions(+), 18 deletions(-)
    
    diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
    index 36518fc..d6f0ded 100644
    --- a/drivers/net/ethernet/ti/davinci_cpdma.c
    +++ b/drivers/net/ethernet/ti/davinci_cpdma.c
    @@ -166,12 +166,12 @@ static struct cpdma_control_info controls[] = {
     #define num_chan	params.num_chan
     
     /* various accessors */
    -#define dma_reg_read(ctlr, ofs)		__raw_readl((ctlr)->dmaregs + (ofs))
    -#define chan_read(chan, fld)		__raw_readl((chan)->fld)
    -#define desc_read(desc, fld)		__raw_readl(&(desc)->fld)
    -#define dma_reg_write(ctlr, ofs, v)	__raw_writel(v, (ctlr)->dmaregs + (ofs))
    -#define chan_write(chan, fld, v)	__raw_writel(v, (chan)->fld)
    -#define desc_write(desc, fld, v)	__raw_writel((u32)(v), &(desc)->fld)
    +#define dma_reg_read(ctlr, ofs)		readl((ctlr)->dmaregs + (ofs))
    +#define chan_read(chan, fld)		readl((chan)->fld)
    +#define desc_read(desc, fld)		readl(&(desc)->fld)
    +#define dma_reg_write(ctlr, ofs, v)	writel(v, (ctlr)->dmaregs + (ofs))
    +#define chan_write(chan, fld, v)	writel(v, (chan)->fld)
    +#define desc_write(desc, fld, v)	writel((u32)(v), &(desc)->fld)
     
     #define cpdma_desc_to_port(chan, mode, directed)			\
     	do {								\
    @@ -542,10 +542,10 @@ int cpdma_ctlr_start(struct cpdma_ctlr *ctlr)
     	}
     
     	for (i = 0; i < ctlr->num_chan; i++) {
    -		__raw_writel(0, ctlr->params.txhdp + 4 * i);
    -		__raw_writel(0, ctlr->params.rxhdp + 4 * i);
    -		__raw_writel(0, ctlr->params.txcp + 4 * i);
    -		__raw_writel(0, ctlr->params.rxcp + 4 * i);
    +		writel(0, ctlr->params.txhdp + 4 * i);
    +		writel(0, ctlr->params.rxhdp + 4 * i);
    +		writel(0, ctlr->params.txcp + 4 * i);
    +		writel(0, ctlr->params.rxcp + 4 * i);
     	}
     
     	dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff);
    @@ -1061,13 +1061,17 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
     	mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
     	cpdma_desc_to_port(chan, mode, directed);
     
    -	desc_write(desc, hw_next,   0);
    -	desc_write(desc, hw_buffer, buffer);
    -	desc_write(desc, hw_len,    len);
    -	desc_write(desc, hw_mode,   mode | len);
    -	desc_write(desc, sw_token,  token);
    -	desc_write(desc, sw_buffer, buffer);
    -	desc_write(desc, sw_len,    len);
    +	/* Relaxed IO accessors can be used here as there is read barrier
    +	 * at the end of write sequence.
    +	 */
    +	writel_relaxed(0, &desc->hw_next);
    +	writel_relaxed(buffer, &desc->hw_buffer);
    +	writel_relaxed(len, &desc->hw_len);
    +	writel_relaxed(mode | len, &desc->hw_mode);
    +	writel_relaxed(token, &desc->sw_token);
    +	writel_relaxed(buffer, &desc->sw_buffer);
    +	writel_relaxed(len, &desc->sw_len);
    +	desc_read(desc, sw_len);
     
     	__cpdma_chan_submit(chan, desc);
     
    @@ -1136,7 +1140,7 @@ static int __cpdma_chan_process(struct cpdma_chan *chan)
     	}
     	desc_dma = desc_phys(pool, desc);
     
    -	status	= __raw_readl(&desc->hw_mode);
    +	status	= desc_read(desc, hw_mode);
     	outlen	= status & 0x7ff;
     	if (status & CPDMA_DESC_OWNER) {
     		chan->stats.busy_dequeue++;
    -- 
    2.10.1.dirty
    
    
    Hi Stephen,

    You could be having a similar issue that was encountered in the cpsw/cpdma driver for Linux. There was a condition that was causing the cpdma to silently stop processing packets. Attached is a Linux patch that describes the issue and the Linux fix for this condition. Obviously it will not directly apply to your OS but it might provide some insight. The patch was written against the AM437, the cpsw IP is the same between the AM335x and the AM437x. The issue was how long it took to update the CPDMA registers, the Linux driver changed the macro so it was essentially closer to the HW. I need to add the caveat that this may or may not help because as you pointed your RTOS maybe introducing other conditions that TI has not seen.

    Best Regards,

    Schuyler

  • Hello Schuyler,

    thank you for the support.

    I checked the Linux fix but this seems not to be related to my problem. I am using the CPPI RAM for the descriptors. I still can't see the reason, why the CPDMA doesn't respond to further writes of the HDP when it's state is idle. I understand that the CPDMA stops working when TX_HOST_ERR_CODE is set. I have seen this before.
    I checked the interrupt masking. The Tx interrupt is not masked. The Rx interrupt is still handling received packets.

    Here is a log example. I made copies of the whole transmit queue in the CPPI RAM when the descriptors are queued:
    Last Tx bd HDP=0x4A102924, CP=0x4A102910, flags=0x1000003C, DMA_STAT=0x80000000
    tx_bd_trace[115]=0x4A1028FC: flags=0xE0000074, next=0x0, buf_len=116, bufptr=0x8321C94E
    tx_bd_trace[116]=0x4A102910: flags=0xE000003C, next=0x0, buf_len=60, bufptr=0x8321C9E0 <<< This is the last send bd by the CPDMA
    tx_bd_trace[117]=0x4A102924: flags=0xE00005EA, next=0x0, buf_len=1514, bufptr=0x83CC9510 <<< This bd is not processed by the CPDMA anymore
    The last start attempt without response from the CPDMA:
    Last start attempt: HDP=0x4A102924: flags=0xE00005EA, next=0x0

    So from my point of view there is no reason why the CPDMA does not start sending the next HDP. Is there any other register I could observe?

    Best Regards,
    Stephan
  • Hello Experts,

    here are some test results which maybe give a clue to someone, where this problem is coming from.

    Here is another log output which is similar to the previous one. But I verified that the (not sent) packet from the IP stack is valid.

    Last tx HDP=0x4A102884, CP=0x4A102870, flags=0x1000003C, DMA_STAT=0x80000000

    tx_cp_trace[107]=0x4A10285C: flags=0xE0000071, next=0x0, len=113, bufptr=0x8321C94E

    tx_cp_trace[108]=0x4A102870: flags=0xE000003C, next=0x0, len=60, bufptr=0x8321C9C4   << This is the last sent bd by the CPDMA

    tx_cp_trace[109]=0x4A102884: flags=0xE000005A, next=0x0, len=90, bufptr=0x8321C94E   << This bd is not sent by the CPDMA anymore

      bd=0x4A102884: Ethernet header dest=fc4596aa29f, src=985dade42a20, type=8

    The Wireshark output shows, that the last sent packet by the CPDMA was 60 bytes. It is always an ARP broadcast to the other host. I don't know why this packet is generated before the transmission stops.

    This behavior is related to the CPSW switch configuration. I am using 2 ports as switch. One port is connected. The other port not. But one task is cyclically polling the Phy via MDIO to check if the other port is connected or not. By doing this, the MDIO auto negotiation and status registers are read. Once the second port is connected, the problem disappears. So somehow the MDIO communication seem to cause this problem.

    I am grateful for any hint.

    Best Regards,

    Stephan

  • Please, check unconnected port state in ALE - it must be disabled.
  • Hi Stephan,

    Discussing internally here are a few suggestions:

    1) If you have not looked already, we recommend looking at the Linux cpsw and cpdma driver source files. You can download the latest TI SDK and after installing (assuming you have a Linux machine) take a look at drivers/net/ethernet/ti/cpsw.c and davinci_cpdma.c. If you don't have a Linux machine you can search on the web for these file names, there are online kernel source trees that can be viewed.

    2) When writing to HDP you need to check:
    "Writing to these locations when they are non-zero is an error (except at reset)." as per TRM

    3) For recovery he have to use teardown or reset - just writing to HDP wouldn't help.

    4) You need to ensure that the unconnected port is disabled in ALE, set to blocking.

    Best Regards,
    Schuyler
  • Hello Schuyler,

    thank you for the support.

    >> 4) You need to ensure that the unconnected port is disabled in ALE, set to blocking.
    Seems like this solved the problem. Causing me headache for several month. I would never have found this out on my own, even if I wrote everything new.

    >> 1) ... take a look at drivers/net/ethernet/ti/cpsw.c and davinci_cpdma.c.
    Yes, I know the Linux drivers and always review them when I have problems. But the Linux drivers are very abstract and cannot be used as a firmware reference, except the developer is a Linux driver expert himself. In the davinci_cpdma.c I didn't find anything about ALE/PORT_STATE/BLOCKING. More helpful are the u-boot drivers. But the features are limited.

    >> 2) When writing to HDP you need to check:
    >> "Writing to these locations when they are non-zero is an error (except at reset)." as per TRM
    Yes, I checked and indeed, when the transmission stalls, writing to HDP again doesn't help.

    >> 3) For recovery he have to use teardown or reset - just writing to HDP wouldn't help.
    So far I didn't try the Tx_Teardown. Maybe this would be a good workaround to get the port alive again. If the problem pops up again unexpectedly, I will try this.

    Best Regards,
    Stephan
  • Hi Stephan,

    We are glad to hear that you were able to make progress with your driver.

    Best Regards,
    Schuyler