Привет, Хабр! Меня зовут Антон Осетров, я разрабатываю СнК в компании YADRO. Раньше я проектировал отказоустойчивые бортовые вычислители, а также испытывал в лаборатории микросхемы. В этой статье я расскажу, что такое DFT, зачем это нужно, а также сравню популярные архитектуры, с помощью которых DFT реализуют на FPGA.

Начну с печального примера Intel из 2023–2024 гг. Тогда покупатели оборудования на процессорах Intel 13–14 поколения столкнулись с быстрым выходом процессоров из строя. Вначале Intel обвиняла во всем производителей печатных плат: они не учли рекомендации производителя и подавали повышенное напряжение для питания процессоров. Рекомендации обновили, начали штамповать новые платы… но оборудование все равно выходило из строя.

Тогда Intel признала ошибку в микрокоде процессора и выпустила обновление. Ошибка эта вводила процессор в предельный режим работы. Обновление не помогло, и Intel пришлось пойти на новое признание: на фабрике произошло загрязнение, из-за которого у процессоров при работе окисляются переходные отверстия. Из-за этой истории падение акций Intel ускорилось еще больше.
Представьте, что такой процессор поставили не в домашний компьютер, а в самолет или атомную станцию. Понятно, что в таком случае нужно проводить тщательные испытания — экспериментально подтверждать характеристики и свойства CPU.
В ходе испытаний мы оказываем воздействия на объект и смотрим, соответствует ли его отклик ожидаемому. В итоге выносим вердикт об успешном прохождении испытания. Или нет. Испытания бывают тестовые и функциональные. В тестовых мы подаем на микросхему воздействия, большие по объему, чем функциональные, тем самым проверяя работоспособность. В функциональных мы исследуем функциональный режим работы устройства, то есть оговоренный в его документации. Но реальные условия часто вносят коррективы в функциональность, поэтому внешние факторы тоже стоит учитывать. В микросхемах, например, — электромиграцию, более широко — температуру и другие факторы.
Модели неисправности
Все причины отказов в СнК учесть сложно, поэтому мы используем модели неисправности. Они помогают сфокусироваться не на причине отказа, а на месте его возникновения.

Первая — это Stuck-at fault, залипание, когда на линии происходит короткое замыкание или обрыв. В схеме выше на вход элемента «И» мы подаем две единицы. Значит, на выходе точно должна быть единица. Если на этой линии произошло залипание в нуле, мы можем отловить это. Подадим верное значение, а в ответ получим результат, который нас не устраивает.

Вторая модель неисправности — это Delay faults и Crosstalk (ошибки на переключения), неисправности, связанные именно с временными характеристиками. Delay faults может возникать при непротраве металла на входе инвертора. Из-за увеличения сопротивления растет и время переключения, или период отклика — когда происходит переход из одного значения в другое. Сrosstalk возникает при неправильном проектировании, например, линий питания, логических линий. Из-за этого появляются паразитные емкости, которые также влияют на временные характеристики.
Как проверить эти неисправности? Пустить два тактовых импульса подряд с частотой, что нужна при функциональном режиме работы или даже за его пределами. Если триггер здесь не успел переключиться, значит, есть ошибка на Delay faults.
На основе моделей неисправностей мы строим тесты из нулей и единиц. Важно учитывать еще и время тестирования, и покрытие, и измерительное оборудование — достоверность контроля включает множество параметров. Оценивая время тестирования, мы, по сути, уже начинаем его эксплуатацию, то есть расходуем технический ресурс изделия, отчего надежность его снижается. Чем дольше проверяем, тем больше.

Мы должны находить здесь баланс — и делаем мы это с точки зрения контролепригодности, то есть того, насколько продукт отвечает требованиям более эффективного тестирования. Для этого нам нужно еще на этапе проектирования встраивать в процессор специализированные структуры и адаптировать архитектуру.
Разделяй и властвуй
Разделим тестовые тестовую и функциональную логику скан-триггером (scan flip-flop). От обычного триггера он отличается наличием мультиплексора, который включает отдельный скан-вход, что используется во время режима тестирования.

Как коммутировать такие триггеры? Как проводить испытания? Мы должны убедиться, что выходные данные структуры достоверны и точно работают. Кроме того, сделать все хотелось бы как можно проще: иметь более легкий доступ к пластине, когда, например, будет происходить проверка во время ее производства.

Так мы пришли к концепции скан-цепочки (scan chain). Если хотя бы один триггер в цепочке уйдет в отказ, будет недостоверен или цепочка не будет соединена, очевидно, что на выходе проявится неисправность. В одной цепочке получается по одному порту для входа и один для выхода — решение доступно, не нужно тратить ресурсы на коммутацию.
Эта скан-цепочка позволяет реализовывать три режима: загрузку, захват и выгрузку. В схемах ниже широкая желтая стрелка показывает направление движения в цепи.

Начнем с режима shift-in — загрузки входных данных сканирования. Представим, что линия залипла там, где на схеме красный нолик. Как мы его проверим? Для этого нужно обосновать все входы — то есть подать такие значения на входы комбинационных элементов, чтобы неисправность или место, которое мы тестируем, точно защелкнулось в какой-нибудь из триггеров. Подаем последовательность 1 и 0. Выставляем Scan Enable = 1 и задвигаем по цепочке.

Значение обосновали, теперь нужно продвинуть эту неисправность и захватить ее в наш триггер. Выставляем Scan Enable = 0, и в следующем такте этот 0 задвинется в триггер. Как только получили все эти значения, снова включаем shift-in и последовательно выгружаем цепочку. На выходе увидим 1 и 0, хотя должны были увидеть 1 и 1. Проблема вскрыта.
Design for Testing (DFT) станет новым треком нашего с МИЭТ хакатона SoC Design Challenge 2026. Если вам интересно попробовать силы в этой дисциплине — регистрация открыта, ждем!
Boundary Scan (BSCAN)
С самой внутренней логикой разобрались, но как понять, что мы способны с ней взаимодействовать? Нужно проверять pad-ячейки, от которых данные приходят во внутреннюю логику. На помощь здесь приходит BSCAN (Boundary Scan) — граничное сканирование с помощью JTAG и TAP-контроллера. JTAG придумали для тестирования, но потом оказалось, что он еще удобен и для функциональных сценариев — например, для прошивки микросхем включая FPGA.

BSCAN позволяет тестировать систему в целом. Благодаря BSCAN и JTAG можно, в принципе, отказаться от контроля стыковки, который используют в работе над бортовыми вычислителями.. Обернув цепочку TDI и TDO по всей системе, можно проверить, что все правильно состыковано и система целостна.
Основа BSCAN — специальные BSCAN-ячейки, которые позволяют забирать сигнал из внешней среды во внутреннюю и наоборот посредством специальных тестовых внутренних структур через TAP-контроллер.

На схеме приведен пример для входной ячейки. Выходная ячейка будет отличаться только тем, что вместо From System Pin будет From Logic Pin, а вместо To System Logic — To Output System Port.

Рассмотрим основные режимы работы Boundary Scan. В режиме Func сигнал проходит с пина прямо к логике, если сигнал Mode = 0.

В режиме Shift, как в скан-цепочке, можно из TAP-контроллера пустить данные через JTAG по всей цепочке внешней микросхемы, то есть сделать обход всей системы. Для задвигания данных по цепочке выставляется ShiftDR=1, а тактирование идет по ClockDR.

В режиме Update мы из скан-триггеров загружаем данные во внутреннюю логику и конфигурируем, например, ПЛИС. Для защелкивания данных триггеров скан-цепочки используется сигнал Mode=1 и тактирование UpdateDR.

Наконец, мы применяем режим Capture, когда надо захватить сигналы с внешнего пина просто во внутренний скан для последующей внутренней конфигурации, апдейта или других целей. Для защелкивания данных с входных пинов триггерами цепочки используется сигнал ShiftDR=0 и тактирование ClockDR.
Built-In Self-Test (BIST) — самотестирование
Помимо логических структур, в СнК есть блоки, которые имеют нецифровую природу — например, аналоговые или MEMS-датчики. Их тестирование скан-способами займет очень много времени, поэтому в DFT мы используем для этого BIST-контроллер.

Простейший BIST-контроллер состоит из контроллера, который запускают с верхнего уровня, чтобы увидеть, как прошел тест. TPG на схеме выше — это генерация тестов, что производится самим BIST. Далее идет сам блок и анализ выходных воздействий.

Память, которая является, по сути, цифровым блоком, мы оборачиваем в MBIST (Memory BIST), то есть самотестируемую логику. Почему? Потому что для тестирования памяти недостаточно просто подавать данные на триггеры, а нужно записать информацию. Тестовые паттерны должны строиться так, чтобы данные записывались по адресу. Кроме того, соседские ячейки памяти друг друга не любят. Поэтому обязательно нужно проводить тестирование со сложными операциями, где друг с другом взаимодействуют соседние элементы.
MBIST позволяет нам закладывать в память отказоустойчивость. На производстве может случиться, что крайние колонки памяти уходят в отказ, и поэтому в середину, в надежную часть памяти встраивают дополнительные колонки. С помощью MBIST можно переконфигурировать память: указать одни колонки как битые, а другие — как замену им.

Для аналоговых блоков мы используем обычно loopback — петли обратной связи. В АФАР-модулях, например, так можно ответвить энергию из передатчика в приемник с помощью циркулятора. Циркулятор — это СВЧ-элемент, который передает энергию из передатчика в антенну, из антенны в приемник, а также из передатчика в приемник. Таким образом, мы задаем в цифровой системе некий зондирующий сигнал, подаем его в передатчик и по обратной петле снова загоняем в цифровую систему.
Самая важная структура
Важность описанных структур в проекте зависит от того, какую СнК мы делаем.

В чисто цифровом дизайне бо́льшую часть теста покрывают скан-структуры. В цифро-аналоговом дизайне — другие структуры, которые связаны именно с аналоговой проверкой, и MBIST, потому что в аналоговых блоках используется очень много памятей.
«Черная пятница» для системы питания
У нас есть отработанные стандарты, но все-таки стоит подумать об альтернативных архитектурах. Serial scan как последовательная задвижка может повредить микросхему из-за избыточного выделения тепла, высокого энергопотребления.

В режиме Capture (захвата) может проявиться эффект IR-drop. Если микросхема не сгорит, могут все равно поплыть временные показатели: увеличатся delay или transition faults, из-за чего мы получим неверные данные по выходу годных изделий.

Одно из альтернативных решений здесь — архитектура random-access scan (RAS). Она позволяет адресно обращаться к ячейкам, как в оперативной памяти.

Как устроена ячейка в этой архитектуре? В ней нужно добавлять мультиплексор, который будет включать scan input (SI) только при обращении к ячейке. Если нет обращения, ячейка просто сохраняет текущее значение.
Сравниваем serial scan и random access scan на FPGA
Я попробовал реализовать оба инструмента на FPGA: встроить и коммутировать скан-триггеры, имплементировать дизайн на FPGA и проанализировать результаты по метрикам:
Потребляемая мощность — это ключевой параметр, по которому random access scan предлагается как альтернатива.
Требуемые логические ресурсы, или занимаемая площадь.
Количество данных для тестирования.
Начну с простейшего дизайна, UART, чтобы получить достаточно осмысленный конечный автомат для работы. Сконфигурируем его с помощью обычной case-конструкции:
case(state) default: begin state<= IDLE; end IDLE: begin out<= 1'b1; done<= 1'b0; busy<= 1'b0; bitIdx<= 3'b0; data<= 8'b0; if(start & en)begin data<= in; state<= START_BIT; end end ... endcase
Если бы мы разрабатывали ASIC, то в Cadence и подобных инструментах уже были бы реализованы все нужные скрипты для вставки DFT, для реализации scan chain. Вначале пройдем объявление тестовых сигналов, проверку на соответствие правилам встраивания DFT. Соединим все и сгенерируем тестовые последовательности:
... read_libs read_hdl elaborate define_test_signal check_dft_rules syn check_dft_rules connect_scan_chains write_dft_atpg ...
Но в FPGA это не реализовано. В космической отрасли, например, люди создают самописные генераторы на Tcl или пытаются реализовывать через RTL. Я решил пока пойти по простому пути и создал автомат через assign и мультиплексоры. Да, этот сценарий более громоздкий, но для первого опыта подойдет.
assign next_state = (!rstn) ? IDLE : (state == IDLE) ? (start & en ? START_BIT : IDLE) : (state == START_BIT) ? DATA_BITS : (state == DATA_BITS) ? (&bitIdx ? IDLE : DATA_BITS) : IDLE;
Дальше собираюсь подключать все скан-триггеры именно в RTL, поэтому я выстраиваю их в определенном порядке. Так в тестах я буду знать, какой именно триггер находится в отказе.
wire [15:0] bus_in; assign bus_in = {next_data, next_bitIdx, next_state, next_out, next_done, next_busy}; wire [15:0] bus_out; assign {data, bitIdx, state, out, done, busy} = bus_out;
Для начала соединю scan-цепочку, то есть обычный serial scan:
genvar i; generate assign scan_chain[0] = scan_in; for (i=0; i<16; i = i+1) begin: scan_ch scan_cell scan_ff( .clk(clk), .rstn(rstn), .data_in(bus_in[i]), .data_out(bus_out[i]), .scan_en(scan_en), .scan_in(scan_chain[i]), .scan_out(scan_chain[i+1]) ); end assign scan_out = scan_chain[16]; endgenerate
Через generate я просто последовательно соединяю scan out и scan in предыдущих элементов со следующими.
genvar i; generate for (i=0; i<16; i = i+1) begin: scan_ras ras_cell ras_ff( .clk(clk), .rstn(rstn), .data_in(bus_in[i]), .data_out(bus_out[i]), .ad_sel (test_address[i]), .scan_en(scan_en), .scan_in(scan_in), .scan_out(scan_out_ras_w[i]) ); end assign scan_out_ras = scan_out_ras_w; endgenerate
В random access scan схема другая. Scan-in — это единственный входной порт, это не шина данных. Для выходной шины данных, что интересно, мы все равно сохраняем сдвиговый регистр на вывод scan-out информации. Можно использовать сжатие, чтобы выводить только сигнатуру теста, но здесь для маленького дизайна и простоты реализации я решил, что это будет последовательный сдвиг. Получается, что мощность здесь экономится только на записи, а на чтение есть подобие своего shift-out режима.
После этой реализации я долго думал, а можно ли было еще проще реализовать скан-режим в RTL? Можно! Необходимо всего лишь добавить условие в блок always перед описанием case-конструкции конечного автомата. То есть разделить RTL на два режима — func и scan. И уже в scan режиме описать любую схему коммутации триггеров.
always @(posedge clk or negedge rstn) begin
if(!rstn)begin
…
end else if(scan_en) begin
{out, done, busy, data, bitIdx, state} <= {done, busy, data, bitIdx, state, scan_in};
end else begin
case(state)
default: begin
state<= IDLE;
end

Теперь опишу весь прототип. Я решил задавать тестовые последовательности и вообще управлять тестированием через SPI. Есть контроллер для управления тестовым режимом, дизайн со встроенными скан-ячейками и input-output, чтобы проверить корректность работы дизайна.

Оборудование для пробы пера я использовал самое дешевое и простое: плату Tang Nano 1K с GW1NZ-LV1 и плату LC Technology на основе микросхемы CH341A в качестве преобразователя USB в SPI.
Тестовые воздействия я задавал в программе CH341Tool. Использовал ее, потому что не хотел разбираться с верхнеуровневым программированием и различными библиотеками. В этой программе даже предусмотрены различные датчики: можно напрямую подключить какой-нибудь настоящий датчик из умного дома и взаимодействовать с ним через плату.

На скриншоте — последовательность для serial scan в режиме bypass. Я просто подавал значения 1, BD и другие, а потом они приходили на выходе.
Как вообще строятся тестовые векторы? Для Serial Scan необходимо загрузить данные в соответствии с количеством триггеров. Например, 0х12 0х34 означает, что я буду загружать в 4, 7, 11, 12 и 14 триггеры единицу, в остальные — нули. И эту цепочку я последовательно задвигаю. Для RAS (Random Access Scan) же нужно адресное обращение. Поэтому одним байтом мы можем обратиться к одному из 225 триггеров. Например, 0x01 0x12 0x34 задает последовательность scan_in = 1 для триггеров с адресом {X, Y} = {1, 2} и {3, 4}.
Казалось, одного взгляда на тестовые векторы хватит для предварительных выводов о том, что для дизайна RAS тестовые данные будут более избыточны. На самом деле мы видим, что в зависимости от конфигурации и того, какие значения мы будем подавать, можно уменьшать или увеличивать последовательность данных. А для serial scan мы каждый раз должны загружать все триггеры. Получается, что для релевантного сравнения тестовых векторов все-таки нужно сгенерировать реальные ATPG.


Как я оценивал потребляемую мощность? В случае serial scan самый требовательный случай — когда мы будем загружать последовательность 101101, потому что каждый раз триггерам нужно будет переключаться. Для RAS в разное время мы можем подавать информацию в разное число триггеров: вероятность их переключения будет разной. Но из-за очень большого объема логики RAS выигрыш все равно не превышает 10%.
Все таки RAS или serial scan?
Serial scan еще долго будет отраслевым стандартом, а RAS останется альтернативой. Причем очень спорной, потому что задействует очень много логики при совсем небольшом выигрыше в потребляемой мощности.
ATPG можно сгенерировать в Cadence или Tessent, но при этом надо учитывать реальную имплементацию FPGA. Сейчас я планирую статью как раз по имплементации ATPG в стороннем инструменте таким образом, чтобы тесты сочетались с архитектурой FPGA. И напоследок вернусь к тому, с чего начал: тема DFT для ПЛИС актуальна как минимум в сферах, где очень высоки требования по надежности и отказоустойчивости — бортовая техника, атомная промышленность и другие.
DFT станет новым треком нашего с МИЭТ хакатона SoC Design Challenge, который пройдет в апреле в Зеленограде. Кроме того, будут треки по RTL-проектированию, топологии, UVM- и системной верификации. Все подробности можно найти на странице хакатона. Присоединяйтесь!