воскресенье, 27 февраля 2011 г.

Опыт - сын ошибок трудных...

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

На другом конце провода сидит большая и умная лисповая программа, которая всё знает и всё умеет. Общается она с железом по RPC-подобному протоколу. Разумеется, кишки сетевого взаимодействия спрятаны глубоко в DSL, снаружи есть просто объекты CLOS (ну, почти CLOS) и их методы. Чтение регистра ядра - это вызов метода-акцессора соответствующего объекта, с записью то же самое.

Далее, адресное пространство ядер имеет 32-битную гранулярность, т.е. чтение/запись может производиться только такими порциями. Логические единицы (регистры) могут быть 32-битными, а могут и отличаться. Например, в одном физическом регистре могут быть упакованы битовые флаги или лежать пара 16-битных счётчика. Или два MAC-адреса (6 байт каждый) упакованы в три регистра.

С 32-битными логическими регистрами всё просто - читай, да пиши, как есть. С битовыми картами чуть посложнее, нужно извлекать или устанавливать нужный бит, не трогая остальные. Вообще, если логический регистр лежит в пределах одного физического, то проблем особых нет. Я по-началу логику на сдвигах и масках накатал, всё хорошо, но потом наткнулся на MAC-адресы, которые в 32 бита не помещались.

Посидел, погрустил, что пересечение границы физического регистра в существующую картину мироздания ну совсем никак не вписывается, и что доработка генератора акцессоров легко и просто превратится в монстра, который я неделю писать и отлаживать буду (генератор много других приколов содержит). По-сути, необходимо писать логику извлечения произвольной части из адресного пространства. А это как раз то, чем занимается стандартная функия ldb. Собственно, мой генератор акцессоров был полу-ldb-велосипедом :)

Отобразил пространство ядра на одно целое число, хитрожопые акцессоры заменил на простой ldb, который легко работает и с большими числами. При записи пишу в копию отображения, на другую сторону шлю разницу между старым состоянием и новым. Если даже ldb на большом числе работает крайне неэффективно, то это всё равно по-барабану, т.к. один RPC-вызов по сети сожрёт в тыщи раз больше времени. Так вот одним ударом серпа решил абсолютно все проблемы и некрасивости в DSL.


Пример описания интерфейса ядра (стандартный альтеровский Media Access Control, по-моему):

(def-core eth-core (:id #x42
                    :access :read-write)
  (instance)
  (:pack
   (enable :type :bit :access :write)
   (reset :type :bit :access :write))
  (:pack
   (tx-count :type :uint16)
   (err-count :type :uint16))
  (page-number)
  (:pack
   (dst-mac :type :uint48)
   (src-mac :type :uint48))
  (src-ip)
  (dst-ip)
  (checksum-base)
  (:pack
   (src-port :type :uint16)
   (dst-port :type :uint16))
  (:pack
   (tx-count-page :type :uint16)
   (err-count-page :type :uint16)))

Объекты создаются стандартным make-instance. Установка MAC-адресов выглядит вот так:

(setf (reset eth-core) t
...
      (dst-mac eth-core) dst-mac-addr
      (src-mac eth-core) src-mac-addr
...
      (enable eth-core) t)

Это делается с клиента через скрытые RPC-вызовы. А было примерно вот так:

typedef enum eth_core {
    ethInstance=0x0,
    ethEnableAndRst=0x1,
    ethTxAndErrCount=0x2,
    ethPageNumber=0x3,
    ethDstMacMsb=0x4,
    ethDstMacLsbSrcMacMsb=0x5,
    ethSrcMacLsb=0x6,
    ethIpSrc=0x7,
    ethIpDst=0x8,
    ethChecksumBase=0x9,
    ethUdpSrcAndDstPorts=0xA,
    ethTxAndErrCountPage=0xB,
    ethChecksum=0xC,
} ethCore_t;

uint32_t pCore_eth = ...;

pCore_eth[ethEnableAndRst] = 0x2;    /* reset */
...
pCore_eth[ethDstMacMsb] =
    dst_mac_addr[0] << 24 |
    dst_mac_addr[1] << 16 |
    dst_mac_addr[2] << 8  |
    dst_mac_addr[3];
pCore_eth[ethDstMacLsbSrcMacMsb] =
    dst_mac_addr[4] << 24 |
    dst_mac_addr[5] << 16 |
    src_mac_addr[0] << 8  |
    src_mac_addr[1];
pCore_eth[ethSrcMacLsb] =
    src_mac_addr[2] << 24 |
    src_mac_addr[3] << 16 |
    src_mac_addr[4] << 8  |
    src_mac_addr[5];
...
pCore_eth[ethEnableAndRst] = 0x1;    /* enable */

Это делалось на старой версии сервера, в адресном пространстве которого мапятся ядра с их регистрами.

Вся крутость DSL тут не видна, в нём ещё есть плюшки типа возожности описания массивов, автомагическая генерация веб-страничек (у нас своя объектная система через MOP построена), прозрачная синхронизация состояния после сбоев и т.д.

Ну и всё это делается в рантайме: юзер пишет описание железки на DSL и инструкции, как всё это конфигурируется, подсовывает уже работающему софту, а тот генерирует развесистую лапшу.

2 комментария:

  1. Вот интересно, а насколько глубоко в такой работе нужно знать весь этот схемотех, ПЛИСы и прочее? Все ли замешанные программисты с этими железками "на ты", или есть как совсем жесткие ембеддщики, так и совершенно ничего в нём не понимающие?

    ОтветитьУдалить
  2. Есть проектировщики железа, есть vhdl-программисты, есть embedded C-программисты, есть линуксовые программисты, есть линуксовые драйверисты, есть лисперы.

    Есть основатель стартапа, который умеет всё. Остальные умеют только часть, например, vhdl и embedded C.

    Я в проектировании железа и vhdl вообще ничего не понимаю, с остальным более-менее. Но с vhdl планирую разобраться. Уж больно востребованный скилл в этой области :)

    ОтветитьУдалить

Архив блога