Jump to content

    

HardRock

Участник
  • Content Count

    43
  • Joined

  • Last visited

Community Reputation

0 Обычный

About HardRock

  • Rank
    Участник

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Функция обратно PWM генератору. На входе с физики PWM - на выходе ширина пульса в память. Что-то с ходу не получается придумать алгоритм для обхода всех один автоматом чтобы это было эффективнее чем просто N отдельных генераторов. Допустим с генерацией PWM, все активные выходы (их маппинг) помещается в вектор (это скорее всего синтезируется в мультиплексоры). На каждом клоке нужно пройти по списку активных генераторов и погасить / зажечь нужные выходы в зависимости от текущего каунтера и заданной ширины пульса. Экономия может быть только на счетчиках в каждом генераторе, но это минимально. С декодерами сложнее, сигнал на их входе не синхронный. Не представляю как реализовать замер ширины пульса для каждого в одном цикле, вырождается в отдельные декодеры как ни крути. Да и в любом случае жрет место именно коммутация, к ней вопросы. Всё остальное - ничтожно мало.
  2. 1 SPI Slave, 32 PWM генератора, 32 PWM декодера, 4 UART RX / TX с фиксированным рейтом. Коммутация между всем кроме SPI, он отдельно. Циклон да, самый маленький - EP4CE6E, такой на девборде распаян, так что пока без вариантов) Да и следующего на 10К ячеек тоже не хватит, а всё что дальше слишком большое физически. Что-то типа того :)
  3. Есть набор выводов плиса. Есть набор приёмников и передатчиков, реализованных в плисе. Каждый вывод, в зависимости от настроек, может быть сконфигурирован как вход, выход или третье состояние. Если как вход, то его можно подключить к заданному приемнику. Если как выход, то к заданному передатчику либо к другому выходу чтобы дублировал состояние. Нужно 32 вывода, соответственно 32 приёмника и передатчика одного типа и штуки 2-4 другого типа. На 16 каналов ёмкости плиса хватает. На 32 - превышение почти в 2 раза) Рост линейный, но общее потребление велико. Можно сделать как планировал изначально чтобы к каждому выводу был свой приёмник и передатчик тк их число должно быть равно чтобы обеспечить "все на приём" и "все на передачу". Это должно уменьшить сложность. Пока сделано что вывод назначается, это удобно для пользователя т. к. очень гибко настраивается, но конечно, потребляет элементы. Вопрос наверно более концептуальный на тему как лучше делать подобного рода коммутаторы. Как это сделать на "корпусах" мультиплексорах и демультиплексорах понимаю, в RTL нечто подобное, но есть ощущение что перерасход элементов. Плюс ко всему ещё память не оптимизировал чтобы использовалась блочная, она ещё реализуется синтезатором, и видимо на стыке коммутатора и памяти происходит перерасход. Вобщем проект с желаемым функционалом не влазит в желаемый плис пока)
  4. Как можно более эффективно сделать коммутацию сигналов по типу каждый с каждым в зависимости от настроек? Сделал по-программистски, завел вектора сигналов и перекладываю их в каждом клоке. Однако, синтез такого тратит много логических элементов. Как оптимизировать? always(@posedge clk) begin ... // ------------------------------------------ // - IO Channels commutation logic // ------------------------------------------ for (io_idx = 0; io_idx < IO_CHANNELS_COUNT; io_idx++) begin case (__REG_MEMORY__.io_channels[io_idx].mode) // - Disabled 'h00: __IO_CHANNELS__[io_idx] = 'bZ; // - Channel 'h01: __IO_CHANNELS__[io_idx] = __IO_CHANNELS__[__REG_MEMORY__.io_channels[io_idx].resource]; // - Input :: PWMDecoder 'h02: __PWM_DECODER_IN_TABLE__[__REG_MEMORY__.io_channels[io_idx].resource] = __IO_CHANNELS__[io_idx]; //'h03: // - Output :: PWMEncoder 'h04: __IO_CHANNELS__[io_idx] = __PWM_ENCODER_OUT_TABLE__[__REG_MEMORY__.io_channels[io_idx].resource]; //'h05: endcase end end
  5. Я думаю нужно исходить из задачи и её вводных. Где-то лучше отладка в железе, где-то симуляция. Все это инструменты для достижения более глобальной цели, а инструменты, как известно, лучше работают если их использовать по назначению :)
  6. Не знаю как относительно других, не с чем сравнивать. i7-7700K 4.2GHz, 16GB RAM, SATA3 SSD, Ubuntu 18.04. В настройках стоит использовать все доступные ядра, но больше 4 вроде не использует. Нужно будет попробовать вручную задать, кстати. Ну это для примера, а по сути так и получается. То что гоняется по SPI - это не 1 в 1 то что лежит внутри. Гонять удобно байты, а хранить как есть. Объем сейчас где-то пару сотен байт, но это ещё не все. Хотя может пока и не стоит заморачиваться, пусть реализует, место есть :) Если я правильно понимаю, то использование встроенной памяти это больше оптимизация чтобы высвободить место под функционал?
  7. Именно о таком варианте писал. Старший бит передаётся, одновременно с этим принимается младший и буфер сдвигается. Проблема в том что между байтами есть пауза, при этом сессия не закрывается (SS поднят), соответственно старший бит оказывается на выходе и если старший бит в буфере оказывается 1, то эта 1-ка соответственно выскакивает в эфир на несколько клоков. Занулить сразу буфер нельзя, только на следующем клоке. Поэтому да, лучше два буфера. Буфер отправки оказывается пустым и на выходе всегда ноль между байтами. Спасибо! Посмотрел. Судя по всему нужно вставлять эту реализацию. Если просто объявить массив скажем reg [7:0] memory[10] то компилятор сам вставляет SYNC_RAM. Если сделать массив структур typedef struct { reg [7:0] data; } Data; Data memory[10]; То происходит реализация. Глубоко копаете :) Мне нравятся ваши рассуждения о подходах в работе и хобби, когда нужно быстро когда нет, и я считаю их правильными. Однако, предлагаемое их применение вызывает логическое противоречие или двойные стандарты. С одной стороны вы говорите что если делать "коммерческую" железку то нужно сделать её быстро, нет смысла экономить на чипах, заниматься вылизыванием кода и тп, с другой стороны говорите что не одобряете "подход некоторых начальников - давайте жарить, а масло потом поднесут" (с) Конечно, сначала проект нужно продумать на много шагов вперёд чтобы потом не было мучительно больно добавлять новые функции или что-то в нём менять, но и этап старта должен иметь разумные трудозатраты. Тут много факторов, например цена ошибки в продукте. Если она способна привести к катастрофе и серьезному ущербу, то да, наверно стоит сильно минимизировать этот риск тестами. Если нет - то может быть разумнее запустить ракету со сварщиками на борту чтобы они в полёте доварили недостающие детали? А тем временем на земле все увидят что "оно полетело" и закажут ещё десяток таких ракет с новыми функциями :) Ну и в целом всех разработчиков можно поделить на две крайности: человек-процесс и человек-результат. Первый - это настоящий программист-перфекционист, которому нравится сам процесс и мало интересует когда появится результат. Второму нужно быстро собрать нечто хоть из г. и палок что решает конкретную задачу. Оба способны сделать проект, только у первого это будет долго и качественно, у второго быстро и криво-косо. А истина как всегда где-то посередине :) Как считаете? На самом деле уже всё есть: SPI слейв, PWM энкодер, PWM декодер, архитектура проекта, которая позволяет легко расширять протокол обмена по SPI и добавлять новые блоки. Осталось доделать коммутацию всего этого на ножки плиса в зависимости от настроек, передаваемых по SPI. И потом в планах сделать SBUS декодер / экодер (вариация на тему UART), который будет ещё одним блоком в общей архитектуре. И всё это работает с настоящим железом. Занимаюсь этим не фулл-тайм, а в свободное время, это железка для моего хобби, однако имеет хороший коммерческий потенциал среди таких же хоббистов, но это не является целью разработки, а скорее бонус. Не нужны готовые куски кода, их полно на opencores, да и просто в интернете. Интересуют подходы и методы, как в ПЛИСах делать стоит, а как нет, а так же различные нюансы. Например к какой книге описано что Qartus Prime не синтезирует static переменные или почему массив структур автоматом не заменяется на SYNC_MEM и как сделать чтобы использовалась память, что более правильно и освободит место в чипе.
  8. Считаю это малоэффективным :) Чтобы симулировать обмен данных по SPI со своим программным протоколом, который постоянно дорабатываю нужно во первых написать и поддерживать передающую часть, как-то убедиться что она работает корректно (наверно написать тесты на неё, а потом тесты на тесты, и тесты на тесты, рискуя уйти в бесконечную рекурсию). Потом нужно симулировать источник данных с его аппаратным протоколом, симулировать приемник данных с его аппаратным протоколом. Короче куча работы ради работы без практического результата У меня комп мощный, синтез проекта (сейчас чуть более 1200 логических блоков) занимает в пределах 15 секунд. Ещё пару секунд ткнуть на кнопку "Start" в программаторе. Остальное железо подключено, его состояние выводится и абы что оно хавать или выдавать не будет т.к. заведомо надёжное. Плюс сразу пишу С++ API для взаимодействия с FPGA. Использую выводы FPGA чтобы дублировать туда интересующие сигналы для осциллографа.
  9. Всётаки использовать один буфер для одновременной отправки / приёма бит - не лучшее решение. Оно работает, но если в принимаемых данных выставлен старший бит, то этот бит появится в эфире передачи после отправки последнего бита. Это не работу не влияет, но на осциллографе или логическом анализаторе выглядит криво + всётаки может нарушить состояние принимающей стороны в теории. Так что лучше делать классически - два буфера. Такой вопрос - как заставить компилятор использовать память, а не реализовывать её? Раньше когда объявлял массив, компилятор помещал блок контроллера памяти в схему. Потом объявил не пакованные структуры и массив из этих структур, компилятор начал реализовывать.
  10. Да капец с этой короношизой одуреть можно столько дома сидеть На удалёнке можно всегда работать, но банально свободно перемещаться нельзя и всё закрыто - это уже перебор Забавно что страны где нет жестких мер - совсем пропали из новостного эфира, например Беларусь. Молодцы, респект им и уважуха, как говорится. Наверно весь этот кипиш не стоит "тех бедолаг, умерших от тяжелых сопутствующих заболеваний, а тут ещё короновирус добавился", как сказал Лукашенко, поддерживаю. Ну да дадно, тема про FPGA. Вобщем воз почти вышел на орбиту. Описал регистры железки пакед структурами, команды будут жить в interface. Всё клёво, всё работает. Осталось реализовать собственно набор команд и другой полезный функционал, например нужно будет ещё ловить UART на вход, парсить его протокол и складывать в регистры железки чтобы потом можно было забрать по SPI или использовать внутри для логики. Но тут уже всё понятно как делать. Ну и в финале развести плату и собрать железку с FPGA и STM32 на борту.
  11. one_eight_seven, теперь понятно, в синтезе это является ограничением Выходит ключевое слово interface - это нормально для абстракции, а не только для описания собственно интерфейсов между модулями. Нормальным поведением было бы как в "обычных" языках программирования. Если какая-то переменная объявлена в таске как статик, то она имеет область видимости внутри таска, но физически располагается компилятором в глобальном пространстве чтобы обеспечить сохранение значения между отдельными вызовами таска. В "обычных" языках в случае обращения к такой переменной из разных потоков её состояние будет не определено ввиду гонки. Для HDL логично было бы отслеживать такое обращение и выдавать ошибку, как это сделано с присвоением одной переменной в разных блоках always. Однако тут статик неявно становится не статиком. Ну тоесть ожидалось что код: task MyTask(ref value); static reg [8:0] count; count <= value; end Будет расценен компилятором в синтезе как reg [8:0] count; task MyTask(ref value); count <= value; end Но это оказывается не так, и это есть тот нюанс, который либо знаешь либо нет. Потому что компилятор игнорирует слово static и переменная становится reentrant, как без слова static. Более того, если указано слово static, то компилятор начинает считать переменную automatic и запрещает неблокирующее присвоение. Выглядит как бага в компиляторе, честно :D Есть код для примеров и есть код рабочий. Кроме этой переменной в другой команде будет куча переменных, автомат с кучей состояний и тп. Тоесть реализация команды в 99% случаев не является такой тривиальной. И если всё это тащить во внешний модуль, то когда команд будет с десяток, разобраться в модуле будет нереально. А так каждая команда в отдельном файле. Вход у всех одинаков, а в главном файле только case. Пример двух команд из рабочего кода // --- Execute command SPI_STATE_CMD: begin case (spi_session_cmd) // - Get PWM channel value SPI_CMD_SET_PWM: SPI_CMD_getChannelPWM.onReceive(spi_session_cmd_reset, __MEMORY__, spi_rx_buffer); // - Set PWM channel value SPI_CMD_GET_PWM: SPI_CMD_setChannelPWM.onReceive(spi_session_cmd_reset, __MEMORY__, spi_rx_buffer); endcase end
  12. Впервые близко этот язык увидел в прошлую среду. Так что да, не знаю языка на котором пишу, это правда. Только изучаю. И кстати ни одной копипасты, даже когда разбираю примеры из интернета, то переписываю почти всегда с модификациями, но сохранением подхода. Воз тоже разгоняется ударными темпами и без копипасты. Начал с мигания светодиодом, теперь N светодиодов меняют яркость с управлением по SPI с протоколом обмена. Сидеть в симуляторе совершенно не хочется, есть настоящая железка. Да посмотрел RTL симуляцию, погонял SignalTap, интересно, но для такого простого проекта не ощущаю необходимость. Вобщем меня интересует информация по различным нюансам, как например со static внутри таска - почему не работает и бест-практис так сказать в разработке, остальное способен расковырять сам. Какие могут быть альтернативы интерфейсу? Классы на сколько понял не синтезируются, дернуть таску из другого модуля нельзя потому что область видимости внутри модуля. Статик переменные в таске не работают хз почему. Выносить все кишки таска в основной модуль не хочу по соображениям архитектуры и чистоты кода. Когда потребуется добавить новую команду SPI, то хочется просто вставить файл в проект и в основном модуле добавить опцию в case.
  13. Ну как, копипастой, но не 1 в 1, всетаки не настолько тупой :D Вот если завернуть в интерфейс interface TestCommand; reg [7:0] channel_id = 0; task run(ref reset, ref reg [7:0] __MEMORY__[4], input wire [7:0] rx_byte); if (reset) begin channel_id <= rx_byte; reset <= 0; end else begin __MEMORY__[channel_id] <= rx_byte; end endtask endinterface и потом вызывать, то всё работает. Но выглядит как костыль немного. Хотя работает и такой вариант меня устроит. Но тема почему не работают стает статик - остаётся открытой.
  14. К сожалению ваш код не синтезируется. Падает на первом обращении a1.update() ошибкой В свою очередь такая таска, определенная вне модуля (да и в модуле тоже) task CMD_TEST(ref reset, ref reg[7:0] __MEMORY__[4], input wire [7:0] rx_byte) ; static int channel_id = 0; if (reset) begin channel_id <= rx_byte; reset <= 0; end else begin __MEMORY__[channel_id] <= rx_byte; end endtask не синтезируется с ошибкой указывает на строку "channel_id <= rx_byte;" Если заменить на блокирующее присваивание, то синтезируется, но channel_id не сохраняется между вызовами. Вызываю так: always @(posedge clk) begin ... // --- Execute command SPI_STATE_CMD: begin case (spi_session_cmd) // - Set PWM channel value //SPI_CMD_SET_PWM: CMD_SetPWMChannel(spi_session_cmd_reset, spi_rx_buffer); SPI_CMD_GET_PWM: CMD_TEST(spi_session_cmd_reset, __MEMORY__, spi_rx_buffer); endcase end ... end Если channel_id вытащить из таски в модуль, откуда она вызывается - то всё прекрасно работает. Интернет есть, изучил книги, в том числе цикл статей от iosifk. Делаю как "должно быть", но оно не работает. Для меня это выглядит как глюк. Либо есть какие-то неявные ограничения, либо какой-то момент упустил. Использую Quartus Prime 19.11 Lite, синтезирую под Cyclone IV EP4CE6E22C8, язык SystemVerilog HDL.
  15. 1. Хочется внутри и чтобы сохранялась между вызовами таска, который расположен в модуле 2. Не нужно :)