Jump to content

    

Ethernet коммутатор L2 буферизация и память

Всем здравствуйте.

Довелось заниматься разработкой Ethernet 100 коммутатора L2 на ПЛИС. Здесь, для примера возьмём 4х портовый.

 

Моя проблема - я не до конца понимаю, как должен буферизироваться трафик. То, как я сделал: В каждом порте стоит 2 FIFO - TX/RX. Пакет пришёл, обработался, передался из буфера порта 1 в буфер порта 2. Вроде, всё ок, всё работает. Но возникают проблемы:

1. Блокировка. Текущий пакет не будет передан, пока занят выходной буфер. Это тормозит все последующие пакеты в очереди.

2. Нерациональное использование памяти. В каждом из портов используются буфера 1520*количество портов байт, для того, что бы успевать обработать все, без потери пакеты.

3. Большая утилизация памяти ПЛИС. Логики используется не так уж много, но вот затраты памяти растут катастрофически. И, главное, мне непонятно, как можно использовать внешнюю память?

 

Если 1-2 как-то терпимо, то 3й пункт очень мешает. Я знаю про round-robin и тому подобные вещи, но не могу понять, как это применить без огромного перетактирования. Например, у меня внутренние шины работают на 125 МГц*8 бит. "В лоб" это означает, что мне нужно (125*количество портов) МГц частоты памяти, что бы сложить туда все порты параллельно.

 

Так же вся внешняя память, как правило, 1-2 портового исполнения. Как туда затолкнуть чтение-запись из 4, 8 портов параллельно?

 

В интернетах нашел много общих слов на эту тему: виртуальные входные очереди, динамически распределяемая память. Но конкретного примера, в котором описан алгоритм работы с единым буфером для множественных каналов БЕЗ большого перетактирования не встретил. Да и вообще, что у буржуев, что у нас вразумительных и четких примеров не получилось найти.

 

Собственно, основной вопрос к знающим людям: как, вообще, это делается? Как работать с памятью в коммутаторе?

 

Спасибо.

Share this post


Link to post
Share on other sites

Переделайте внутренние шины на размер 2*N*8 бит, где N - количество портов. К каждому из портов у Вас будет дополнительно два небольших FIFO данных, с одной стороны 2*N*8, с другой 8 бит. Почему есть множитель 2 - потому что каждый порт в обе стороны работает.

В терминах аппаратуры лучше работать, видимо, вообще с FIFO. Общее поле памяти разбивается на блоки размером с пакет (размер шины внутренней памяти - 2*N*8).

Есть FIFO номеров пустых блоков, которое изначально заполнено номерами всех доступных блоков.

Если какому-то каналу нужен блок (у него RXDV активизировался), то он вычитывает номер нового блока из этого FIFO. Конечно, т.к. FIFO номеров один, то должен быть предусмотрен арбитр. Но время на этот доступ есть, т.к. заполняется FIFO данных, о котором я говорил в самом начале.

Далее, у каждого канала есть FIFO номеров для передачи.

После заполнения блока определяется, в какой канал его передавать, и в FIFO номеров выходного канала заталкивается номер блока.

Ну и передатчик тривиален - если появился номер в FIFO, то передаем блок с этим номером, после окончания передачи запихиваем уже освободившийся номер в FIFO пустых блоков.

Итого нужно реализовать самое главное - FIFO, у которого может быть много источников и много приемников. С арбитражем, естественно.

Хотя пожалуй даже и арбитр не нужен, ибо он сложный. Просто работа с FIFO номеров делится на N тактов, в каждом такте доступ имеет только один канал. Да и работа с общим полем памяти разбивается точно так же, только 2*N, потому что отдельно прием и передача.

В общем да, самый простой способ такой. Скажем, у Вас 8 портов. Тогда вся работа с памятью происходит за 16 тактов. В каждом такте доступ ко всем FIFO и общему полю памяти разрешен каналу n*2+dir, где n - номер, dir - направление (0 или 1). Маленькие FIFO данных в каждом канале позволяют дождаться нужного слота доступа без потери данных. Ширина шин данных общего поля памяти будет 2*8*8=128 бит. Все.

Share this post


Link to post
Share on other sites
1 hour ago, Rst7 said:

... Все.

Спасибо большое, очень хорошее и доступное описание. Хочу уточнить пару нюансов:

Quote

В терминах аппаратуры лучше работать, видимо, вообще с FIFO. Общее поле памяти разбивается на блоки размером с пакет (размер шины внутренней памяти - 2*N*8).

Если чуть усложнить - можно передавать указатели на начало/конец пакета, что бы не было пустых слов в блоке. Но в приведенном вами простейшем случае мы бьём общий пулл на много блоков, фиксированной длины (максимальный размер пакета), я правильно понял?

Quote

К каждому из портов у Вас будет дополнительно два небольших FIFO данных, с одной стороны 2*N*8, с другой 8 бит

Здесь получается преобразование разрядности? А как обрабатываются пакеты длины не кратной 2*N*8? Какой-то дополнительный маркер вводится?

Quote

Да и работа с общим полем памяти разбивается точно так же, только 2*N, потому что отдельно прием и передача.

Это означает, что нужно либо увеличить частоту работы с общим полем в 2 раза относительно шины данных, либо потерять половину пропускной способности? Ну, или предположить, что общее поле двухпортовое.

Share this post


Link to post
Share on other sites
29 minutes ago, nice_vladi said:

Если чуть усложнить - можно передавать указатели на начало/конец пакета, что бы не было пустых слов в блоке. Но в приведенном вами простейшем случае мы бьём общий пулл на много блоков, фиксированной длины (максимальный размер пакета), я правильно понял?

Это очень плохой план делать аналог менеджера кучи с произвольным размером блоков. а) Замучаетесь реализовывать, а когда реализуете - б) фрагментация потом все убъет. В таких случаях всегда делаются блоки фиксированного размера, а дальше пока проще для начала реализовать вариант с блоками максимальной длины. Потом можно будет приделать туда блоки размером короче, чем максимальный пакет, скажем, по 128 байт. Или по 256.

31 minutes ago, nice_vladi said:

Здесь получается преобразование разрядности? А как обрабатываются пакеты длины не кратной 2*N*8? Какой-то дополнительный маркер вводится?

Ну так как основное поле памяти - это не FIFO, а обычная RAM, то можно просто в первой ячейке блока хранить его длину в байтах. Приняли пакет, записали в нулевую ячейку размер, а потом номер пакета положили в FIFO номеров нужного передатчика. Передатчик взял из своего FIFO номеров номер пакета, прочитал первую ячейку, сохранил в счетчик количество байт и пошел выдавать остальное.

33 minutes ago, nice_vladi said:

Это означает, что нужно либо увеличить частоту работы с общим полем в 2 раза относительно шины данных, либо потерять половину пропускной способности? Ну, или предположить, что общее поле двухпортовое.

Зачем? Не надо увеличивать частоту. Вы разрядностью все делаете. Увеличиваете ее так (*2*N), что общая пропускная способность осталось той же.

Share this post


Link to post
Share on other sites
Quote

Зачем? Не надо увеличивать частоту. Вы разрядностью все делаете. Увеличиваете ее так (*2*N), что общая пропускная способность осталось той же.

@Rst7 для 8 портов тогда будет 8*8*2 = 128 бит, как вы писали выше. Для 16 - уже 256. Это ок? На мой взгляд, 16 портов на 125 МГц даже для современных ПЛИС не так уже легко... Повторюсь, я не знаю, как правильно, просто рассуждаю. Бесконечно увеличивать разрядность мы же не сможем.

Про разбиение буфера на какие-то атомарные куски - круто, спасибо.

Share this post


Link to post
Share on other sites
2 hours ago, nice_vladi said:

Это ок?

Ну а какие проблемы? Шин, конечно, много, к сожалению, z-состояние внутри FPGA недоступно, с ним, конечно, намного меньше. Увеличение разрядности упирается только в ресурсы FPGA как по лутам, так и по трассировке. Которых явно больше, чем мегагерц.

Share this post


Link to post
Share on other sites
5 minutes ago, Rst7 said:

Ну а какие проблемы? Шин, конечно, много, к сожалению, z-состояние внутри FPGA недоступно, с ним, конечно, намного меньше. Увеличение разрядности упирается только в ресурсы FPGA как по лутам, так и по трассировке. Которых явно больше, чем мегагерц.

Спасибо за разъяснения, буду обдумывать

Share this post


Link to post
Share on other sites

Еще может помочь описание свичей от марвел. Не знаю как сейчас, а раньше там была описана организация движка. Сам свич был со внутренней памятью, порядка 256килобайт. ЕМНИП, все адресное пространство делилось на блоки по 64 байта, туда укладывались данные и к ним списки контекстов, с дескрипторами того, что храниться в памяти)  Можно поискать старую документацию)

Share this post


Link to post
Share on other sites

Наткнулся в сети на довольно интересные материалы по теме, да ещё и на русском. Судя по всему, будет и продолжение этих статей. Ссылки:

https://nag.ru/articles/reviews/106515/intellekt-seti-kak-peredat-paket.html

https://nag.ru/articles/reviews/106886/pamyat-seti-kak-sohranit-paket.html

Так же нашёл очень интересную книжку которая затрагивает в том числе и организацию и структуру свитчей:

Network Routing. Algorithms, Protocols, and Architectures. Deepankar Medhi, Karthikeyan Ramasamy

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now