[En-Nut-Discussion] Nutos 5.1 on Ethernut 1.3g with multiple threads: network freezes

Jonathan Woithe jwoithe at atrad.com.au
Thu Jun 18 08:42:04 CEST 2015


Hi all

On Thu, Jun 11, 2015 at 06:33:23PM +1000, hmnews at proconx.com wrote:
> Now you mention EEPROM emulation, that reminded of some issues in the past
> with the RTL8019AS chip we had.
> :
> So I think you are on the right track by looking at the init routines and/or
> EEPROM emulation. Good luck, been there before...

As previously noted, I now have a patch against NutOS 5.1.0 which seems to
work reliably in situations where an unpatched NutOS 5.1.0 fails.  I have
included the patch at the end of this email.  This version has a minor
fix to one of the comments.

Is there any chance that this could be merged into mainline?  As far as I
can tell, this is required if the RTL8019AS is to be reliably initialised on
hardware which has been set up for EEPROM emulation.  Even when the existing
mainline code results in a functioning NIC, the NIC has clearly not been set
up as expected (based on NutOS 4.4.1 for example) since the NIC LED0 has
changed behaviour.

Regards
  jonathan

=== Patch follows ===

The RTL8019AS NIC on Ethernut 1 boards does not appear to initialise
reliably without software EEPROM emulation when the board circuitry is
configured to expect EEPROM emulation.  Reinstate a minimalist EEPROM
emulation to address this.  While many NutOS applications would work fine
without this, others exhibited various network-related problems.

Signed-off-by: Jonathan Woithe <jwoithe at atrad.com.au>

--- a/ethernut-5.1.0/nut/arch/avr/dev/nicrtl.c	2012-12-28 20:57:17.000000000 +1030
+++ b/ethernut-5.1.0/nut/arch/avr/dev/nicrtl.c	2015-06-15 12:34:47.175277508 +0930
@@ -144,6 +144,76 @@
 
 #endif /* RTL_SIGNAL_IRQ */
 
+/* Pins used to support for EEPROM emulation */
+#if (RTL_EESK_AVRPORT == AVRPORTB)
+#define RTL_EESK_PIN    PINB
+#define RTL_EESK_DDR    DDRB
+  
+#elif (RTL_EESK_AVRPORT == AVRPORTC)
+#define RTL_EE_MEMBUS
+#define RTL_EESK_PIN    PINC
+#define RTL_EESK_DDR    DDRC
+  
+#elif (RTL_EESK_AVRPORT == AVRPORTD)
+#define RTL_EESK_PIN    PIND
+#define RTL_EESK_DDR    DDRD
+  
+#elif (RTL_EESK_AVRPORT == AVRPORTE)
+#define RTL_EESK_PIN    PINE
+#define RTL_EESK_DDR    DDRE
+  
+#elif (RTL_EESK_AVRPORT == AVRPORTF)
+#define RTL_EESK_PIN    PINF
+#define RTL_EESK_DDR    DDRF
+  
+#endif /* RTL_EESK_AVRPORT */
+
+#if (RTL_EEDO_AVRPORT == AVRPORTB)
+#define RTL_EEDO_PORT   PORTB
+#define RTL_EEDO_DDR    DDRB
+
+#elif (RTL_EEDO_AVRPORT == AVRPORTC)
+#define RTL_EE_MEMBUS
+#define RTL_EEDO_PORT   PORTC
+#define RTL_EEDO_DDR    DDRC
+
+#elif (RTL_EEDO_AVRPORT == AVRPORTD)
+#define RTL_EEDO_PORT   PORTD
+#define RTL_EEDO_DDR    DDRD
+
+#elif (RTL_EEDO_AVRPORT == AVRPORTE)
+#define RTL_EEDO_PORT   PORTE
+#define RTL_EEDO_DDR    DDRE
+
+#elif (RTL_EEDO_AVRPORT == AVRPORTF)
+#define RTL_EEDO_PORT   PORTF
+#define RTL_EEDO_DDR    DDRF
+
+#endif /* RTL_EEDO_AVRPORT */
+
+#if (RTL_EEMU_AVRPORT == AVRPORTB)
+#define RTL_EEMU_PORT   PORTB
+#define RTL_EEMU_DDR    DDRB
+
+#elif (RTL_EEMU_AVRPORT == AVRPORTC)
+#define RTL_EE_MEMBUS
+#define RTL_EEMU_PORT   PORTC
+#define RTL_EEMU_DDR    DDRC
+
+#elif (RTL_EEMU_AVRPORT == AVRPORTD)
+#define RTL_EEMU_PORT   PORTD
+#define RTL_EEMU_DDR    DDRD
+
+#elif (RTL_EEMU_AVRPORT == AVRPORTE)
+#define RTL_EEMU_PORT   PORTE
+#define RTL_EEMU_DDR    DDRE
+
+#elif (RTL_EEMU_AVRPORT == AVRPORTF)
+#define RTL_EEMU_PORT   PORTF
+#define RTL_EEMU_DDR    DDRF
+
+#endif /* RTL_EEMU_AVRPORT */
+
 
 /*!
  * \brief Size of a single ring buffer page.
@@ -243,6 +313,146 @@
 #define NICINB(reg)         (*((volatile uint8_t *)RTL_BASE_ADDR + reg))
 #define NICOUTB(reg, val)   (*((volatile uint8_t *)RTL_BASE_ADDR + reg) = val)
 
+/*!  
+ * \brief Provide basic EEPROM emulation for the Ethernet controller.
+ *
+ * Tests show that if the EEPROM emulation circuitry is in place it is
+ * necessary to at least provide a string of 0xff bytes to the RTL8019AS
+ * via emulation in order for it to be initialised reliably.
+ *
+ * Return 0 if EEPROM emulation was detected or -1 if not.  The caller could
+ * use this information for its own purposes if desired.
+ *
+ */   
+static int InitNicEeprom(void)
+{
+#ifdef RTL_EESK_BIT
+    register u_int cnt = 0;
+
+    NutEnterCritical();
+
+    /*
+     * Prepare the EEPROM emulation port bits. Configure the EEDO
+     * and the EEMU lines as outputs and set both lines to high.
+     */
+    sbi(RTL_EEDO_PORT, RTL_EEDO_BIT);
+    sbi(RTL_EEDO_DDR, RTL_EEDO_BIT);
+#ifdef RTL_EEMU_BIT
+    sbi(RTL_EEMU_PORT, RTL_EEMU_BIT);
+    sbi(RTL_EEMU_DDR, RTL_EEMU_BIT);
+#endif
+
+    /* Insert a delay of approximately 20ms to allow the pins to settle (the
+     * exact delay length is not critical).  Use assembly to remove any
+     * possibility that the external SRAM is used since the eeprom
+     * emulation bits - which may be part of the data bus - have already
+     * been configured.
+     *
+     * Like Delay16Cycles(), this assumes a 14.7456 MHz system clock.  adiw
+     * consumes 2 clocks, a brne when taken consumes 2 clocks.  The loop
+     * will run for 65536 cycles, giving a delay of 17 ms.  That's close
+     * enough.
+     */
+    __asm__ __volatile__(
+        "        ldi  r24, 0    " "\n"   /* Clear counter. */
+        "        ldi  r25, 0    " "\n"   /* */
+        "DelayLoop:             " "\n"   /* */
+        "        adiw r24, 1    " "\n"   /* Count loops. */
+        "        brne DelayLoop " "\n\t" /* Exit loop on counter overflow. */
+        :      /* No outputs. */
+        :      /* No inputs. */
+        :"r24", "r25");
+
+    /* Force the chip to re-read the EEPROM contents. */
+    NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1);
+    NICOUTB(NIC_PG3_EECR, NIC_EECR_EEM0);
+
+#ifdef RTL_EE_MEMBUS
+    /*
+     * No external memory access beyond this point.
+     */
+#ifdef __AVR_ENHANCED__
+    /* On the ATmega 128 we release bits 5-7 as normal port pins. */
+    outb(XMCRB, inb(XMCRB) | _BV(XMM0) | _BV(XMM1));
+#else
+    /* On the ATmega 103 we have to disable the external memory interface. */
+    cbi(MCUCR, SRE);
+#endif
+#endif
+
+    /* Check, if the chip toggles our EESK input. If not, we do not
+     * have EEPROM emulation hardware.
+     */
+    if (bit_is_set(RTL_EESK_PIN, RTL_EESK_BIT)) {
+        while (++cnt && bit_is_set(RTL_EESK_PIN, RTL_EESK_BIT));
+    } else {
+        while (++cnt && bit_is_clear(RTL_EESK_PIN, RTL_EESK_BIT));
+    }
+
+    /* Loop until the chip stops toggling our EESK input, ensuring it reads
+     * 0xff until it is completed.  We do it in assembly language to make
+     * sure that no external RAM is used for the timeout counter variable.
+     *
+     * On boards without EEPROM emulation the loop will time out after
+     * 0xffff iterations, which should be more than enough time for the
+     * NIC to have completed its read.
+     */
+    __asm__ __volatile__("\n"   /* */
+                         "EmuLoop:               " "\n" /* */
+                         "        ldi  r24, 0    " "\n" /* Clear counter. */
+                         "        ldi  r25, 0    " "\n" /* */
+                         "        sbis %0, %1    " "\n" /* Check if EESK set. */
+                         "        rjmp EmuClkClr " "\n" /* */
+                         "EmuClkSet:             " "\n" /* */
+                         "        adiw r24, 1    " "\n" /* Count loops with EESK set. */
+                         "        breq EmuDone   " "\n" /* Exit loop on counter overflow. */
+                         "        sbis %0, %1    " "\n" /* Test if EESK is still set. */
+                         "        rjmp EmuLoop   " "\n" /* EESK changed, do another loop. */
+                         "        rjmp EmuClkSet " "\n" /* Continue waiting. */
+                         "EmuClkClr:             " "\n" /* */
+                         "        adiw r24, 1    " "\n" /* Count loops with EESK clear. */
+                         "        breq EmuDone   " "\n" /* Exit loop on counter overflow. */
+                         "        sbic %0, %1    " "\n" /* Test if EESK is still clear. */
+                         "        rjmp EmuLoop   " "\n" /* EESK changed, do another loop. */
+                         "        rjmp EmuClkClr " "\n" /* Continue waiting. */
+                         "EmuDone:               \n\t"  /* */
+                         :      /* No outputs. */
+                         :"I"(_SFR_IO_ADDR(RTL_EESK_PIN)), /* Emulation port. */
+                          "I"(RTL_EESK_BIT)                 /* EESK bit. */
+                         :"r24", "r25");
+
+#ifdef RTL_EE_MEMBUS
+    /*
+     * Enable memory interface.
+     */
+#ifdef __AVR_ENHANCED__
+    /* On the ATmega 128 we return bits 5-7 to the data bus. */
+    outb(XMCRB, inb(XMCRB) & ~(_BV(XMM0) | _BV(XMM1)));
+#else
+    /* On the ATmega 103 we have to disable the external memory interface. */
+    sbi(MCUCR, SRE);
+#endif
+#endif
+
+    /* Reset port outputs to default. */
+    cbi(RTL_EEDO_PORT, RTL_EEDO_BIT);
+    cbi(RTL_EEDO_DDR, RTL_EEDO_BIT);
+#ifdef RTL_EEMU_BIT
+    cbi(RTL_EEMU_PORT, RTL_EEMU_BIT);
+    cbi(RTL_EEMU_DDR, RTL_EEMU_BIT);
+#endif
+
+    /* Restore previous interrupt enable state. */
+    NutExitCritical();
+
+    /* Wait until controller ready. */
+    while (NICINB(NIC_CR) != (NIC_CR_STP | NIC_CR_RD2));
+
+    return cnt ? 0 : -1;
+#else
+    return -1;
+#endif
+}
 /*!
  * \brief Reset the Ethernet controller.
  *
@@ -306,6 +516,8 @@
         return -1;
     }
 
+    InitNicEeprom();
+
     /*
      * Mask all interrupts and clear any interrupt status flag to set the
      * INT pin back to low.
@@ -315,10 +527,10 @@
 
     /*
      * During reset the nic loaded its initial configuration from an
-     * external eeprom. On the ethernut board we do not have any
-     * configuration eeprom, but simply tied the eeprom data line to
-     * high level. So we have to clear some bits in the configuration
-     * register. Switch to register page 3.
+     * external eeprom.  On the ethernut board we do not have any
+     * configuration eeprom, but either tied the eeprom data line to high
+     * level or emulate 0xff eeprom data in software.  So we have to clear
+     * some bits in the configuration register.  Switch to register page 3.
      */
     NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1);
 


More information about the En-Nut-Discussion mailing list