|
Чтение/Запись из HPS в FPGA и наоборот |
|
|
|
Nov 13 2017, 19:18
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Всем привет! Я только начал вникать в мир ПЛИС но столкнулся со слишком уж большим количеством входных данных) Сам программист, решил вот с железом "пообщаться" Задача стоит, как мне казалась простая - из 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 ?
|
|
|
|
|
 |
Ответов
(1 - 7)
|
Nov 13 2017, 19:55
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
В описании аппаратуры вы работаете не с последовательностью действий как в программах, а с состояниями схемы. Поэтому создания последовательности действий вам требуется создать конечный автомат. Так что вы выбрали не элементарную задачу, может стоит начать с чего-то по проще.
я поглядел на ваш кусочек кода и меня вот что смутило. reset(hps_fpga_reset_n). Обычно с буковкой n на конце обозначают сигналы которые имеют активный уровень 0. То есть в этом случае, когда схема сброшена, на сигнале hps_fpga_reset_n ноль, а когда в рабочем состоянии там 1. В отличии от сигнала reset, который без n и вполне может иметь активный уровень 1, то есть когда на входе 1, схема в сбросе. Так что есть подозрение что вы в сброс загнали ваш блочек.
стоит попробовать reset(~hps_fpga_reset_n).
также имеет смысл проверить активный уровень чипселекта, он хоть записан и без n, но слишком часто бывает активным по 0.
|
|
|
|
|
Nov 13 2017, 22:23
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Спасибо за ответ! Светодиодами я помигал уже и хочется дальше) Сейчас у молодёжи такая тенденция,- хочется всего и побыстрее))) Приведённый выше кусочек кода я взял из интернета, и он создаёт инстанс, который можно просмотреть в "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 правильный получить, чтобы инстанцировать модуль этот в правильное пространство памяти?
|
|
|
|
|
Nov 14 2017, 00:02
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Возможно я неверно решаю задачу. К примеру линукс получает данные по сети, плис эти данные должен обработать, результат вернуть в линукс, а линукс уже по сети даст ответ в сторонний сервис. Решил сделать так: 1. в блоке always плис мониторит флаг впамяти, назовём его requestReady 2. линукс закидывает в память данные для обработки и меняет флаг requestReady на 1. и в цикле начинает мониторить флаг в памяти answerReady 3. плис увидев что requestReady == 1, забирает данные для обработки, обрабатывает и закидывает в память ответ, меняя флаг answerReady на 1 4. линукс увидев что answerReady == 1, забирает ответ сгенеренный в плисе
Верна ли логика и какое самое быстрое и оптимальное решение? Данных не много, но поток постоянный. Из тех знаний что я собрал, решил что работа через soc_system_onchip_memory2_0 будет самым верным решением. Вот теперь пытаюсь понять как это в верилоге реализовать) Думал что это одна из самых распространённых задач, но фактически не могу найти ни одного дельного туториала. Буду благодарен за любую информацию.
Сообщение отредактировал chipmasta - Nov 14 2017, 00:02
|
|
|
|
|
Nov 14 2017, 06:46
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата в блоке 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 и так далее... Обратите внимание что часто нужны какие-то однобитные сигналы, желательно появляющиеся сразу, без необходимости что-то делать, выставлять адрес и так далее... Поэтому обычно это решают не через память, а через регистры. Делают блок который висит на вашей шине и когда видит обращение по определенному адресу сохраняет данные с шины в регистры, которые доступны другим блокам как сигналы или команды. В ПЛИС все происходит параллельно и у нее есть альтернативные варианты хранения нежели память, нет абсолютно никакой необходимости вести управляющий обмен через блоки памяти. И я бы начал с простенького автомата без линукса, ядра и прочей фигни. Попробуйте почитать данные из одной памяти и положить в другую, например. Или посчитать их средние и записать обратно. Поймите как работают автоматы.
|
|
|
|
|
Nov 14 2017, 13:44
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Приветствую! Благодарю за развёрнутый ответ! Примерно так себе и представлял реализацию этого 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 - Nov 14 2017, 13:23
|
|
|
|
|
Nov 14 2017, 16:43
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Продолжая рассуждения... в Qsys у нас есть инстанс этого onchip_memory2_0 https://screencast.com/t/GFn3nu9x , из HPS модуля в него уходит сигнал, именно этот HPS модуль и исопльзуется линуксом, и именно по этому линукс имеет доступ к RAM через этот onchip_memory2_0. Следовательно в этот же порт нужно стучаться из плис. Может есть идеи как это сделать? Может как-то из кода напрямую можно туда ходить, или нужно добавить еще компонент в Qsys типа PIO и из кода с этим PIO уже общаться... Так-же у onchip_memory2_0 есть опция dual-port судя по всему как раз для моей цели, но всё равно не понятно что в порт должно заходить чтобы из верилог кода был доступ)
Сообщение отредактировал chipmasta - Nov 14 2017, 17:34
|
|
|
|
|
Nov 14 2017, 18:15
|
Группа: Участник
Сообщений: 6
Регистрация: 13-11-17
Пользователь №: 100 178

|
Победил! Нужно в 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Всем большое спасибо за помощь!
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|