Перейти к содержанию
    

Чтение/Запись из HPS в FPGA и наоборот

Всем привет! Я только начал вникать в мир ПЛИС но столкнулся со слишком уж большим количеством входных данных) Сам программист, решил вот с железом "пообщаться"

Задача стоит, как мне казалась простая - из HPS записать данные в RAM FPGA, в FPGA эти данные прочитать, и записать туда же в RAM "ответ".

Имеем : De0 Nano SoC, за основу взять проект который идёт в комплекте GHRD. Квартус 17.

Смотрим в Qsys, там уже имеется блок onchip_memory2_0, который даст нашему HPS доступ к RAM. С этим доступом вроде как всё ясно - в Си приложении работаем с /dev/mem, базовый адрес у нас по 0xC0000000. И действительно - работает. Если в блоке onchip_memory2_0 постаивть галочку "доступно для In-System memory Editor" то можно посмотреть этот RAM, увидеть и изменения сделанные со сторны HPS. Здесь всё хорошо.

А вот с чтением этих байтов в ПЛИС у меня полный ступор, от слова совсем) Я нашел в soc_system.v (который я так понял генерится из Qsys) как инстанцируется модуль onchip_memory2_0. Дальше что?

 

Я пробовал инстанцировать свой onchip_memory2_0 и даже записать туда данные -

 

soc_system_onchip_memory2_0 m(.address(0)
, .byteenable(4'b1111)
, .chipselect(1)
, .clk(FPGA_CLK1_50)
, .clken(1'b1)
, .reset(hps_fpga_reset_n)
, .reset_req(1'b0)
, .write(1'b1)
, .writedata(32'hF0F0F0F0)
, .readdata(datain)
);

 

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

 

Люди добрые, подскажите, как реализовывать в Верилоге... как будет выглядеть код который запишет/прочитает данные в этот, доступный для HPS, RAM ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

я поглядел на ваш кусочек кода и меня вот что смутило.

reset(hps_fpga_reset_n). Обычно с буковкой n на конце обозначают сигналы которые имеют активный уровень 0. То есть в этом случае, когда схема сброшена, на сигнале hps_fpga_reset_n ноль, а когда в рабочем состоянии там 1. В отличии от сигнала reset, который без n и вполне может иметь активный уровень 1, то есть когда на входе 1, схема в сбросе. Так что есть подозрение что вы в сброс загнали ваш блочек.

 

стоит попробовать reset(~hps_fpga_reset_n).

 

также имеет смысл проверить активный уровень чипселекта, он хоть записан и без n, но слишком часто бывает активным по 0.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за ответ!

 

Светодиодами я помигал уже и хочется дальше) Сейчас у молодёжи такая тенденция,- хочется всего и побыстрее)))

 

Приведённый выше кусочек кода я взял из интернета, и он создаёт инстанс, который можно просмотреть в "In-System Memory Content editor". И в этом инстансе видны записанные hF0F0F0F0 - https://www.screencast.com/t/yOkuCoK2pKxj

 

Но в сгенерированном проектом файле soc_system.v тоже инстанцируется этот блок:

soc_system_onchip_memory2_0 onchip_memory2_0 (
        .clk        (clk_clk),                                          //   clk1.clk
        .address    (mm_interconnect_0_onchip_memory2_0_s1_address),    //     s1.address
        .clken      (mm_interconnect_0_onchip_memory2_0_s1_clken),      //       .clken
        .chipselect (mm_interconnect_0_onchip_memory2_0_s1_chipselect), //       .chipselect
        .write      (mm_interconnect_0_onchip_memory2_0_s1_write),      //       .write
        .readdata   (mm_interconnect_0_onchip_memory2_0_s1_readdata),   //       .readdata
        .writedata  (mm_interconnect_0_onchip_memory2_0_s1_writedata),  //       .writedata
        .byteenable (mm_interconnect_0_onchip_memory2_0_s1_byteenable), //       .byteenable
        .reset      (rst_controller_reset_out_reset),                   // reset1.reset
        .reset_req  (rst_controller_reset_out_reset_req),               //       .reset_req
        .freeze     (1'b0)                                              // (terminated)
    );

 

От этого в "In-System Memory Content editor" у меня два инстанса (на скриншоте видно) .

 

Так вот, когда я записываю память через Си код со сдвигом к AXI порту ( 0xC0000000 ) который должен быть залинкован к soc_system_onchip_memory2_0, то мои байты записываются в 1 инстанс - https://www.screencast.com/t/5W9LjT1jjtP , а добраться во второй инстанс я не нахожу как, пробовал разные сдвиги расчитывать но безрезультатно.

 

Выходит тот инстанс который я сам создаю не связан с axi портом, а где-то "болтается" в просторах памяти с другим сдвигом.

 

Буду пробовать работать с тем инстансом который создаётся по дефолту в soc_system.v. Просто странно писать код в сгенерированном файле.

 

Надеялся что есть возможность получить память в ПЛИСЕ напряму по адресу, типа getmem[0xC0000000]

 

Всё приходится собирать по крупицам из разных источников, тема сложная, еще раз спасибо за поддержку!

 

 

 

 

 

Верно ли понимаю, что мне важно chipselect правильный получить, чтобы инстанцировать модуль этот в правильное пространство памяти?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Решил сделать так:

1. в блоке always плис мониторит флаг впамяти, назовём его requestReady

2. линукс закидывает в память данные для обработки и меняет флаг requestReady на 1. и в цикле начинает мониторить флаг в памяти answerReady

3. плис увидев что requestReady == 1, забирает данные для обработки, обрабатывает и закидывает в память ответ, меняя флаг answerReady на 1

4. линукс увидев что answerReady == 1, забирает ответ сгенеренный в плисе

 

Верна ли логика и какое самое быстрое и оптимальное решение? Данных не много, но поток постоянный.

Из тех знаний что я собрал, решил что работа через soc_system_onchip_memory2_0 будет самым верным решением. Вот теперь пытаюсь понять как это в верилоге реализовать) Думал что это одна из самых распространённых задач, но фактически не могу найти ни одного дельного туториала. Буду благодарен за любую информацию.

Изменено пользователем chipmasta

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в блоке always плис мониторит флаг впамяти, назовём его requestReady

Эта фраза выдает что вы немного не понимаете как оно все устроено. Блок алвайс в плис это не совсем while в С.

 

В целом кроме памяти в ПЛИС есть еще и регистры, которые для проца выглядят как память, а для плис как удобные сигналы, но это детали.

Попробуем поработать с решением в вашей постановке.

 

Вам нужен конечный автомат FSM по буржуйски. У него должны быть состояния

IDLE - в этом состоянии ПЛИС читает ячейку памяти с флагом и если видит флаг переходит в READ

READ - в этом состоянии плис перебирает адреса памяти, чтобы на шине появлялись данные, которые забирает блок обработки. Прочитав нужное количество переходит в PROCESS

PROCESS - в этом состоянии плис ждет когда блок обработки закончит обработку и сформирует ответ. Дождавшись переходит в WRITE

WRITE - в этом состоянии плис перебирает адреса для записи памяти, а блок обработки отдает данные, они попадаю в память. Записав нужное количество плис переходит в DONE

DONE - в этом состоянии плис ставит флаг готовности данных, записывает его в нужную ячейку памяти и переходит в IDLE

 

Конечный автомат описывается так.

 

localparam IDLE = 0;
localparam READ = 1;
....

always @(posedge clk)
begin
   if(reset) State <= IDLE; 
   else State <= NextState;
end

always @(*)
begin
  if((State == IDLE) && (requestReady == 1'b1))         NextState = READ;
  if((State == READ) && (RCounter == 0))                  NextState = PROCESS;
  if((State == PROCESS) && (ProcessDone == 1'b1))   NextState = WRITE;
  if((State == WRITE) && (WCounter == 0))               NextState = DONE;
  if(State == DONE)                                                   NextState = IDLE;
end

 

выше приведенный код ходит по состояниям, переводя автомат из одного в другое. Теперь вам надо описать действия внутри этих состояний, например

 

always @(posedge clk)
begin
if(State == IDLE)
    begin
      MemAddr <= FlagAddr;
      if(MemAddr == FlagAddr)
        requestReady <= MemData[0];
      RCounter <= 10;
      WCoutner <= 10;
    end
end

always @(posedge clk)
begin
if(State == READ)
    begin
      MemAddr <= MemAddr  + 1'b1;
      RCounter <= RCounter  - 1'b1;
    end
end

 

и так далее...

 

 

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

 

И я бы начал с простенького автомата без линукса, ядра и прочей фигни. Попробуйте почитать данные из одной памяти и положить в другую, например. Или посчитать их средние и записать обратно. Поймите как работают автоматы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую! Благодарю за развёрнутый ответ!

Примерно так себе и представлял реализацию этого FSM с состояниями.

 

Очень интересует непосредственно работа с RAM, вы используете MemAddr, но что это должно быть? если это будет просто reg то как указать что этот reg должен обращаться к RAM а не к регистрам плис, далее как получить нужный базовый адрес этого axi порта, по которому HPS обращается к памяти...

Другими словами я не могу понять как из плиса прочитать или записать в определённый участок RAM, и именно тот участок к которому hps обращается. Толи этот доступ суперпримитивно делается что никто не пишет, то ли секретная магия, потому что конкретной реализации я не могу найти в примерах. Нужно ли для такой записи/чтения использовать soc_system_onchip_memory2_0 компонент...

 

( тем временем курю вот эту книгу http://www.dsol.ru/stud/book7/index_annotation.html )

 

Есть дока по этой памяти - https://www.altera.com/content/dam/altera-w.../cyc_c51007.pdf без примеров))

Изменено пользователем chipmasta

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продолжая рассуждения... :rolleyes:

 

в Qsys у нас есть инстанс этого onchip_memory2_0 https://screencast.com/t/GFn3nu9x , из HPS модуля в него уходит сигнал, именно этот HPS модуль и исопльзуется линуксом, и именно по этому линукс имеет доступ к RAM через этот onchip_memory2_0. Следовательно в этот же порт нужно стучаться из плис. Может есть идеи как это сделать? Может как-то из кода напрямую можно туда ходить, или нужно добавить еще компонент в Qsys типа PIO и из кода с этим PIO уже общаться... Так-же у onchip_memory2_0 есть опция dual-port судя по всему как раз для моей цели, но всё равно не понятно что в порт должно заходить чтобы из верилог кода был доступ)

Изменено пользователем chipmasta

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Победил!

Нужно в Qsys указать dual-port для этого onchip_memory, подключаем к клоку и тд, на s2 я ничего не подключал https://screencast.com/t/qzC2b9Qh7BIi

 

далее в сгенеренном Qsys'ом файле дописал тестовый код-

 

soc_system_onchip_memory2_0 onchip_memory2_0 (
        .clk         (clk_clk),                                          //   clk1.clk
        .address     (mm_interconnect_0_onchip_memory2_0_s1_address),    //     s1.address
        .clken       (mm_interconnect_0_onchip_memory2_0_s1_clken),      //       .clken
        .chipselect  (mm_interconnect_0_onchip_memory2_0_s1_chipselect), //       .chipselect
        .write       (mm_interconnect_0_onchip_memory2_0_s1_write),      //       .write
        .readdata    (mm_interconnect_0_onchip_memory2_0_s1_readdata),   //       .readdata
        .writedata   (mm_interconnect_0_onchip_memory2_0_s1_writedata),  //       .writedata
        .byteenable  (mm_interconnect_0_onchip_memory2_0_s1_byteenable), //       .byteenable
        .reset       (rst_controller_reset_out_reset),                   // reset1.reset
        .reset_req   (rst_controller_reset_out_reset_req),               //       .reset_req
        .address2    (0),                                                 //     s2.address
        .chipselect2 (1'b1),                                                 //       .chipselect
        .clken2      (1'b1),                                                 //       .clken
        .write2      (1'b1),                                                 //       .write
        .readdata2   (datain),                                                 //       .readdata
        .writedata2  (32'hF0F0F0),                                                 //       .writedata
        .byteenable2 (4'b1111),                                                 //       .byteenable
        .clk2        (clk_clk),                                          //   clk2.clk
        .reset2      (rst_controller_reset_out_reset),                   // reset2.reset
        .reset_req2  (rst_controller_reset_out_reset_req),               //       .reset_req
        .freeze      (1'b0)                                              // (terminated)
    );

 

Запускаем, смотрим память в Си и видим наши байтики - https://screencast.com/t/HUUN9xW0

 

Всем большое спасибо за помощь!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...