Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Чтение/Запись из HPS в FPGA и наоборот
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Системы на ПЛИС - System on a Programmable Chip (SoPC)
chipmasta
Всем привет! Я только начал вникать в мир ПЛИС но столкнулся со слишком уж большим количеством входных данных) Сам программист, решил вот с железом "пообщаться"
Задача стоит, как мне казалась простая - из 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 ?
Golikov A.
В описании аппаратуры вы работаете не с последовательностью действий как в программах, а с состояниями схемы.
Поэтому создания последовательности действий вам требуется создать конечный автомат. Так что вы выбрали не элементарную задачу, может стоит начать с чего-то по проще.

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

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

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

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

Приведённый выше кусочек кода я взял из интернета, и он создаёт инстанс, который можно просмотреть в "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 правильный получить, чтобы инстанцировать модуль этот в правильное пространство памяти?
chipmasta
Возможно я неверно решаю задачу. К примеру линукс получает данные по сети, плис эти данные должен обработать, результат вернуть в линукс, а линукс уже по сети даст ответ в сторонний сервис.
Решил сделать так:
1. в блоке always плис мониторит флаг впамяти, назовём его requestReady
2. линукс закидывает в память данные для обработки и меняет флаг requestReady на 1. и в цикле начинает мониторить флаг в памяти answerReady
3. плис увидев что requestReady == 1, забирает данные для обработки, обрабатывает и закидывает в память ответ, меняя флаг answerReady на 1
4. линукс увидев что answerReady == 1, забирает ответ сгенеренный в плисе

Верна ли логика и какое самое быстрое и оптимальное решение? Данных не много, но поток постоянный.
Из тех знаний что я собрал, решил что работа через soc_system_onchip_memory2_0 будет самым верным решением. Вот теперь пытаюсь понять как это в верилоге реализовать) Думал что это одна из самых распространённых задач, но фактически не могу найти ни одного дельного туториала. Буду благодарен за любую информацию.
Golikov A.
Цитата
в блоке 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


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


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

И я бы начал с простенького автомата без линукса, ядра и прочей фигни. Попробуйте почитать данные из одной памяти и положить в другую, например. Или посчитать их средние и записать обратно. Поймите как работают автоматы.
chipmasta
Приветствую! Благодарю за развёрнутый ответ!
Примерно так себе и представлял реализацию этого 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.gif

в 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

Всем большое спасибо за помощь!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.