Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: EMAC AT91SAM9260
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
ZED
Доброго всем здравия!!!

Начал разбираться с EMAC для AT91SAM9260. Имеется документация на сам контроллер и тестовый пример для IAR под названием basic-emac-uip-webserver-project-at91sam9260-ek. В этой теме планируется задавать вопросы, которые касаются проблемы запуска Ethernet на вышеупомянутом контроллере и просто непонятные вещи.

Первый вопрос заключается в следующем. В файле emac.h имеются некоторые указатели на функции:
Код
/// Callback used by send function
typedef void (*EMAC_TxCallback)(unsigned int status); // EMAC_TxCallback - указатель на функцию, возвращающую void и принимающую unsigned int
typedef void (*EMAC_RxCallback)(unsigned int status); // EMAC_RxCallback - указатель на функцию, возвращающую void и принимающую unsigned int
typedef void (*EMAC_WakeupCallback)(void); //  EMAC_WakeupCallback - указатель на функцию, возвращающую void и принимающую void


Далее они используются, например в функции EMAC_Handler, которая управляет прерываниями, расположенной в emac.c:
Код
    volatile EmacTxTDescriptor *pTxTd;
    volatile EMAC_TxCallback   *pTxCb;

Код
if (rxTd.rxCb) {
            rxTd.rxCb(rxStatusFlag);
        }

Код
            if (*pTxCb) {
                (*pTxCb)(txStatusFlag);
            }

Я так понял это так называемые обратные функции, но что они делают нигде не определено (по-крайней мере я не нашел). Вопрос собственно для чего они нужны?

Еще один вопрос, что выполняет следующий кусок кода в той же функции EMAC_Handler и для чего он нужен? Можно ли обойтись без всего этого?
Код
        // Check the buffers
        while (CIRC_CNT(txTd.head, txTd.tail, TX_BUFFERS)) {
            pTxTd = txTd.td + txTd.tail;
            pTxCb = txTd.txCb + txTd.tail;

            // Exit if buffer has not been sent yet
            if ((pTxTd->status & EMAC_TX_USED_BIT) == 0) {
                break;
            }
            
            // Notify upper layer that packet has been sent
            if (*pTxCb) {
                (*pTxCb)(txStatusFlag);
            }
            
            CIRC_INC( txTd.tail, TX_BUFFERS );
        }

        // If a wakeup has been scheduled, notify upper layer that it can send
        // other packets, send will be successfull.
        if( (CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) >= txTd.wakeupThreshold)
         &&  txTd.wakeupCb) {
            txTd.wakeupCb();
        }
ZED
И еще один вопрос. В функции EMAC_Init, расположенной в emac.c есть следующие строки:
Код
    // Инициализация дескриgторов передающего буфера.
    for(Index = 0; Index < TX_BUFFERS; Index++) {// Цикл по количеству передающих буферов
        // Инициализация адресов дескрипторов передающего буфера
        Address = (unsigned int)(&(pTxBuffer[Index * EMAC_TX_UNITSIZE]));
        txTd.td[Index].addr = Address; // Нет Маски, т.к. Слово 0 - адрес начала буфера
        txTd.td[Index].status = EMAC_TX_USED_BIT; // Установка для всех буферов бита used!!!
    }
    txTd.td[TX_BUFFERS - 1].status = EMAC_TX_USED_BIT | EMAC_TX_WRAP_BIT;


Строка txTd.td[Index].status = EMAC_TX_USED_BIT; устанавливает биты 31 в слове 1 ВСЕХ дескрипторов в единицу, тогда как в документации сказано, что этот бит должен быть установлен только для первого буфера, а при инициализации все биты 31 из слова 1 должны быть сброшены:

Цитата
This bit is only set for the first buffer in a frame unlike receive where all buffers have the Used bit set once used.

Mark all entries in this list as owned by EMAC, i.e. bit 31 of word 1 set to 0.


Вопрос: пример не рабочий получается или что?
ZED
На счет строки txTd.td[Index].status = EMAC_TX_USED_BIT; все вроде правильно. В документации сказано про прием, а не про передачу, это я не туда посмотрел.
С остальным трудности, специалисты откликнитесь.
Zelepuk
Один "специалист" мне когда-то сказал, что Ethernet у ARM есть, но никто не знает как он работает. После чего к арму был успешно прикручен модуль WIZNET.
MALLOY2
Цитата
Один "специалист" мне когда-то сказал, что Ethernet у ARM есть, но никто не знает как он работает. После чего к арму был успешно прикручен модуль WIZNET.


У нас тоже был такой "специалист" как только сказал что нужно поставить WIZNET, так сразу и пошел лесом.

Счас работаю c SAM9XE(думаю мак контроллер ничем не отличается) LwIP и FREERTOS, могу сказать что МАС довольно примитивный и простой, посмотрел на примеры от атмела сразу отставил их в сторону, делал все по доке, все работает.
sergeeff
Берете u-boot и смотрите как там at91-eth.c реализован. Единственное, u-boot работает без прерываний, но это, надеюсь, не проблема. Подтверждаю, что c lwip'ом, по крайней мере at91rm9200, работает как надо.
ZED
О, спасибо за указание направление, а то там дальше такой треш начинается wacko.gif Попробую поразбираться с U-Boot'ом, будут вопросы напишу.
ZED
MALLOY2, а не выложите Ваш вариант EMAC?
MALLOY2
Могу, только это шаблонный класс для C++, и не очень оптимальная версия, не оптимальность заключается в том что МАС контролер позволяет принимать пакеты с определенным смещением, эта фича позволяет отказаться от упаковки сетевых структур что немного подымает производительность сетевого стека. Ну мож еще какие есть бажки, сильно не тестил.
ZED
Огромное спасибо, буду разбираться.
Кстати в U-boot я не нашел файл с именем at91-eth.c и даже что-то похожее на правду.
sergeeff
u-boot\drivers\net\at91_emac.c
ZED
Вот именно, что нету, у меня есть U-boot для MMNET1000 u-boot-2009.01-MMnet1000.tar и U-Boot u-boot-1.3.4.tar. И там нет этого файла. Если можно, прикрепите его пожалуйста в сообщении.
sergeeff
Пожалуйста.

Сам u-boot вы можете скачать с его FTP: ftp://ftp.denx.de/pub/u-boot/

Последняя версия : u-boot-2010.09.tar.bz2
ZED
Спасибо, буду разбираться

Мне кажется в U-boot еще больший треш, если честно. Тоже, что и в тестовом примере, только более все разбросано по отдельным файлам и без комментариев.

Спасибо MALLOY2, но C++ я к сожалению не знаю sad.gif Но Вам огромное уважение, что Вы сам по доке разобрались и написали код! a14.gif Я вот никак не могу с этими дескрипторами разобраться.
sergeeff
Цитата(ZED @ Dec 14 2010, 11:19) *
Спасибо, буду разбираться

Мне кажется в U-boot еще больший треш, если честно. Тоже, что и в тестовом примере, только более все разбросано по отдельным файлам и без комментариев.

Спасибо MALLOY2, но C++ я к сожалению не знаю sad.gif Но Вам огромное уважение, что Вы сам по доке разобрались и написали код! a14.gif Я вот никак не могу с этими дескрипторами разобраться.


Ладно вам накатывать. Пример - он и в Африке пример. Но, по крайней мере, u-boot живой проект и работает. А то что голову придется все равно поднапрячь, так это работа наша такая.


Насчет дескрипторов. Обратите внимание на:
1. Выравнивание.
2. Вы же, скорее всего кеши включили? Так вот приемо/передаточные буфера разместите в некешируемой области.
MALLOY2
И сами дескрипторы должны быть в не кешируемой области.
ZED
Цитата
Вы же, скорее всего кеши включили? Так вот приемо/передаточные буфера разместите в некешируемой области.

Извините за глупость, я знаю, что в контроллере есть 2 Кэша ICache и DCache. Я так понял, что они нужны для ускорения работы ядра наряду с MMU. Но я с ними так и не разобрался, тем более не знаю как его включать/выключать. Тем более не знаю как вне кэша размещать переменные.

И еще я вот тут начал с Вашим кодом разбираться. Вот в функции irq_handler() вызываются две не понятные функции:
Код
  static void irq_handler()
  {
    portBASE_TYPE xYieldRequired = pdFALSE;
    uint32_t status = AT91C_BASE_EMAC->EMAC_ISR;
    if ( status &  AT91C_EMAC_RCOMP)
    {
      xSemaphoreGiveFromISR(RxSem, &xYieldRequired);
    };
    portEND_SWITCHING_ISR(&xYieldRequired);
  }

Что должен делать EMAC по прерыванию? В примере он подсчитывает статистику тех или иных ситуаций и возвращает ее в регистр статистики (статуса передачи или приема). А еще какие-то операции с указателями, про которые я задавал вопрос вначале темы.
MALLOY2
Цитата
Что должен делать EMAC по прерыванию?


то что запрограммируете, мне пока статистика не нужна. Если у вас не используется ОС то можно обойтись и без прерываний.

У меня используется ОС FreeRTOS и функции о которых вы спрашиваете являются функциями ОС.

Цитата
Извините за глупость, я знаю, что в контроллере есть 2 Кэша ICache и DCache. Я так понял, что они нужны для ускорения работы ядра наряду с MMU. Но я с ними так и не разобрался, тем более не знаю как его включать/выключать. Тем более не знаю как вне кэша размещать переменные.


Если не разберетесь с MMU и кешами, то получите редкую черепаху.

О работе с кешами все расписано в ARM926EJ-S™ Technical Reference Manual или на сайте
ZED
А вот еще вопросик по приему пакетов (фреймов). Я так понял, что его осуществляет у вас функция task Что делает функция pbuf_alloc(PBUF_RAW,1536,PBUF_POOL)?

Каков вообще алгоритм приема фрейма?
Сначала мы проверяем бит Ownership Слова 0 дескриптора приемного буфера. Если он установлен, это значит, что этот буфер используется. Тогда мы проверяем бит начала фрейма SOF. Если он установлен, то в примере от IAR делается какая-то хитрая операция. Видимо связанная со сбросом предыдущего кадра и подсчетом кол-ва использованных буферов. По мимо всего прочего разрешается прием фрема:
Код
if ( RxIndx->status & EMAC_RX_SOF_BIT )
          {
            ptr = static_cast<uint8_t*>(p->payload);
            flen =  0;
            sof  = ~0;
          };
          
          if ( sof )
.......................


Дальше я пока встрял. Пока пытаюсь разобраться.

ZED
Блин, ведь прием возможен только, если буфер не занят (Ownership = '0'). Я совсем запутался. Читаю доку - вижу фигу.
ZED
Появился еще один дополнительный вопрос, когда устанавливается и кем бит начала фрейма SOF? Зачем его вообще проверять? При инициализации ончдолжен быть равен нулю, как в принципе и все Слово 1 (Статуса) дескриптора. Каков тогда вообще принцип работы с этими дескрипторами?
Еще в доке сказано про 11-битный счетчик, значения которого складывается со значением регистра указателя очереди приемных буферов EMAC_RBQP. Этот счетчик нужно реализовывать или он уже есть в процессоре? При этом не понятно регистр EMAC_RBQP процессор сам инкрементирует ибо написано, что он содержит адрес (точку входа) дескриптора к которому производится обращение в настоящее время или что-то туда нужно постоянно писать?
ZED
Я так понял, что когда кадр (фрейм) находится в приемном буфере, а точнее он может находиться сразу в нескольких буферах, то в дескрипторах этих буферах автоматически устанавливается бит used (Ownership = '1'). Помимо всего прочего, в дескрипторах начального буфера и в конечного буфера, где располагается принимаемый фрейм, установлены биты начала кадра SOF и конца кадра EOF. В дескрипторе буфера, где расположен бит конца кадра EOF в слове статуса в битах 0..11 содержится информация о длине фрейма. Тогда можно предложить следующий алгоритм принятия фрейма:

Среди всех буферов с установленным битом Ownership найти буфер с установленным битом начала кадра SOF и буфер с установленным битом конца кадра EOF. Для последнего считать длину. Если таковые не найдены - кадр не принимать!
Если буферы с SOF и EOF найдены, значения из них и всех буферах между ними переписать в память функцией memcpy и сбросить бит Ownership.

Прошу Вас прокомментируйте мои соображения. Особенно хочется услышать мнение MALLOY2.
MALLOY2
да, так и есть, только нужно учесть то что может отсутствовать SOF или EOF, и в этом случае вы должны правильно отработать и сбросить Ownnership.
ZED
А нельзя ли сделать так: если биты SOF или EOF (какой-нибудь из них) не обнаружены, то сбросить Ownnership во всех дескрипторах?
ZED
Вы не могли бы прокомментировать мою функцию приема пакета:

CODE
//-----------------------------------------------------------------------------
/// Прием пакета:
// pFrame - Адрес области памяти, куда копировать фрейм;
// pFrame_Size - Адрес, где расположено значение размера принимаемого кадра
//-----------------------------------------------------------------------------
void EMAC_Receive_Frame(unsigned char *pFrame, unsigned int *pFrame_Size)
{
unsigned int Rx_Index_tmp = Rx_Index; // Локальный номер приемных буферов
unsigned char SOF_Enable = 0; // Обнаружен бит начала кадра SOF
unsigned char EOF_Enable = 0; // Обнаружен бит конца кадра EOF

unsigned int *Rx_Buffer_SOF = 0; // Адрес буфера с установленным битом начала кадра SOF

// Пока бит Ownership = 1 (буфер используется и в нем есть данные)
while (Rx_Descriptor[Rx_Index].addr & EMAC_RX_OWNERSHIP_BIT) {

// Перебераем все буферы, чтобы найти буфер с установленным битом
// Начала кадра EOF.

// Если буфер с установленым битом начала кадра SOF найден
// сбросить во всех дескрипторах предыдущих буферов бит Ownership:
if (Rx_Descriptor[Rx_Index_tmp].addr & EMAC_RX_SOF_BIT) {

// Сбросить предыдущий фрагмент кадра (буфер):
while (Rx_Index_tmp != Rx_Index) {
// Сбросить бит USED:
Rx_Descriptor[Rx_Index].addr &= ~(EMAC_RX_OWNERSHIP_BIT);

// Инкрементировать счетчик количества приемных буферов и,
// если он равен количеству приемных буферов - обнулить его:
if (++Rx_Index == RX_BUFFERS){
Rx_Index = 0; // Обнуление
}
}
// Найдено начало кадра:
SOF_Enable = 1;

// Запомнить адрес буфера с установленным битом начала кадра SOF
Rx_Buffer_SOF = (unsigned int)(&Rx_Buffer[Rx_Index_tmp]) & EMAC_ADDRESS_MASK; // Маска адреса;
}

if (Rx_Descriptor[Rx_Index_tmp].addr & EMAC_RX_EOF_BIT) {
// Найден конец кадра:
EOF_Enable = 0

// Запомнить размер кадра
*pFrame_Size = Rx_Descriptor[Rx_Index_tmp].status & EMAC_LENGTH_FRAME;
}


// Инкрементировать счетчик количества приемных буферов и,
// если он равен количеству приемных буферов - обнулить его:
if (++Rx_Index_tmp == RX_BUFFERS){
Rx_Index_tmp = 0; // Обнуление
}
}

// Eсли обнаружены биты начала кадра и конца кадра, копировать данные
// из буфера в память:
if (SOF_Enable && EOF_Enable) {
memcpy(pFrame, Rx_Buffer_SOF, *pFrame_Size);
}

// Обнуление счетчика приемных буферов
Rx_Index = 0;

// Сбросить бит Ownership во всех дескрипторах:
while (Rx_Index != RX_BUFFERS) { //

// Сбросить бит USED:
Rx_Descriptor[Rx_Index].addr &= ~(EMAC_RX_OWNERSHIP_BIT);

// Инкрементировать счетчик приемных буферов
Rx_Index++;
}
// Обнуление счетчика приемных буферов
Rx_Index = 0;

} // End While
} // End Function


Я просто думаю, зачем принимать часть кадра? Если все равно его потом отбрасывать? Лучше сразу принимать только корректные фреймы. Жду с нетерпением Ваших комментариев, спасибо!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.