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

OparinVD

Свой
  • Постов

    109
  • Зарегистрирован

  • Посещение

Весь контент OparinVD


  1. Цепочка такая: input --> 1Peak detector --> 2Peak_d detector 2Peak_d detector - это тот, который ищет максимум в "нижнем течении". Он не просто ищет максимум, а строит функцию F=max(range_width) и заносит результат в таблицу - некий макро-LUT. Например, если данные едут нулевым номером вперед, то в позиции F(1) будет храниться максимальное значение среди [d_0000], на F(5) будет храниться максимальное значение среди [d_0004..d_0000] (точнее, хранится пара координата:значение). Тогда детектор главного пика 1Peak пробегает весь поток, находит координату максимума 1Peak_pos и по таблице смотрит значение F(1Peak_pos - a) Вот в зеркальную от 1Peak сторону такой фокус не пройдет, там по аналогии надо строить функцию для диапазонов [d_1023], [d_1023:d_1022] и т.д. то есть везде присутствует d_1023, а он приезжает только в конце потока. Поэтому надо или развернуть поток хвостом вперед (что вероятно невозможно по природе стримов) или принять весь блок данных и пройти по нему повторно уже "офф-лайн"
  2. Не то, чтобы совсем не будет... Так можно сделать только для диапазона, который находится ниже по "течению" стрима относительно найденного 1Peak. А в верхнем течении придется проходить повторно. Точнее, может и не придется, но я не придумал, как этого избежать
  3. Начал я чесать репу в раздумьях о создании системы сборки проекта под Alveo и наткнулся на такую статью Вещь позиционируется как инструмент управления зависимостями, заточенный под железячные проекты. Заявляют, что это менее утомительно чем изобретать свой велосипед с Makefile и .sh. Кто-нибудь сталкивался с этим зверем, стоит он внимания или свой велосипед всегда будет поудобней и ехать помягче?
  4. это очень похоже на правду... только скорей всего ему не понравился вариант с BlockBytesLeft=0... Тогда, с точки зрения синтезатора, получится так: Block_ShReg <= S_AXIS_TDATA(-1 downto 0) & Block_ShReg(Block_ShReg'high downto 0); Как я уже заметил в другом эксперименте, S_AXIS_TDATA(-1 downto 0) при синтезе дает S_AXIS_TDATA, на всю ширину, заполненную нулями. Т.е. слева Block_ShReg'range а справа S_AXIS_TDATA'range & Block_ShReg'range - разница как раз на ширину S_AXIS_TDATA. Оно, конечно, находится в ветке if, которая исключает вариант с BlockBytesLeft=0, но синтезатор вряд ли это отслеживает, и его возмущение тогда вполне оправдано. Осталось понять почему это: Block_ShReg(Block_ShReg'high downto Block_ShReg'high-BlockBytesLeft*8+1) <= S_AXIS_TDATA(BlockBytesLeft*8-1 downto 0); -- get high part порождает нули... ведь видно, что на момент принятия порции данных BlockBytesLeft не равен 0
  5. в этот момент я tkeep не анализирую, беру то, что физически присутствует на tdata, т.е. даже если бы пакет оказался короче, чем я ожидаю для заполнения header, то остаток заполнился бы мусором из не валидных позиций... но на чипскопе tkeep не нулевой, и в нужных позициях tdata находятся правильные значения, а не нули
  6. Доброго дня! Столкнулся то ли лютой особенностью синтезатора Vivado, то ли сам что-то не так понимаю. Задача такая: Принимаю axis-поток, забираю из него header, остальное ретранслирую дальше (всё в litle endian). Длина заголовка и ширина входных данных задаются в generic, но для упрощения внешнего вида ширину tdata зафиксирую на 32 бит: generic ( g_block_length : natural := 8; ...); port( ... S_AXIS_TDATA : in STD_LOGIC_VECTOR(31 downto 0); ... ); ... signal Block_ShReg : std_logic_vector(g_block_length*8-1 downto 0); Для отслеживания, сколько байт заголовка, принято есть счетчик: signal BlockBytesLeft : natural range 0 to g_block_length := g_block_length; Поведение вполне простое: если заголовок еще не заполнен, т.е. BlockBytesLeft больше, чем приходит за одну транзакцию, то всю эту транзакцию забираем в сдвиговый регистр: Block_ShReg <= S_AXIS_TDATA & Block_ShReg(Block_ShReg'high downto 32); BlockBytesLeft <= BlockBytesLeft - 4; Если же очередная транзакция полностью заполнит заголовок, то из S_AXIS_TDATA берем фрагмент шириной, которая зависит от количества оставшихся байт: Block_ShReg <= S_AXIS_TDATA(BlockBytesLeft*8-1 downto 0) & Block_ShReg(Block_ShReg'high downto BlockBytesLeft*8); BlockBytesLeft <= 0; И вот тут прозвенел первый звоночек. На симуляции и в ActiveHDL, и в Vivado всё прошло хорошо, а вот синтез вывалился с ошибкой. Синтезатор заявил, что правая и левая часть не совпадают по ширине и отличаются ровно на ширину S_AXIS_TDATA. Тогда я разделил прием последней транзакции на явный сдвиг сдвигового регистра вправо на BlockBytesLeft байт и прописывания в освободившееся место новых байтов: Block_ShReg(Block_ShReg'high-BlockBytesLeft*8 downto 0) <= Block_ShReg(Block_ShReg'high downto BlockBytesLeft*8); -- shift right low part Block_ShReg(Block_ShReg'high downto Block_ShReg'high-BlockBytesLeft*8+1) <= S_AXIS_TDATA(BlockBytesLeft*8-1 downto 0); -- get high part Синтез прошел успешно, но в железе не заработало. Отладка с чипскопом показала, что сдвиг проходит успешно, а вот освободившиеся байты заполняются нулями. Подобное заполнение нулями я встречал в другом месте, там в результате ошибки по формуле получался неправильный диапазон S_AXIS_TDATA(-1 downto 0). В этом же случае выглядит всё правильно, формула (BlockBytesLeft*8-1) на чипскопе отображается корректно числом из диапазона (31 downto 0). Но очевидно, что проблема топчется именно вокруг диапазона для S_AXIS_TDATA, но я не пойму как. Есть мысли, как это работает? PS: на данный момент проблема решена побитным копированием из S_AXIS_TDATA в старшую часть сдвигового регистра: for i in S_AXIS_TDATA'range loop if (i <= BlockBytesLeft*8-1) then Block_ShReg(Block_ShReg'length - BlockBytesLeft*8 + i) <= S_AXIS_TDATA(i); end if; end loop; Но это не отменят того, что я не постиг причину проблемы
  7. У меня такой опыт был: Имеется некий входной вектор, например, signal vector_in : std_logic_vector(7 downto 0); для именования некоторых из его значений введены константы, например, constant s1 : std_logic_vector(7 downto 0) := x"02"; constant s2 : std_logic_vector(7 downto 0) := x"17"; constant s3 : std_logic_vector(7 downto 0) := x"8b"; и когда я захотел case'ом анализировать на соответствие этим значениям: case vector_in is when s1 => null; when s2 => null; when s3 => null; when others => null; end case; Получил от компилятора ошибку, что-то про то, что он не может перебрать все варианты, что надо использовать "when others", и вроде про vhdl2008 что-то. "when others" при этом присутствовал и никак не помогал. Если в том же case константы заменить фактическими значениями, то всё ок. case vector_in is when x"02" => null; when x"17" => null; when x"8b" => null; when others => null; end case; Пришлось заменить на if-elsif. Чуть менее элегантно выглядит, зато работает
  8. С этим мнением соглашусь, и преимущественно у меня так и есть... Но иногда хочется рюшечек, чтобы упростить себе жизнь, что даст не просто эстетику и читаемость кода, и даст защиту от себя-дурака в будущем. Вот как вы видите решение озвученной задачи? У меня сейчас на все варианты определены null-константы для каждого поля. Решение простое, надежное, работает и не сломается до тех пор, пока кто-то не вздумает изменить тип поля с int на uint. С прикладной точки зрения, может ничего и не поменяется, но null-значение надо будет класть другое, и это будет моя головная боль. А хотелось переложить ее на компилятор
  9. Идея с singned/unsigned прекрасна! С ними останется только два типа функций function null_of(arg : signed) return signed; function null_of(arg : unsigned) return unsigned; Это полностью устранит ambiguous при вызове для вектора любой ширины. Сами типы вполне стандартны и библиотечны. Я было кинулся реализовывать, но уперся в то, что для этого: reg_data <= unsigned(data_in ); опять надо отматывать datasheet наверх, чтобы посмотреть, какого типа у нас reg_data, чтобы решить, использовать unsigned(data_in) или signed(data_in)... Т.е. от чего бежал, к тому и вернулся
  10. Доброго дня! Задача такая: в протоколе определены типы данных int8, uint8; int16, uint16 и т.д. На внутреннюю математику их знаковость или без знаковость не особо влияет. Важна их ширина и то, что они отличаются null-значением, которое показывает, что поле соответствующего типа отсутствует. Для int это x"7FF..", а для uint это x"FFF..." Я пытаюсь изобрести простой изящный метод, чтобы при присваивании сигналу null-значения не лазать в datasheet и не искать какого конкретно типа это поле, а чтобы "здесь и сейчас" имея на руках только сигнал понять, какой у него null. В datasheet конечно сходить можно, но это неудобно. Например, поле TimeStamp имеет тип TTimeStamp, а сам TTimeStamp в одной вариации может быть int64, а в другой - uint64. Я пытался зайти с двух сторон: через attributes и через перегрузку функций, но приемлемого решения не получил ( 1) подход через атрибуты вижу так объявляю сабтипы, и атрибут для этого сабтипа, например subtype t_int8 is std_logic_vector(7 downto 0); attribute null_value : std_logic_vector; attribute null_value of t_int8 : subtype is x"7F"; attribute null_value of t_int16 : subtype is x"7FFF"; Но это атрибут сабтипа, и для присваивания сигналу null-значения надо сделать s <= t_int8'null_value, т.е. нужно знать, какого типа у меня сигнал, плюс сама запись принципиально не отличается от присвоения предопределенной константы. Хотелось бы получить что-то типа s <= s'null_value; но атрибут сабтипа не передается по наследству сигналу. Можно, конечно, объявить атрибут сигналу, но тогда для набора однотипных сигналов прописывать атрибут с null_value придется каждому индивидуально, а учитывая то, что часто присвоение null-значения - это единственное присвоение сигнала, такая конструкция получается слишком избыточной. 2) для второго подхода я объявлял одноименные функции типа function null_of(arg t_int8) return t_int8; function null_of(arg t_uint8) return t_uint8; и т.д. в надежде, что компилятор по типу аргумента возьмет нужную функцию и вернет соответствующее значение, чтобы можно было бы написать так: s : t_int8; s <= null_of(s); но базовый тип для всех это std_logic_vector и вызов функции получается ambiguous Есть светлые мысли, как это сделать красиво, а не просто присвоением констант вручную?
  11. Я из будущего оставлю себе из прошлого подборку ссылочек: my-greatest-xilinx-hack ip-packaging-parameter-passing help-with-automatic-generation-of-parameter-such-as-clock-frequency how-to-i-get-the-auto-button-in-custom-ip inferring-axi-clock-freq-inside-custom-ip
  12. Всем доброго дня! В Vivado IP packager в группах файлов есть раздел: Что это за звери в двух словах? А то интернет на XSpice разве что пикантных картинок не предлагает 🤷‍♂️
  13. а можно ли как-то автоматически перебирать поля в record, чтобы написать такую функцию? или универсальную функцию написать не получится, т.к. можно только обращаться по имени поля, и для каждой record будет своя функция преобразования?
  14. А существует какой-то красивый способ прикрутить record на поле tdata в стандартном axi stream так, чтобы Вивада проглотила? Понятно, что группу сигналов, если их правильно обозвать, Вивада автоматически распознает и объединяет их в единый интерфейс. Речь идет о том, чтобы разбить поле tdata на свои логические подполя. Виваде нужно, чтобы сигнал был только std_logic или std_logic_vector. Попробовал влоб присвоить сигналу с типом std_logic_vector сигнал с типом record (по аналогии с subtype сигналами) - не прокатило 🙄 PS у меня VHDL
  15. пока на подъеме энтузиазма мне кажется, что я готов терпеть боль и неудобства дебага :) лишь бы начать upd: для начала в качестве эксперимента запустил упаковку не обычной корки, а axi periferal... Обнаружил, что в Advanced группе файлов появился раздел Block Diagram, в котором собственно лежит bd.tcl: поредактировал его, всё сохраняется и обратно не затирается... эта боль вроде как не побеспокоит. Напихал в каждую proc свой puts {Hellow world} и получил его в ответ и от init, и от pre_propagate, и от popogate, значит они как-то зарегистрировались в системе и запускаются. А может достаточно просто придерживаться предопределенных имен Еще гадство заключается в том, что раздел Block Diagram отсутствует для простых смертных. Как мастер упаковки его впихнул остается неясным... но это всё вроде не мешает продолжать тыкать палкой в этот черный ящик, работает и ладно :) Еще в разделе drivers нашел скрипт с функцией generate. По идее она должна генерировать xparameters.h для микроба, и мне бы она подошла. Второе гадство в том, что я не получил от нее Hellow world при генерации BD, видимо она запускается в другом месте, а счастье было так возможно :)
  16. И на том спасибо, а то я уж начал думать, что со мной что-то не так :) Связывался с ребятами из plis2, они сходятся на том, что для обычных смертных доступна упаковка ipcore только в виде полностью статичной единицы, а вся кастомизация доступна только на основе hdl параметров, прокинутых наверх. Помнится в похожей теме уважаемый RobFPGA говорил, модули на BD это скрипты на TCL... Вот именно это мне сейчас и нужно. Есть ли у Вас чем поделиться по этой теме?
  17. Спустя полгода, я снова добрался до этой темы, и в этой войне я снова проигрываю Обновлю хотелки, и что имеется у меня на сегодняшнем этапе развития: Топ-левел в моем проекте собран в BD из моих hdl-ных модулей, связанных стандартными axi интерконнектами и другими стандартными корками. Далее этот BD я упаковываю как ipcore и отправляю на дальнейшую сборку. Но в таком ipcore всё намертво прибито гвоздями, ничего не настроить. К тому же появляются новые варианты конфигурации, и приходится поддерживать несколько независимых схем, хоть и имеющих между собой много общего. Хочу научиться собирать bd динамически на основе галочек в настроечном gui (ну или по значениям property в xci). Плюс хочу генерировать свои дополнительные output products, например yaml-описание для программистов, файл connectivity.ini и *.xo (для сборки под Alveo). И я не перестаю верить в то, что в понятие "генерация" входит возможность именно генерировать нечто кастомное, а не только экспорт hdl с места на место. Как это реализовать непосредственно в коде, вроде, проблем нет. По крайней мере, я знаю, где всё это посмотреть в примерах. А вот как привязать запуск моих функций к соответствующим функциям vivado - хоть убей, не могу найти. Вряд ли каждый ipcore вешает свой хук, например, на generate_target ... Я игрался с XIL_IFX_TRACE, можно проследить всю цепочку вызова функций внутри, но нет первичного запуска. Например, при добавлении на канвас axis_interconnect trace-цепочка начинается с запуска update_boundary из его bd.tcl, а для axis_switch запускается init из его bd.tcl... Как vivado понимает, кому какую функцию запустить надо? Облазил с grep всё что доступно в текстовом виде tcl/xml - не нашел, где бы ipcore регистрировал свои функции в функциях vivado. Аналогичная ситуация и для generate_target. Я даже пробовал что-то типа info body ::generate_target - не показал ничего :) Прошу стартового пинка в направлении, как создавать динамические корки, а дальше я уж сам :)
  18. Только что столкнулся с такой ситуацией: На выходе стоит axis_switch, который коммутирует несколько потоков в один. В гуи задал ему параметр ARB_ON_TLAST=1, в таком виде ipcore на основе BD без проблем упаковывался, всё работало. Сделал экспорт BD в tcl, и после восстановления этот параметр оказался равен 0. Скопировал из консоли строчку с принудительной установкой в 1, он мне заявил, что нельзя установить disabled параметр. Обошел проблему заменой switch на axis_interconnect... Не стал заводить новую тему, просто вспомнился старый разговор :)
  19. Да, нашел такой файлик, спасибо, буду иметь в виду. Команды там понятные. HDL исходники подтягиваются из внешней папки при восстановлении проекта, а при восстановлении BD - в основном create_bd_cell и connect_net. Я сейчас пробую разобраться с тиклем, который лежит в папке /ipcore/xgui/. В нем расписан собственно сам интерфейс кастомизации (страницы, группы, поля) и прописаны функции update и validate для параметров. Только вот пока не докопался до того, как эта вся механика работает, кто кого вызывает? То ли сюда и напихать все эти create_bd_cell и connect_net, то ли наоборот подключать его как библиотеку в скрипте восстановления BD и рисовать этот BD с учетом параметров из GUI...
  20. Для хранения в git я отдельно сохраняю проект в tcl и отдельно сохраняю BD в tcl. Потом в скрипт восстановления проекта добавляю source BD.tcl. Да, после этого рождается тот же самый BD, что я руками рисовал (с поправкой, конечно, на взаимной расположение BD_cell'ов)
  21. При включении XIL_IFX_TRACE едет куча информации, а при включении XIL_IFX_DEBUG на "всё" интерконнект сломался, при изменении количества интерфейсов стал вываливаться с руганью на другие параметры - неожиданно :) В любом случае, для меня это большой шаг вперед, буду развлекаться, поклон вам! Еще напоследок вопрос: namespace для IPI - ipx? или каждая корка в своем namespace живет?
  22. Вот, именно про это! И да, прочитав, основные юзер-гайды, я не нашел ничего вразумительного на этот счет. В разделе про упаковку кастомного IP написаны очевидные вещи про добавление property, его тип, зависимости и т.д., а что делать с ними дальше - не понятно. С чего начать? Как заглянуть внутрь процессов в том же axis_interconnect?
  23. Вовсе нет... Как раз наоборот, результат идентичный. Я хочу сказать, что вроде как не параметризуемый снаружи cell вполне себе меняет внутреннюю структуру и подтягивает разные исходники в зависимости от параметров... Чего-то подобного я и хочу добиться от своей корки
  24. Всё тут же перерисовывается и в сторону увеличения, и в сторону уменьшения количества. И в "закрытом" и в "раскрытом" состоянии
×
×
  • Создать...