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

dxp

Свой
  • Постов

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

  • Посещение

  • Победитель дней

    18

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


  1. Речь не про то, что всё работает в продукте, а про то, что оно не ломается на сборке. Когда ломается на сборке -- это просто мешает остальным работать с этой базой. Первый человек влил вчера вечером, а сегодня он в отпуске. Сборка сломалась потому, что человек недосмотрел и забыл просто исходный файл добавить в реп -- человек, в общем-то, квалифицированный и ответственный, но недосмотрел, бывает. Теперь (сегодня) его физически нет (уехал на Алтай), проект не собирается из-за отсутствия файла в репе. Вся остальная команда (8 человек) не может пользоваться этой кодовой базой -- приходится извращаться, откатываться на предыдущие стабильные комиты. И это простой случай, который лечится на раз -- и диагноз сразу ясен, и метод лечения тривиален. А бывают варианты похуже, когда при слиянии конфликты возникают, неправильное разрешение которых ломает логику работы. Поэтому в нашей группе мы делаем заливку в общую ветку только через merge request: к записи в эту ветку имеет доступ один человек, он, получив MR, просто запускает прогон: общий функциональный тест, цель которого выявить ошибки типа "отсутствует файл", правильность соединения модулей на топе, какое-то минимальное тестирование проекта в сборе, идёт тест недолго, после чего проект запускается на синтез -- идёт проверка целостности другим тулом. PnR не делается, т.к. это долго и на этом этапе не нужно, т.к. цель -- обеспечить целостность кодовой базы, чтобы любой член группы мог в любой момент взять из общей ветки топ и стоить на нём свою фичеветку, будучи уверенным, что его работа не станет колом, из-за чужих ошибок. Описанное -- это ещё не CI. CI идёт следом по цепочке -- там полная сборка и расширенный набор тестов (в перспективе и в железе на стенде). Здесь же главное -- не сломать сборку и тем самым не тормозить работу других членов группы. А то, как процесс встаёт колом, я повидал. В других подразделениях (программеры) исповедуют как раз описанный вами подход: все без ограничений льют в общую ветку и регулярно ломают сборку. Бывало, что не могли неделями привести в стабильное состояние -- одни багованные фичи накладывались на другие и разгрести это оказывалось не так просто. Коллега, который с этим работал, в итоге за три недели не дождавшись стабильного топа общей ветки, просто взял какой-то старый комит и пилил временно на основе него -- ему не нужны были эти новые правки, которые ломали сборку. Матерился, конечно. Если бы у них был вот этот этап предварительной проверочной сборки (просто на собираемость), когда проблема обнаруживается ещё до вливания кода в общую ветку и борьба с ней идёт в локальной фичеветке, то этого безобразия просто не было бы. Элементарная дисциплина -- нерабочие, непроверенные на целостность собираемости правки не попадают в общую базу, которая является стартовой точкой для новых фич/фиксов для всех членов группы.
  2. Вот ситуация: кто-то сломал мастера, влив в него свои правки и не проверив их как следует, другой член команды вытянул себе свежего мастера, делает свою фичу на базе общего кода, и у него сборка ломается из-за того, что тот, предыдущий накидал туда каки, исправить это данный член команды не может -- не его код (ему в нём разбираться долго и дорого). Как быть? Может всё-таки требовать целостности общей базы -- пусть в проекте есть ошибки, но он по крайней мере собирается?
  3. Написать скрипт, который из yaml описания родит SV заголовочный файл. Мы так делаем. Т.е. из описания типа: parameters: PROJECT_NAME : detector TOP_NAME : detector_m TESTBENCH_NAME : detector_tb DEVICE : xcku15p-ffve1517-2-i DEVICE_ID : 1 RELEASE_VERSION : 0 BUILD_NUMBER : 0 NET_PORT_COUNT : 4 VLAN_COUNT : 10 MAIN_AXIS_WIDTH : 512 MAIN_AXIS_USER_WIDTH : 48 MAIN_AXIS_ID_WIDTH : 8 PACKET_PERIOD : 10ns рожается: //------------------------------------------------------------------------------- // // This file is automatically generated. Do not edit the file! // //------------------------------------------------------------------------------- `ifndef GUARD_CFG_PARAMS_SVH `define GUARD_CFG_PARAMS_SVH // synopsys translate_off `ifndef SIMULATOR `define SIMULATOR `endif // SIMULATOR // synopsys translate_on `define PROJECT_NAME detector `define TOP_NAME detector_m `define TESTBENCH_NAME detector_tb `define DEVICE xcku15p-ffve1517-2-i `define DEVICE_ID 1 `define RELEASE_VERSION 0 `define BUILD_NUMBER 0 `define NET_PORT_COUNT 4 `define VLAN_COUNT 10 `define MAIN_AXIS_WIDTH 512 `define MAIN_AXIS_USER_WIDTH 48 `define MAIN_AXIS_ID_WIDTH 8 `define PACKET_PERIOD 10ns `endif // GUARD_CFG_PARAMS_SVH Реализация на питоне. Тривиальна: всасывается yaml описание с помощю pyyaml, дальше это просто словарь, проход по его ключам с генерированием выходных строк. Как-то так: import yaml #------------------------------------------------------------------------------- ... with open( <path-to-yaml-file> ) as f: cfg = yaml.safe_load(f) params = cfg['parameters'] max_len = max_str_len(params.keys()) + 2 ... text += '// synopsys translate_off' + os.linesep text += '`ifndef SIMULATOR' + os.linesep text += '`define SIMULATOR' + os.linesep text += '`endif // SIMULATOR' + os.linesep text += '// synopsys translate_on' + os.linesep*2 for p in params: value = str(params[p]) text += '`define ' + p + ' '*(max_len - len(p)) + value + os.linesep ... with open(<path-to-output-svh-file>, 'w') as ofile: ofile.write(text) ... #------------------------------------------------------------------------------- def max_str_len(x): return len(max(x, key=len)) #------------------------------------------------------------------------------- Код скрипта неполный (опущены генерирование титла, гардов и всякое, не относящееся напрямую к теме (скрипт этот работает не сам по себе, а в составе системы сборки, чтобы автоматом всё это генерить из параметров). Заморочки с длиной строк ключей сделаны для того, чтобы в выходном файле значения были выровнены -- это изрядно повышает читабельность.
  4. Благодарю за лекцию, но с чего вы взяли, что кто-то предлагал ломать систему типов С++ этими кастами? Я лишь возражал против утверждения, что reinterpret_cast совсем не нужен: Нужен. Именно для низкого уровня. Консенсус. static_cast тоже небезопасная штука -- если привести указатель на объект базового класса к указателю на потомка, то работать будет до тех пор, пока не вздумается вылезти за пределы определения базового класса. Т.е. полная ответственность точно так же на программисте. И тоже надо хорошенько подумать, нет ли ошибки проектирования, что приходится преобразовывать тип хоть и static_cast. Собственно, вот этот безобразный внешний вид плюсовых кастов таким и сделали, чтобы их было хорошо видно в коде -- чтобы потенциально опасные места выделялись.
  5. Да это ж просто пример, ни к чему придираться к условиям. Они могут быть вполне другими. Например: внешнее устройство размещает какие-то данные в общей памяти (через расшаренную шину), потом по какому-то каналу мечет адрес (хоть тот же UART), прилетает пакет, в котором та же чиселка - адрес, по которому надо это данные взять и обработать. Таких ситуаций можно придумать бесконечное количество, всё зависит от конкретных условий и задания. И никак тут без reinterpret_cast<>() не обойтись. Претензия наподобие как предъявляют к goto -- де, моветон и суксь. Но раз оставили в языке, значит для чего-то надо. И действительно есть одна ситуация, когда все остальные варианты уступают как минимум в производительности. Просто надо средствами пользоваться правильно, только и всего.
  6. Для чего же в языке это оставили тогда? И как вы будете обходить ситуацию: вот хранится адрес некой структуры данных во внешней SPI флешке, вы его читаете оттуда числом. Как теперь его в адрес преобразовать?
  7. type'() -- это просто говорит компилятору трактовать выражение в скобках как указанный тип. От ошибок оно не защищает. Если хендл предка присвоить типу хендла потомка, то будет рантайм ошибка. В С++ это не обязательно ошибка, если после такого приведения не лезть в "зону" объекта, определённую в потомке. В SV он чекает тип хендла и прогон падает с фатальной ошибкой (которая типа сегфолта). Хотя type'() вполне закрывает вопрос на этапе компиляции. type'() -- это годная тема для "мапинга" пакованных объектов друг на друга, для классов в SV не годится. $cast() тоже вызывает ошибку при таком приведении, но она не фатальная, а обычная, т.е. это не рантайм ошибка -- прогон после неё не останавливается, это ошибка только лишь приведения типов, которая сообщает, что преобразование некорректно. Она служит для последующего анализа. Вот внятное объяснение разницы между динамическим и статическим кастами от гуру SV: https://verificationacademy.com/forums/systemverilog/difference-between-static-casting-and-dynamic-casting-sv В С++ ничего никуда не перескакивает. Касты просто велят компилятору трактовать выражение как указанный тип. Если при дальнейшем использовании программа упадёт, это вина программиста, а не компилятора.
  8. Нет, не одинаково. type' -- это статический каст, он просто говорит компилятору: "трактуй это как такой-то тип", конечно, в рамках допустимого. Например, можно привести пакованный массив к пакованной структуре, но хендл объекта класса это не пропустит. Т.е. если в терминах С++, то это аналог static_cast<>(). И выполняется это именно на этапе компиляции. $cast же -- это аналог dynamic_cast<>(), он выполняется на рантайме. Используя принципы RTTI. У него другое назначение. Он полезен, например, когда ситуация требует разбора контекста во время выполнения. Например, вот есть у нас сетевые пакеты: ethernet, IP, UDP, TCP и т.д., они описаны как иерархия классов -- базовый ethernet, его наследник -- IP, его наследники UDP и TCP. И у нас код принимает, например, IP пакеты, и нам надо отфильтровать TCP пакеты с флагом SYN -- т.е. начало соединения. Для этого мы входящий пакет (IP) динамически кастуем к TCP, если кастуется, то полученный объект уже проверяем на флаги TCP -- они в нём точно есть, т.к. проверка типов это гарантирует. Т.е. это полезно для парсинга, когда на этапе компиляции недостаточно информации, полная картина есть только на рантайме. В плюсах, чтобы привести указатель базового класса на указатель производного, достаточно static_cast<>(). И делается это на этапе компиляции. При этом ответственность за правильность возлагается на программиста. А reinterpret_cast<>() -- это никакой не грязный хак, это необходимое средство в ряде случаев. Просто пользоваться этим надо с пониманием. Например, если нужно, как в примере выше, указатель базового класса привести к указателю на производный, то нужно использовать static_cast<>(), а не reinterpret_cast<>(), использование которого может привести к неожиданным ошибкам (например, можно привести указатель на тип вообще другой иерархии классов, что, очевидно, не является целью -- такой класс ошибок отлавливается static_cast<>()).
  9. $cast - это типа dynamic_cast (в С++). Особенность: это одновременно и task, и function, зависит от контекста использования.
  10. Немного офтопик. Что "всё переделывать"? Весь дизайн? Почему? Зависит от контекста. Например: logic a; logic b; logic c; always_ff @(posedge clk) begin logic slon; ... slon = a && b; // вычисление значения локальной переменной ... c <= slon; end вполне легальный и годный подход. В этом примере код выхолощенный, он только для показать принцип. В реальном коде использование локальных переменных в блоках упрощает кодирование и улучшает читабельность. По правилам языка тут происходит усечение, поведение документированное стандартом и предсказуемое, никакого криминала тут нет. Сам предпочитаю писать logic slon = 1; вместо logic slon = 1'b1; которое выглядит загромождённым и менее читабельным. Как говаривал Б.Страуструп: "Это короче, чем я могу написать, а компилятор должен понимать умолчания". Это с чего это недопустимо для синтезатора? Очень даже допустимо: мы сообщаем синтезатору, что тут нам не важно, что будет, и он волен туда совать любое значение, как ему удобнее. Тем самым мы задаём более расслабленные условия для тула, что "развязывает" ему руки для оптимизаций.
  11. В общем виде: "Собрать устройство согласно схеме". Подходит, например, если нужно реализовать это на макетной плате. Если есть печатная плата, то тогда уж "Смонтировать ЭРИ на печатной плате в соответствии со сборочной документацией". В сборочную документацию входит сборочный чертёж, инструкции и т.п.
  12. Не понятно, почему именно ломалось. Если 64-разрядная модель памяти, то забить её всю, это надо очень постараться. По второй ссылке лечение с помощью какого-то патча. Т.е. пофиксили программно. Думается, на системе без MMU никакими патчами фрагментацию купировать не получится.
  13. Я некорректно выразился. Исправил. Вам известны случаи падения программ, например, на x86 из-за того, что фрагментация "побила" всю память?
  14. Физическая память выделяется страницами, а виртуальная "склеивается" из этих страниц. Если запрос не вмещается в промежуток свободного адресного пространства, то память выделяется в адресах, где достаточно места. А страницы физической памяти используются те же самые. Максимум что может грозить виртуальной памяти — то, что она (её адресное пространство) может закончиться. Но это контролировать гораздо проще.
  15. Фрагментация кучи на физической памяти — неустранимая проблема. Успешно она преодолевается при использовании виртуальной памяти — сиречь через MMU.
  16. Имена можно посмотреть в нетлисте (он обычно при открытом синтезированном проекте появляется в левой панели, где вкладка Sources) или в схематике — там можно встать на цепь и в контекстном меню вызвать свойства, в появившемся окошке (обычно тоже в левой панели снизу) можно увидеть все подробности.
  17. (1) Ваша балка длиной 4 м, сечение выбрал 80х40 мм (в больший размер в вертикальной плоскости), распределённая нагрузка от собственного веса 20 Н (2 кгс). (2) Эпюра изгибающего момента. Максимальное значение в точке защемления — 160 Нм. (3) Эпюра перемещений — максимальное перемещение логично на конце, 37.5 мм. Эта величина зависит от жёсткости балки, которая определяется геометрией и материалом. Геометрия указана выше, материал дерево (модуль упругости 10000 МПа). 4-метровая деревянная балка такого сечения — похоже на правду. Вообще, правильно сказали, что в таком простом случае не нужны никакие интегралы и сопроматы — нужно найти центр масс и приложить к нему результирующую силу. Центр масс тут, очевидно располагается в середине балки, т.е. плечо до него получается 2 м. А распределённая нагрузка пересчитывается в сосредоточенную силу, приложенную к этой точке, и сила эта равна в данном случае силе тяжести, действующей на балку — 8 кгс или 80 Н. Умножая, получаем: \(M_{y} = F \cdot l = 80 \cdot 2 = 160\text{ } Н\cdot m\)
  18. Обычно это входит в драйвер чипсета (часто и свичи в "южном" мосте). Енумерацию проходит, но корректно ли после этого работает — вот вопрос. Там может быть инициализация проходит на уровне стандартных действий, а конкретный свич требует ещё какие-то специфичные действия, которые обеспечивает проприетарный для этого компонента драйвер. По типу как с видеокартами — все они как-то обнаруживаются системой и что-то выдают. Но чтобы извлечь из видеокарты её настоящий потенциал, необходимо использовать специализированный драйвер для неё. Другие PCIe устройства в этом слоте исправно работают?
  19. А вы запрос по BDF делаете? Я всегда с адресами памяти работал. Свич по идее не должен влиять — при енумерации RC его должен обнаружить и запрограммировать на корректную маршрутизацию. И она же работает на два запроса. Со свичами дел не имел, не могу ничего сказать, может там и правда надо что-то со стороны драйвера настраивать, хотя это сомнительно. Всё же надо как-то локализовать проблему — где возникает. Вот ушёл второй запрос, что на RR порте? Он (порт) готов принимать запрос? Или может там уже после второго tready в нуле. Или, например, только на третьем затыкается. Или и после третьего не затыкается, но просто уже не проходит — ломается где-то дальше. По тому, что ругается на таймаут, похоже на то, что запрос где-то потерялся и ядро вернуло ошибку таймаута. Адреса (если по адресам запросы идут) посмотрите внимательно — корректные ли. На уровне TLP 32-разрядные запросы можно метать только в 32-разрядно-адресуемую память, а и 64-разрядные — в 64-разрядно-адресуемую. Хотя тут не TLP, а их проприетарный интерфейс. Но может есть какие-то нюансы. Не включен ли на хосте IOMMU, может он там вносит коррективы. Пока идеи кончились. 🙂
  20. Имхо, таймаут тут ни при чём. Он там достаточно большой и служит для того, чтобы терминировать запросы, на которые за вменяемое время не пришли ответы — чтобы запросы не висели вечно. У вас что-то ломается с логикой запросов — первые корректные, на них приходит, а потом что-то в них портится. Посмотрите внимательнее, что отсылаете. Попробуйте слать подряд несколько одинаковых запросов. Например, первый проходит, ещё раз его же. Если проходит, то ещё несколько раз. Если это работает, то по крайней мере покажет, что с буферами и очередями запросов проблем нет. А если на совершенно корректные запросы (ну, раз хотя бы один работает) дальше ломается, то тогда уже смотреть, что там с логикой транспорта — может кредитов выделено с гулькин нос и они не возобновляются (бредово, конечно, но кто знает, что там может быть). У вас тут не самая простая топология — коммутатор имеется, проблема может быть на любом уровне.
  21. Коммутатор, даже если ограничивает MPS ниже RC и дивайса, не должен влиять — при енумерации RC проверяет всю топологию, и если находит узел, который режет MPS — например, на 128 байт, то всем узлам в этой цепочке устанавливается MPS 128, несмотря на Capability. У вас, судя по скрину, свич не ограничивает. Что за тандемный ответ? Надо разбирать заголовок, чтобы понять, что там пришло.
  22. Согласен, что особого прироста ожидать не стоит, т.к. тактовые близки, у интола она даже выше. Имхо, тут рулит размер кэша, он и даёт основной прирост. Тот же рыжень с 4МБ кэша (со встроенной видюхой) и с 32МБ (без видюхи) дают разницу раза в полтора, если не больше, хотя ядро и память и материка одинаковые. Проводил сравнение Ryzen 5 3600x vs Core i5-8600K. Тож выигрыш был пользу рыженя процентов на 10-15, сборка проекта там шла порядка 30 минут. За счёт кэша, имхо (9МБ vs 32МБ).
  23. первым делом смотреть состояние потоковых портов — что с ними, готовы принимать-отправлять или tready в нуле. Например, если второе, то скорее всего почему-то затык внутри — кредиты кончились, например, по какой-то причине. Можно повесить счётчик и определить, сколько передано до зависания. Может там на первом же пакете всё падает.
  24. а нету доступа удалённого до хоста, с которого можно к ПЛИС прицепиться по JTAG?
×
×
  • Создать...