2012-01-13 7 views
1

Я разрабатываю основное ядро ​​обработки изображений для FPGA и ASIC. Идея состоит в том, чтобы связать с ним стандартный процессор. Одна из проблем, которые у меня есть, - это «запрограммировать» это. Позвольте мне объяснить: у ядра есть декодер команд для моих «пользовательских» расширений. Например:Как включить и перевести пользовательские инструкции/расширение на стандартном уровне поддержки кода C/C++

vector_addition $vector[0], $vector[1], $vector[2] // (i.e. v2 = v0+v1) 

и многие другие подобные этому. Эта операция отослана процессором через шину к ядру, используя процессор для петель, не-векторных операций и т.д., как это:

for (i=0; i<15;i++)   // to be executed in the processor 
    vector_add(v0, v1, v2) // to be executed in my custom core 

Программа написана на C/C++. Ядро нужно только саму команду, в машинном коде

  1. опкод = vector_add = 0x12h
  2. register_src_1 = v0 = 0x00h
  3. register_src_2 = v1 = 0x01h
  4. register_dst = v2 = 0x02h

    машинный код = opcore | v0 | v1 | v2 = 0x7606E600h

(или любой другой, просто contatenation различных областях, чтобы построить команду в двоичный)

После отправки его через шину к ядру, ядро ​​может запросить все данные память с выделенными шинами и обрабатывать все без использования процессора. Большой вопрос: Как я могу перевести предыдущую инструкцию в шестнадцатеричное представление? (отправить его через автобус не проблема). Некоторые варианты, которые приходят на ум

  • Run интерпретируемый код (перевести в машинный код во время выполнения в процессоре) -> очень медленно, даже с помощью какой-то инлайн макросъемки
  • Компиляция пользовательских разделов с внешний пользовательский компилятор, загружает двоичный файл из внешней памяти и перемещает его в ядро ​​с помощью некоторой уникальной команды -> трудно читать/понимать исходный код, слабую интеграцию с SDK, слишком много разделов, если код очень сегментирован
  • JIT-компиляция - -> комплекс только для этого?
  • Расширение компилятора -> кошмар!
  • Пользовательский процессор, соединенный с настраиваемого ядра для обработки все: петли, указатели, выделение памяти, переменные ... -> слишком много работы

Проблема заключается в том о программном обеспечении/составителей, но для тех, имеют глубокие знания в этой теме, это SoC в FPGA, основным процессором является MicroBlaze, а IP Core использует шины AXI4.

Надеюсь, я объяснил это правильно ... Спасибо заранее!

+0

Может быть, мне нужно короткое мое предложение ... Как добавить новые инструкции к сценарию [Code Generation] (http://en.wikipedia.org/wiki/Code_generation_%28compiler%29), который я использую (gcc/g ++) – amnl

ответ

0

Не могли бы вы перевести все ваши разделы кода в машинный код в начале программы (только один раз), сохранить их в двоичном формате в блоках памяти и затем использовать эти двоичные файлы, когда это необходимо.

В основном это работает с шейдерами OpenGL, и я считаю, что это довольно легко управлять.

Основным недостатком является потребление памяти, так как у вас есть в памяти как текстовое, так и двоичное представление одних и тех же скриптов. Я не знаю, если это проблема для вас. Если это так, есть частичные решения, как разгрузка исходных текстов после их компиляции.

+0

да, это хорошая идея, спасибо! Я подумал что-то подобное. Проблема в том, что интеграция очень плохая, потому что весь код должен находиться в одной и той же части программы, когда на самом деле он распределяется по всей программе. Подумайте о инструкциях SSE, это точно та же проблема, что и у меня. – amnl

+0

Вы можете создать синглтонную «фабрику сценариев» так или иначе, и определить источники по имени или что-то в этом роде. Или зарегистрируйте их при первом использовании. Существует множество способов улучшить интеграцию на ее основе. – rodrigo

+0

У меня есть редкие инструкции по всему коду, в середине циклов, if-else и т. Д. И многие из них. Проблема с их «прекомпиляцией» заключается в том, что некоторые аргументы этих функций вычисляются в процессоре, а затем передаются внутри инструкции. Если аргумент изменяет значение во время выполнения (т. Е. Индекс цикла), он не будет работать ... т.е. vector_add (v0, v1, #constant) – amnl

1

Я не уверен, что полностью понимаю, но я думаю, что раньше я сталкивался с чем-то подобным. Основываясь на комментарии к ответу Родриго, это звучит так, будто у вас небольшие инструкции, разбросанные по вашему коду. Вы также отмечаете, что внешний компилятор возможен, просто боль. Если вы комбинируете внешний компилятор с макросом C, вы можете получить что-то приличное.

Рассмотрим этот код:

for (i=0; i<15;i++) 
    CORE_EXEC(vector_add(v0, v1, v2), ref1) 

CORE_EXEC макрос будет служить двум целям:

  1. Вы можете использовать внешний инструмент для сканирования исходных файлов для этих записей и компиляции основного кода. Этот код будет связан с C (просто создайте файл C с бинарными битами), используя имя «ref1» в качестве переменной.
  2. В C вы определяете макрос CORE_EXEC, чтобы передать строку «ref1» в ядро ​​для обработки.

Так этап 1 будет производить файл скомпилированных двоичных инструкций ядра, например, выше может иметь строку, как это:

const char * const cx_ref1[] = { 0x12, 0x00, 0x01, 0x02 }; 

И вы могли бы определить CORE_EXEC так:

#define CORE_EXEC(code, name) send_core_exec(cx_##name) 

Очевидно, вы можете выбрать префиксы, но вы хотите, хотя на C++ вы, возможно, захотите использовать пространство имен.

С точки зрения инструментальной цепочки вы можете создать один файл для всех своих битов или создать один файл на файл C++, что может быть проще для обнаружения загрязнений. Затем вы можете просто включить сгенерированные файлы в исходный код.

+0

Мне нравится идея. «Внешний компилятор» может быть просто ассемблером для машинного кодового переводчика, объединяя каждую поданную команду, как я показал на примере вопроса. Но, как я ответил на @rodrigo, некоторые параметры вычисляются во время выполнения. Как я могу столкнуться с этим «особым» случаем? – amnl

+0

Это зависит от того, насколько сложны параметры. Вы можете изменить этот подход на создание макроса __VA_ARGS__, который принимает ссылку, имя функции и параметры. Затем вместо фиксированной строки вы можете иметь встроенную функцию, которая быстро отображает определенные параметры в эквивалент байта. Или вы можете создать уникальный макрос для каждого кода и сгенерировать полный макрос, например, 'CORE_EXEC_REF1'. –

0

Допустим, я собирался изменить ядро ​​руки, чтобы добавить некоторые пользовательские инструкции, а операции, которые я хотел запустить, были известны во время компиляции (они будут доступны для работы в секунду).

Я хотел бы использовать сборки, например:

.globl vecabc 
vecabc: 
    .word 0x7606E600 ;@ special instruction 
    bx lr 

или встраивать его с тем, что синтаксис рядного для вашего компилятора это делает его более трудным, если вам нужно использовать регистры процессора, например, когда с компилятором заполняет регистры на встроенном ассемблере, тогда ассемблер собирает эти инструкции. Я нахожу написание фактического asm и просто вводя слова в поток команд, как указано выше, только компилятор выделяет некоторые байты как данные и некоторые байты в качестве инструкций, ядро ​​увидит их в порядке, как написано.

Если вам нужно сделать что-то в реальном времени, вы можете использовать самомодифицирующийся код, снова мне нравится использовать asm для батута.Строит инструкцию вы хотите запустить где-то в оперативной памяти, скажем, по адресу 0x20000000, то есть батут называют его:

.globl tramp 
tramp: 
    bx r0 ;@ assuming you encoded a return in your instructions 

вызова его

tramp(0x20000000); 

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

+0

mmm ... Подумайте об этом и просмотрите asm, я не уверен, что я его поймал. Благодаря ;) – amnl