Ethernet II или сырые Ethernet пакеты на Nios II Altera часть 3
После создания системы на Nios II можно приступать к написанию программы которая будет посылать в регистр данных МАС eth_ocm 'сырые' даные. Для этого определим пакет из 1168 бай случайных данных. Почему так? Все привязано к 48 элементам гидроакустической антенной решетки минус первые 16 служебных байт формата Ethernet II.
Ниже по тексту представлен тестовый вариант Ethernet передатчика и приемника пакетов формата Ethernet II. Автор Ethernet драйвера - функций инициализации MAC, функций приема и передачи для модуля eth_ocm Вадим Котельников.
#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"
#include "stdio.h"
#include "eth_driver.h"
#include "sys/alt_irq.h"
#include "sys/alt_cache.h"
#include "eth_ocm_regs.h"
#include "eth_ocm_desc.h"
//---------------------------------------------------------------------------------------------
// Объявления функций
static void eth_ocm_isr();
static int eth_ocm_read_init();
static int eth_ocm_rx_isr();
//---------------------------------------------------------------------------------------------
// Глобальные переменные
volatile char pkt[1562];
volatile char ovr_pkt[1568];
char ovr_tx_pkt[1568];
int sem;
char Start = 0;
char PhyReg = 0x1f;
//---------------------------------------------------------------------------------------------
// Задержка
void Delay ( tick)
{
while(tick--);
}
//---------------------------------------------------------------------------------------------
// Инициализация MAC
int MAC_init()
{
alt_u32 t2=0;
int status=0;
//---------------------------------------------------------------------------------------------
// Ожидать пока не появится линк
while((eth_ocm_read_phy_reg(ETH_OCM_0_BASE, PHY, 0x01)&0x04)==0)
{
// скорость 100Mbps
eth_ocm_write_phy_reg(ETH_OCM_0_BASE, PHY, 0x00, 0xB300);
// скорость 10Mbps
// eth_ocm_write_phy_reg(ETH_OCM_0_BASE, PHY, 0x00, 0x9300);
Delay(0xFFFFF);
}
// Обнуление дескрипторов Tx и Rx
for(t2=ETH_OCM_DESC_START;t2<ETH_OCM_DESC_END;++t2)
{
IOWR(ETH_OCM_0_BASE,t2,0);
}
IOWR_ETH_OCM_MODER(ETH_OCM_0_BASE, 0);
IOWR_ETH_OCM_TX_BD_NUM(ETH_OCM_0_BASE,ETH_OCM_TX_DESC_COUNT);
IOWR_ETH_OCM_INT_MASK(ETH_OCM_0_BASE, ETH_OCM_DEFAULT_INTERRUPT_MASK);
IOWR_ETH_OCM_INT_SOURCE(ETH_OCM_0_BASE,0xFFFFFFFF);
IOWR_ETH_OCM_IPGT(ETH_OCM_0_BASE, ETH_OCM_FULL_DUPLEX_IPGT);//!FOR DUPLEX!
IOWR_ETH_OCM_IPGR1(ETH_OCM_0_BASE, 0x0000000C);
IOWR_ETH_OCM_IPGR2(ETH_OCM_0_BASE, 0x00000012);
IOWR_ETH_OCM_PACKETLEN(ETH_OCM_0_BASE, 0x00400600);
IOWR_ETH_OCM_COLLCONF(ETH_OCM_0_BASE, 0x000F003F);
IOWR_ETH_OCM_CTRLMODER(ETH_OCM_0_BASE, 0);
// MAC адрес 010101010101
IOWR_ETH_OCM_MAC_ADDR0(ETH_OCM_0_BASE, ((int)(0x01) | (int)(0x01 << 8) | (int)(0x01 << 16) | (int)(0x01 << 24)));
IOWR_ETH_OCM_MAC_ADDR1(ETH_OCM_0_BASE, (((int)(0x01) | (int)(0x01 << 8)) & 0xFFFF));
// Без маски ETH_OCM_MODER_PRO_MSK
IOWR_ETH_OCM_MODER(ETH_OCM_0_BASE,
ETH_OCM_MODER_PAD_MSK |
ETH_OCM_MODER_PRO_MSK |
ETH_OCM_MODER_CRCEN_MSK |
ETH_OCM_MODER_RXEN_MSK |
ETH_OCM_MODER_TXEN_MSK |
ETH_OCM_MODER_FULLD_MSK);
// Регистрация обработчика прерываний eth_ocm_isr по ETH_OCM_0_IRQ
t2=alt_irq_register(ETH_OCM_0_IRQ, (void*)ETH_OCM_0_BASE, eth_ocm_isr);
// Инициализация Ethernet чтения
eth_ocm_read_init();
sem=0;
return status;
}
//---------------------------------------------------------------------------------------------
// Обработчик прерываний ETH_OCM_0_IRQ
static void eth_ocm_isr()
{
int result;
result=IORD_ETH_OCM_INT_SOURCE(ETH_OCM_0_BASE);
while(result)
{
IOWR_ETH_OCM_INT_SOURCE(ETH_OCM_0_BASE, result);
// Если прерывание по чтению то eth_ocm_rx_isr()
if(result & (ETH_OCM_INT_MASK_RXB_MSK | ETH_OCM_INT_MASK_RXE_MSK))
{
eth_ocm_rx_isr();
}
// Если прерывание на передачу то ничего не делать
if(result & (ETH_OCM_INT_MASK_TXE_MSK | ETH_OCM_INT_MASK_TXB_MSK))
{
}
result=IORD_ETH_OCM_INT_SOURCE(ETH_OCM_0_BASE);
}
}
//---------------------------------------------------------------------------------------------
static int eth_ocm_read_init()
{
alt_u8 *buf_ptr;
buf_ptr = (alt_u8*)alt_remap_cached ((volatile void*) pkt, 4);
buf_ptr = (alt_u8*)(((unsigned int)buf_ptr) + ETHHDR_BIAS);
if(!(IORD_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 1)Ð_OCM_RXDESC_EMPTY_MSK))
{
IOWR_ETH_OCM_DESC_PTR(ETH_OCM_0_BASE, 1, (alt_u32)buf_ptr);
IOWR_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 1, ETH_OCM_RXDESC_EMPTY_MSK | ETH_OCM_RXDESC_IRQ_MSK | ETH_OCM_RXDESC_WRAP_MSK);
}
return 0;
}
//---------------------------------------------------------------------------------------------
static int eth_ocm_rx_isr()
{
alt_u32 stat;
alt_u8 *buf_ptr;
int pklen;
// чтение ETH_OCM_DESC_CTRL
stat=IORD_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 1);
while(!(stat & ETH_OCM_RXDESC_EMPTY_MSK))
{
pklen = stat & ETH_OCM_RXDESC_LEN_MSK;
pklen = pklen >> ETH_OCM_RXDESC_LEN_OFST;
if (pklen<1550)
{
if(!(stat & ETH_OCM_RXDESC_ERROR_MSK))
{
//Ok. Process packet here!
// Зажечь все светодиоды
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0xff);
// Установить признак разрешения передачи
Start = 1;
}
}
buf_ptr = (alt_u8*)alt_remap_cached ((volatile void*) pkt, 4);
buf_ptr = (alt_u8*)(((unsigned int)buf_ptr) + ETHHDR_BIAS);
IOWR_ETH_OCM_DESC_PTR(ETH_OCM_0_BASE, 1, (alt_u32)buf_ptr);
IOWR_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 1, ETH_OCM_RXDESC_EMPTY_MSK | ETH_OCM_RXDESC_IRQ_MSK | ETH_OCM_RXDESC_WRAP_MSK);
stat=IORD_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 1);
}
return 0;
}
//---------------------------------------------------------------------------------------------
// Установка адреса регистра PHY
void eth_ocm_set_phy_addr(int base, int phyad, int reg)
{
phyad &= ETH_OCM_MIIADDRESS_FIAD_MSK;
reg = reg << ETH_OCM_MIIADDRESS_RGAD_OFST;
reg &= ETH_OCM_MIIADDRESS_RGAD_MSK;
phyad |= reg;
IOWR_ETH_OCM_MIIADDRESS(base, phyad);
}
//---------------------------------------------------------------------------------------------
// Запись данных в регистр PHY
void eth_ocm_write_phy_reg(int base, int phyad, int reg, int data)
{
eth_ocm_set_phy_addr(base, phyad, reg);
IOWR_ETH_OCM_MIITX_DATA(base, data);
IOWR_ETH_OCM_MIICOMMAND(base, ETH_OCM_MIICOMMAND_WCTRLDATA_MSK);
Delay(2000);
}
//---------------------------------------------------------------------------------------------
// чтение данных из регистра PHY
int eth_ocm_read_phy_reg(int base, int phyad, int reg)
{
int result;
eth_ocm_set_phy_addr(base, phyad, reg);
IOWR_ETH_OCM_MIICOMMAND(base, ETH_OCM_MIICOMMAND_RSTAT_MSK);
Delay(2000);
result = IORD_ETH_OCM_MIIRX_DATA(base);
return result;
}
//---------------------------------------------------------------------------------------------
// Посылка eth пакета (*data - указатель на массив данных, data_bytes - длина пакета)
int eth_ocm_raw_send(char * data, unsigned data_bytes)
{
alt_u8 *buf;
int result;
int i=0;
result=0;
if (sem)
{
return 1;
}
else
{
sem=1;
buf = (alt_u8 *)alt_remap_cached( (volatile void *)data, 4);
if(data_bytes < ETH_OCM_MIN_MTU)
result = 2; //packet too small
if(data_bytes > ETH_OCM_MAX_MTU)
result = 3; //packet too big
if(IORD_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 0) & ETH_OCM_TXDESC_READY_MSK)
result = 4; //DMA not available
if(!result)
{
IOWR_ETH_OCM_DESC_PTR(ETH_OCM_0_BASE, 0, (unsigned int)buf);
IOWR_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 0, (data_bytes << ETH_OCM_TXDESC_LEN_OFST) | ETH_OCM_TXDESC_READY_MSK | ETH_OCM_TXDESC_WRAP_MSK );
}
do
{
result = IORD_ETH_OCM_DESC_CTRL(ETH_OCM_0_BASE, 0);
++i;
}
while((result & ETH_OCM_TXDESC_READY_MSK) && i<ETH_OCM_TRANSMIT_TIMEOUT);
if(i<ETH_OCM_TRANSMIT_TIMEOUT)
{
if(result &
(ETH_OCM_TXDESC_UR_MSK |
ETH_OCM_TXDESC_RL_MSK |
ETH_OCM_TXDESC_LC_MSK |
ETH_OCM_TXDESC_CS_MSK))
{
result = 5;
}
else
{
if(result & ETH_OCM_TXDESC_RTRY_MSK)
{
result = 0;
}
}
}
else
{
result = 6;
}
sem=0;
return result;
}
}
int main (void)
{
alt_u8 led = 0x2;
alt_u8 dir = 0;
volatile int i = 0, j = 16;
// Neptun Broadcast
static char ethmass[1168] = {0x00,0x90,0xf5,0x6c,0x3d,0x9f,0xff,0xff,0xff,0xff,0xff,0xff,0x23,0x21,0x32,0x21};
// Инициализация MAC
MAC_init();
while (1)
{
// формирование случайных данных
ethmass[j] = led;
j++;
if(j == 1168) j = 16;
// бегущий светодиод
if(led & 0x81)
{
dir = (dir ^ 0x1);
}
if(dir)
led = led >> 1;
else
led = led << 1;
for(i = 0; i < 10; i++)
{
if (i == 0 && Start == 1)
{
// запись в PIO_0_BASE (0x00000000) led
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, led);
// послать Ethernet пакет
eth_ocm_raw_send(ethmass, sizeof(ethmass));
}
};
}
return 0;
}
Попробуем разобрать представленный код. Перед тем как послать байт в регистр данных МАС, необходимо его инициализировать. Функция инициализацми int MAC_init() включает в себя цикл ожидания линка PHY, цикл обнуления дескрипторов записи и чтеня (Tx и Rx) MAC. Затем производится настройка регистров MAC (смотри документацию на eth_ocm ocmacspec.pdf - находится в директории eth_ocm проекта) и устанавливается MAC адрес в регистрах MAC ADDR0, ADDR1. Также при инициализации MAC производится регистрация обработчика прерывания по приему alt_irq_register(... и вызов функции инициализации чтения данных eth_ocm_read_init();.
Функции общения с PHY eth_ocm_set_phy_addr(int base, int phyad, int reg), eth_ocm_write_phy_reg(int base, int phyad, int reg, int data), eth_ocm_read_phy_reg(int base, int phyad, int reg) позволяют записывать и читатать данные в (из) регистров PHY.
Функция main выполняет несколько простых операций. Первая из них это установка первых 16 байт данных пакета формата Ethernet II static char ethmass[1168] в которых первые 6 байт это MAC адрес приемника инфрмации, точнее MAC адрес сетевой карты компа куда будет подключен Ethernet модуль с NIOS II, вторые 6 байт это MAC адрес источника информации, а точнее MAC адрес Ethernet модуля с NIOS II (обычно устанавливается широковещательный ff:ff:ff:ff:ff:ff), далее определен тип протокола 0x2321 (формат Ethernet II) и последние 2 байта из 16 никак не используются.
Вторая это собственно инициализация MAC и третья это бесконечный цикл в котором заполняется массив ethmass[1168] начиная с адреса 0x0010 и затем с небольшой задержкой присходит поочередное зажигание светодиодов (бегущий светодиод) на отладочной плате DE0 с одновременной отсылкой Ethernet пакета с помощью функции eth_ocm_raw_send(ethmass, sizeof(ethmass)). Третья операция взводится по прерыванию на прием любого пакета (бит ETH_OCM_MODER_PRO_MSK установлен) пришедшего на eth_ocm (устанавливается переменная Start (if (i == 0 && Start == 1)) ).
Акхив с готовым проектом Ethernet приемопередатчика Nios II + eth_ocm + DE0+ SK-Ethernet Plug под Quartus 90 ниже по тексту. Наверняка будет работать и под более поздними версиями Quartus.
Кому все это пригодилось просьба отозваться в комментах к данному посту не забыв про респект автору драйвера к корке Ethernet MAC eth_ocm профессионалу форума Electronix.ru Вадиму Котельникову.
В следующем посте из серии Ethernet на Nios II о том как всю эту махарайку запустить и пронаблюдать ее в действии.
Электроника :
- Техника электроника (11)
- Полезная электроника (4)
- Электроника для всех (5)
- Техника для дома (6)
- Cхемотехника ПЛИС (11)
- Пректирование PCAD (4)
Программирование :
- Микроконтроллеры (9)
- ПЛИС VHDL Verilog (29)
- C++ Builder (7)
- Visual Studio C++ C# (7)
- Java programming (7)
- Matlab programming (4)
Сайтостроение :
- Сайтостроение HTML (5)
- Сайтостроение PHP (8)
- PHP CMS на файлах (3)
- Web инструменты (9)
- Полезное вебмастеру (11)
- SEO раскрутка сайта (4)
- PHP скрипты (3)
Реклама :
Книги и учебники :
- Шаблоны сайтов (6)
- Книги и учебники (2)
Компьютер и интернет :
Поиск по сайту :
Реклама :
Облако меток :
Бесплатная подписка :
Статистика :
- Популярность (3)
- Посещаемость (3)
- Поисковые запросы (3)