Научная статья на тему 'Некоторые вопросы автоматизации построения тестовых программ для модулей обработки переходов микропроцессоров'

Некоторые вопросы автоматизации построения тестовых программ для модулей обработки переходов микропроцессоров Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
166
46
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
АВТОМАТИЗИРОВАННОЕ ПОСТРОЕНИЕ ТЕСТОВЫХ ПРОГРАММ / ФУНКЦИОНАЛЬНАЯ ВЕРИФИКАЦИИ МОДУЛЕЙ ОБРАБОТКИ ПЕРЕХОДОВ МИКРОПРОЦЕССОРОВ

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Камкин А. С.

В работе рассматриваются вопросы автоматизированного построения тестовых программ, предназначенных для функциональной верификации модулей обработки переходов микропроцессоров. Формулируются задачи, возникающие при создании таких программ, и предлагаются техники их автоматизированного решения. Статья фокусируется на общих вопросах тестирования механизмов обработки переходов и не затрагивает проблемы специфичные для конкретных микропроцессорных архитектур. Предложенные техники можно использовать в промышленных генераторах тестовых программ.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Некоторые вопросы автоматизации построения тестовых программ для модулей обработки переходов микропроцессоров»

Некоторые вопросы автоматизации построения тестовых программ для модулей обработки переходов микропроцессоров

А. С. Камкин катк1п@1АргаА\ ги

Аннотация. В работе рассматриваются вопросы автоматизированного построения тестовых программ, предназначенных для функциональной верификации модулей обработки переходов микропроцессоров. Формулируются задачи, возникающие при создании таких программ, и предлагаются техники их автоматизированного решения. Статья фокусируется на общих вопросах тестирования механизмов обработки переходов и не затрагивает проблемы специфичные для конкретных микропроцессорных архитектур. Предложенные техники можно использовать в промышленных генераторах тестовых программ.

1. Введение

Во всех универсальных, вычислительно полных по Тьюрингу, компьютерах, начиная с электронного калькулятора ЕМАС (1946 г.), в том или ином виде присутствуют инструкции перехода — инструкции, позволяющие управлять потоком выполнения программы: осуществлять переходы на заданные инструкции, организовывать ветвления и циклы. Устройство компьютера, отвечающее за обработку таких инструкций, обычно называют модулем обработки переходов1.

С точки зрения программиста, возможности модуля обработки переходов типичного микропроцессора невелики и сводятся к условным и безусловным переходам, вызовам подпрограмм и возвратам из них. В то же время внутренняя, скрытая от посторонних глаз, логика работы модуля может быть очень сложной и запутанной за счет использования механизмов повышения производительности: предсказания переходов, суперскалярного выполнения и многих других.

1 Не следует понимать этот термин буквально — физически отдельного модуля обработки переходов может и не существовать. В этом случае корректнее говорить о логике обработки переходов.

Не секрет, что сложность является основным источником ошибок, допускаемых при проектировании микропроцессоров. В случае же если сложность является скрытой, обнаружить ошибки бывает достаточно трудно из-за отсутствия средств, позволяющих явно управлять поведением тестируемой системы или хотя бы наблюдать его. Таким образом, несмотря на кажущуюся простоту, модули обработки переходов микропроцессоров могут содержать в себе нетривиальные, трудно обнаруживаемые ошибки.

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

Поскольку модуль обработки переходов тесно взаимодействует с другими компонентами микропроцессора, имеет смысл проверять его работу системно, в связке с окружением. Наиболее распространенным способом системной верификации микропроцессоров и других программируемых устройств является тестирование моделей уровня регистровых передач (RTL-моделей) с помощью тестовых программ. Предметом статьи является автоматизация построения таких программ. В работе рассматриваются вопросы генерации структур переходов (по сути, графов потока управления), трасс выполнения (маршрутов в графе потока управления) и управляющего кода (вспомогательных инструкций, обеспечивающих проход по заданному маршруту). Предлагаемые подходы основаны на комбинаторных техниках и могут использоваться для разных микропроцессорных архитектур.

Статья организована следующим образом. В разделе 2 кратко описываются возможности модулей обработки переходов микропроцессоров, и рассматриваются проблемы, возникающие при их верификации. Раздел 3 определяет основные понятия используемого подхода, а его детальное описание дается в разделе 4. В разделе 5 описывается инструментальная поддержка, и приводятся примеры тестовых программ. Раздел 6 является заключением.

2. Обработка переходов в микропроцессорах

Рассмотрим кратко, что представляют собой модули обработки переходов современных микропроцессоров, и какие проблемы возникают при их верификации.

2.1. Модули обработки переходов

Модулем обработки переходов (branch unit) называется подсистема микропроцессора, отвечающая за обработку инструкций переходов —

инструкций, осуществляющих изменение потока управления2. Простейшими инструкциями данного типа являются инструкции безусловного перехода. К более сложным относятся инструкции условного перехода, которые изменяют поток управления только при выполнении определенных условий на значения операндов (обычно условия переходов выражаются через сравнения вида =, Ф, <, >, >, <). Во многих микропроцессорных архитектурах также предусмотрены инструкции вызова подпрограмм и возврата из них. При вызове подпрограммы перед передачей управления на нее происходит сохранение адреса следующей инструкции (адреса возврата) в специальном регистре или стеке. При завершении подпрограммы этот адрес считывается и по нему делается передача управления.

Современные микропроцессоры имеют конвейерную архитектуру, что означает, что они параллельно обрабатывают сразу несколько инструкций (каждую на своей стадии) [1]. В зависимости от организации микропроцессора выполнение перехода может приводить к приостановке конвейера (когда после выборки инструкции перехода загрузка конвейера приостанавливается до тех пор, пока не будет принято решение о переходе) или сбросу конвейера (когда все инструкции, загруженные на конвейер после инструкции перехода, сбрасываются, если принимается решение о выполнении этого перехода).

В некоторых микропроцессорах для борьбы со сбросами конвейера имеются так называемые слоты задержки переходов (branch delay slots) [1,2]. Это означает, что перед обработкой инструкции перехода микропроцессор успевает выполнить (точнее, загрузить на конвейер) одну или несколько инструкций, следующих за ней, (для каяедой архитектуры это число фиксировано — в архитектурах MIPS или SPARC имеется один слот задержки, в SHARC DSP их два [2]). Инструкции, расположенные в слотах задержки перехода, выполняются независимо от того, выполнен переход или нет. Заметим, что в слот задержки нельзя помещать инструкцию перехода.

Для постоянной загрузки конвейера (минимизации числа сбросов) также применяется прогнозирование переходов. Стратегии прогнозирования могут быть статическими и динамическими, кроме того, они могут различаться для разных типов инструкций перехода [3]. Статические методы предсказания переходов являются наиболее простыми. Их суть состоит в том, что все переходы одного типа либо всегда прогнозируются выполненными, либо всегда прогнозируются невыполненными. Например, в ранних микропроцессорах архитектуры MIPS и SPARC условные переходы «назад» предсказывались выполненными, а условные переходы «вперед» — нет. Статическое предсказание используется и в современных микропроцессорах, но только в качестве «подстраховки», когда невозможно применение динамических методов [3].

2 Под изменением потока управления понимается изменение счетчика адреса — регистра микропроцессора, содержащего адрес текущей инструкции.

Динамические методы подразумевают анализ истории переходов. Примером метода такого типа является двухуровневый алгоритм Иеха (УсИ). используемый в микропроцессорах 1гие1 архитектуры Р6 [4]. Первый уровень алгоритма представлен таблицей, хранящей для «каждой»3 инструкции перехода историю четырех последних переходов. История хранится в виде четырехбитного паттерна и обновляется при каждом выполнении соответствующей инструкции (осуществляется сдвиг паттерна на один разряд влево, после чего в младший разряд записывается 1 или О в зависимости от того, выполнен переход или нет). Второй уровень состоит из шестнадцати двухбитных счетчиков с насыщением. Паттерн первого уровня используется в качестве индекса в таблице счетчиков. При осуществлении перехода счетчик инкрементируется, в противном случае декрементируется. Переход прогнозируется тогда и только тогда, когда счетчик находится в состоянии 2 или 3.

2.2. Особенности верификации модулей обработки переходов

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

1. тип инструкции перехода (условный или безусловный переход, вызов подпрограммы или возврат из нее) — разные типы инструкций обрабатываются по-разному, поскольку у них просто напросто разная семантика;

2. направление перехода (вперед или назад) — направление перехода обычно влияет на прогноз, даваемый статическими методами;

3. способ передачи адреса перехода (непосредственно или через регистр) — если адрес перехода передается непосредственно, направление перехода можно определить уже на этапе декодирования инструкции;

4. структура переходов (граф потока управления) — некоторые архитектуры оптимизируют выполнение кода с определенной структурой переходов, например, «коротких» циклов;

5. трасса выполнения (история переходов) — история переходов влияет на результаты динамического прогнозирования, которые, в свою очередь, влияют на спекулятивную загрузку инструкций на конвейер;

6. типы инструкций в слотах задержки переходов (если архитектура предусматривает наличие слотов задержки) — в слотах задержки

3 Понятно, что размер таблицы конечен. Идентификация инструкций осуществляется по младшим битам адреса.

132

могут находиться разные инструкции, в том числе, вызывающие исключения;

7. типы спекулятивно выполняемых инструкций (типы инструкций, следующих за переходом или находящихся по адресу перехода) — в микропроцессорах возможны ограничения на инструкции, которые можно выполнить спекулятивно;

8. вызовы исключений (исключения в слотах задержки и других инструкциях) — обработка исключений, вызванных инструкциями, находящихся в слотах задержки, обычно происходит особым образом, а инструкции, вызывающие исключения, не всегда можно выполнить спекулятивно;

9. наличие зависимостей между инструкциями (по регистрам и через память) — зависимости, например, между инструкциями, расположенными после перехода, и инструкциями до перехода (или инструкциями в слотах задержки) может приводить к блокировкам конвейера при спекулятивном выполнении;

10. положение инструкции перехода в памяти (относительно буфера предвыборки, кэш-памяти, страницы виртуальной памяти) — в зависимости от положения инструкции в памяти обработка перехода по ряду причин может происходить по-разному.

Как видно, факторов, влияющих на обработку переходов, достаточно много и ручная разработка полного комплекта тестовых программ вряд ли возможна. Огромное пространство состояний делают несостоятельной и чисто случайную генерацию. Целью настоящей работы является создание методологической поддержки для автоматизированной генерации тестовых программ. Поскольку задач, возникающих при тестировании модулей обработки переходов, достаточно много, мы решили ограничиться некоторыми из них, которые не связаны с техническими деталями работы таких устройств. Рассматриваемые вопросы затрагивают (в той или иной степени) только первые семь факторов из числа перечисленных (типы инструкций, структуры переходов и трассы выполнения).

3. Основные понятия предлагаемого подхода

В предлагаемом подходе построение тестовых программ осуществляется автоматически или полуавтоматически на основе формальной спецификации системы команд и описания тестовых ситуаций и зависимостей для инструкций микропроцессора. Построение тестовых программ осуществляется путем создания различных цепочек инструкций (последовательностей из инструкций разных типов с разной структурой переходов) и комбинирования для них тестовых ситуаций (событий, связанных с выполнимостью условий переходов, исключениями и т.п.) и зависимостей (взаимосвязей между инструкциями через регистры или

память) [5]. Рассматриваемый подход можно использовать и при создании тестовых программ на основе шаблонов, когда инженер-верификатор описывает набор ограничений (тестовый шаблон), а программа генерируется автоматически на основе заданных ограничений.

3.1. Структура тестовой программы

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

Таким образом, структуру тестовой программы можно описать с помощью формулы Test= {{Preh Action„ Post^)}i=0 пЛ, где Pret — это инициализирующие

инструкции, Actionj- тестовое воздействие, Postj — тестовый оракул. В

простейшем случае каждая тестовая программа состоит из одного тестового варианта, то есть Test = (Pre, Action, Post).

Ниже в качестве примера приведен фрагмент тестовой программы в системе команд MIPS [6], содержащий один тестовый вариант.

/////////// Инициализирующие инструкции ///////////

// Инициализация регистров инструкции 1: lui vO, Oxdead ori vO, vO, Oxbeaf

lui aO, Oxdead ori aO, aO, Oxbeaf

// Инициализация регистров инструкции 2: ori al, zero, 0x0

j test_action_begin nop // Слот задержки

////////////// Тестовое воздействие //////////////

backward_jump:

j test_action_end

4 Основным способом проверки правильности работы модуля обработки переходов является сравнение трасс, полученных при выполнении ЯТЬ-модели микропроцессора и его эталонного симулятора, а не встроенные в программы тестовые оракулы.

addi vl, vl, 1 // Ситуация: Overflow=false

test_action_begin:

beq vO, aO, backward_jump // Ситуация: Condition=true

addi vl, al, 2009 // Ситуация: Overflow=false

test_action_end:

///////////////// Тестовый оракул /////////////////

// Запись эталонного значения ori tl, zero, 2010

// Ошибочное завершение при несоответствии результата bne vl, tl, test_fails nop // Слот задержки

Тестовое воздействие состоит из четырех инструкций: инструкции

безусловного перехода j; инструкции addi, складывающей содержимое второго регистра-операнда с 16-битным непосредственным значением и сохраняющей результат в первом регистре; инструкции beq, осуществляющей переход в случае совпадения значений регистров; и еще одной инструкции addi. Точка входа в тестовое воздействие обозначена меткой test_action_begin. Напомним, что в архитектуре MIPS у инструкций перехода имеется один слот задержки, поэтому в данном примере при выполнении переходов (инструкций j и beq) также выполняются инструкции addi. Тестовый оракул сравнивает выходной регистр инструкций addi (он одинаковый у обеих инструкций) с эталонным значением.

Несколько слов о вспомогательных инструкциях, используемых в примере. Инструкция lui загружает 16-битное непосредственное значение в разряды [31:16] регистра-результата (биты [63:32] получаются путем знакового расширения). Инструкция ori осуществляет побитовое ИЛИ значения второго регистра с 16-битным значением, заданным в третьем операнде (результат записывается в первый регистр). Инструкция bne выполняет условный переход в случае неравенства значений регистров.

3.2. Тестовый шаблон

Важным понятием подхода, которое следует рассмотреть подробнее, является понятие тестового шаблона. Тестовым шаблоном называется абстрактная форма представления тестового воздействия или тестовой программы в целом, в котором вместо конкретных значений операндов инструкций указываются ограничения (тестовые ситуации и зависимости), которым они должны удовлетворять. По сути, каждый тестовый шаблон (если он не является противоречивым) задает некоторую цель тестирования. Разные тестовые программы, соответствующие одному и тому же тестовому шаблону, эквиваленты в том смысле, что каждая из них достигает эту цель.

Ниже приведен тестовый шаблон из рассмотренного выше примера. Шаблон состоит из инструкций j, addi, beq и еще одной инструкции addi. Для обеих инструкций addi задана одинаковая ситуация Overflow =false (отсутствие переполнения), а для инструкции beq ситуацией является Condition=true (выполнимость условия перехода). Кроме того, между двумя инструкциями addi имеется зависимость (первый и второй регистры первой инструкции должны совпадать с первым регистром второй инструкции). backward_j ump:

j test_action_end

addi R, R, ? 0 Overflow=false

test_action_begin:

beq ?, ?, backward_jump 0 Condition=true

addi R, ?, ? 0 Overflow=false

test_action_end:

Выполнение описываемого этим шаблоном тестового воздействия начинается с инструкции beq, расположенной по метке test_action_begin. Условие перехода выполнено (Condition=true), поэтому осуществляется переход на метку backward_j ump. Выполняемая в слоте задержки инструкция addi не вызывает переполнения (Overflow = false). Далее осуществляется безусловный переход по метке test_action_end вместе с выполнением еще одной инструкции addi, которая также не вызывает переполнения (Overflow =false).

3.3. Структура переходов

Важным атрибутом тестового шаблона является структура переходов. В широком смысле, структурой переходов называется граф потока управления, задаваемый тестовым шаблоном. В более практическом понимании, структурой переходов является тестовый шаблон, который не содержит информации о тестовых ситуациях и зависимостях, но в котором все переходы снабжены метками. Ниже приведена структура переходов для рассматриваемого примера и соответствующий ей граф потока управления.

backward_j ump:

j test_action_end addi ?, ?, ? test_action_begin:

beq ?, ?, backward_jump addi ?, ?, ? test action end:

Рис. 1. Структура переходов и соответствующий граф потока управления.

Более подробно понятие структуры переходов рассматривается в разделе «Построение структур переходов».

3.4. Трасса выполнения

Помимо структуры переходов тестовый шаблон определяет трассу выполнения, задающую порядок обработки инструкций тестового воздействия (маршрут в графе потока управления). Для этого каждая инструкция условного перехода, входящая в тестовый шаблон, помечается последовательностью из значений истинности условий перехода (первое значение соответствует первому выполнению инструкции, второе — второму и т.д.; если инструкция перехода является недостижимой, она помечается пустой последовательностью). В рассматриваемом примере трасса выполнения полностью определяется истинностью условия перехода инструкции Ьед (Сопс1Шоп=1гие).

Рис. 2. Трасса выполнения и соответствующий маршрут в графе потока управления.

Более подробно трассы выполнения и метод их генерации рассматриваются в разделе «Построение трасс выполнения».

3.5. Управляющий код

Для того чтобы обеспечить заданную трассу выполнения, в тестовый шаблон встраивается управляющий код. Для рассматриваемого примера это можно и не делать, поскольку единственная инструкция условного перехода, содержащаяся в тестовом шаблоне, выполняется ровно один раз, следовательно, всю необходимую подготовку можно выполнить в инициализирующих инструкциях до выполнения тестового воздействия. В общем случае инструкции условного перехода могут выполняться по нескольку раз, причем значения истинности условий при этом могут меняться.

Рассмотрим структуру переходов, отличающуюся от рассмотренной ранее тем, что в ней отсутствует инструкция з, которая располагалась по метке Ьаскиагс^итр. Для этой структуры переходов рассмотрим трассу

выполнения, соответствующую последовательности {true,false} значений истинности условий инструкции beq.

backward_j ump:

addi ?, ?, ? test_action_begin:

beq ?, ?, backward_jump 0 Condition={true, false} addi ?, ?, ? test_action_end:

Обозначим регистры инструкции beq как R1 и R2. Для того чтобы обеспечить истинность условия при первом выполнении beq, инициализирующие инструкции заносят в R1 и R2 одинаковые значения. Еще раз подчеркнем, что инициализация не является частью управляющего кода, поскольку она выполняется вне тестового шаблона. Управляющий код должен изменить значение истинности условия с true на false после первого выполнения инструкции. Это можно сделать следующим образом. backward_j ump:

addi Rl, Rl, 1 // управляющий код

addi ?, ?, ?

test_action_begin:

beq Rl, R2, backward_jump 0 Condition={true, false}

addi ?, ?, ?

test_action_end:

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

Заметим, что в данном примере изменение значения регистра R1 (или R2) можно осуществить и в одной из инструкций addi, находящихся в слотах задержки переходов. Более подробно вопросы генерации управляющего кода рассматриваются в разделе «Построение управляющего кода».

4. Предлагаемый подход

В основе предлагаемого подхода к автоматизации генерации тестовых программ для модулей обработки переходов микропроцессоров лежат техники построения структур переходов, трасс выполнения и управляющего кода. Входными параметрами генератора являются классы эквивалентности инструкций переходов, базовых блоков (цепочек инструкций, не содержащих переходов) и инструкций в слотах задержки, а также ограничения на число переходов в тестовых шаблонах и на длину трасс выполнения. При описании предлагаемых техник будем, не ограничивая общности рассуждений, считать, что в микропроцессоре имеется один слот задержки перехода.

4.1. Построение структур переходов

Пусть инструкции переходов разбиты на классы эквивалентности, то есть задано фактор-множество = I,, 'L,,,0,0. где Е„ — фактор-множество

условных переходов, a Egoto — безусловных5. Пусть, кроме того, заданы классы эквивалентности слотов задержки ЪМау и базовых блоков ЕЫосЛ. Каждый элемент Е,, имеет вид if С goto /, где С — это условие, которое может принимать значения true или false, а / — метка перехода, принимающая целое неотрицательное значение. Элементы множества Egoto имеют вид goto /. Для генерации структур переходов устройство слотов задержки и базовых блоков не важно.

Обозначим через Е объединение u и ЕЫос*. Структурой переходов называется конечная последовательность S из элементов множества Е, в которой после каждого перехода (элемента множества следует слот

задержки (элемент множества 1£/„/„,)6. а все метки переходов удовлетворяют ограничению 0 < / < |<Sj. Пример структуры переходов приведен ниже.

{В0, if Q goto 6, D2, -S3, goto 1, D5,B6, if C7 goto 0, Z)8}.

Здесь и далее мы будем обозначать слоты задержки буквой D с некоторым индексом, а базовые блоки — буквой В. Заметим, что в структурах переходов допускаются переходы на любые инструкции, в том числе на самих себя. Структура переходов, в которой нет ни одного перехода, называется

вырожденной.

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

4.2. Построение трасс выполнения

Трассой выполнения структуры переходов S = } называется

последовательность Т= {Tj} из индексов элементов S. которая удовлетворяет следующим свойствам:

5 Инструкции вызова подпрограмм и возврата из них для простоты изложения не рассматриваются.

6 Напомним, что мы рассматриваем случай, когда у инструкции перехода имеется ровно один слот задержки. Если слота задержки нет или их несколько, ограничения очевидным образом меняются.

• То = О7;

• если Л'|7,| е Е^-и .S'|7,| = i f С,- goto Lh тогда

о |7] > /+1, Тт = ТУЫ8 и выполняется одно из свойств:

■ если Тт = |<S]-1, \Т\ = /+ 2 (С, = false)',

■ если Tm < |<S]-1, \Т\ > /+2 и Ti+2 = Tj+2 (С, = false)',

| 71 > /'+2 и Ti+2 = Li (С, = true);

• если .S'|7,| 6 Egoto и .S'|7,| = goto тогда

о 171 > i+2, Tm = Tj+1 и Ti+2 = Ь{,

• если .S'|7,| 6 Ej,ocfc тогда

о если Ti = |S|-1,171 = /+1;

о если T, < ISI-1,171 > /+1 и Tm = 77+1.

Примером трассы выполнения для рассмотренной в предыдущем разделе структуры переходов {В0, if Q goto 6, D2, Въ, goto 1, D5, B6, if C7 goto 0, Ds} является последовательность:

{О, 1,2, 6, 7, 8}.

Ей соответствует следующий поток выполнения (последовательность элементов структуры переходов):

{В0, if Ci goto 6, D2, B6, if C7 goto 0, D$}.

Заметим, что в большинстве случаев по трассе можно восстановить значения истинности условий выполняемых переходов (исключения составляют ситуации, когда метка перехода указывает на инструкцию, следующую за слотом задержки). В дальнейшем будем считать, что каждое вхождение условного перехода в трассу выполнения (или поток выполнения) помимо индекса (соответственно, элемента структуры переходов) содержит также значение истинности условия. Таким образом, трасса и соответствующий ей поток выполнения для рассматриваемого примера имеют вид:

{0, (1, true), 2, 6, (1, false), 8} и {Во, if Ci|*.Me goto 6, D2, B6, if Cllfaise goto 0, Ds}.

Трасса выполнения полностью определяется своей подпоследовательностью, содержащей только вхождения условных переходов. Такая подпоследовательность называется редуцированной трассой. Для рассматриваемого примера редуцированная трасса имеет вид {(1, true), (1,false)}.

1 Для простоты считается, что выполнение начинается с первой инструкции.

8 Выполнение слота задержки перехода.

140

Рассмотрим более сложную трассу выполнения той же самой структуры переходов:

{О, (1, true), 2, 6, (7, true), 8, О, (I, false), 2, 3, 4, 5, (1, true), 2, 6, (7,false), 8}. Ее редуцированное представление имеет вид:

{(1, true), (7, true), (I, false), (1, true), (7, false)}.

Или еще короче в форме отображения:

{(1 —>■ {true, false, true}), (7 —>■ {true, false})}. Последовательность, состоящая из значений истинности условий перехода, называется трассой выполнения инструкции перехода. Пусть Т — трасса выполнения структуры переходов, тогда через T(i) будем обозначать трассу выполнения инструкции перехода с индексом /.

При построении трасс выполнения заданной структуры переходов указываются такие параметры, как максимальная длина трассы выполнения, максимальная длина редуцированной трассы или максимальная длина трассы одного перехода. Для перечисления трасс, удовлетворяющих такого рода ограничениям, можно использовать поиск в глубину. Ниже приводится алгоритм построения трасс выполнения, в котором используется представление редуцированных трасс в форме отображений. Кроме того, помимо трасс условных переходов в алгоритме используются трассы безусловных переходов (последовательности, состоящие только из true). вход: структура переходов S; вход: максимальная длина трассы переходаhf; выход: множество трасс выполнения R;

1. результат R <— 0;

2. стек возвратов В <— 0;

3. признак завершенности трассы С <— false;

4. индекс текущей инструкции / <— 0;

5. текущая редуцированная трасса Т <— 0;

6. цикл: ищем первую инструкцию перехода в S, начиная с

инструкции /:

a. если инструкция перехода не найдена, то

i. R<^R^{T};

ii. если В = 0, то алгоритм завершает работу;

iii. С <— true;

iv. /' <— top(B);

v. перейти на цикл;

b. если инструкция перехода найдена и / — ее индекс, то

9 Для наглядности здесь используется ограничение на длины трасс выполнения отдельных переходов.

!• I <~j',

ii. если С = false и |7X0I <M, to

1. Г«(-(7\{О)и{/->7Х0-{*}},где

a. к = false, если S|/] e

b. k= true, если SI/] e Egoto;

2. push(B, i);

3. i <— l, где

a. I = i+1, если S|/] e Y.g,

b. / = Lt, если S])'] e Egoto и S|/'] = goto Z,;

4. перейти на цикл;

iii. если С = true И T(/)|7t0|-l = false, то

1. Т <— (7\{/})и{/ —>■ Д/)о,....

2. / <— Li, где Л'|/1 = if С, goto Z,;

3. С <— false;

4. перейти на цикл;

iv. пока В Ф0 делать

1. Т <— (7\{/})и{/ —>■ 7ХОо,..., |7toi-2};

2. /' <— рор(В);

3. если T(i) / 0и 7X/)|y(i)|-i = false, то

a. Т<— (7\{/})и{/ —> ДОо,.... \T(i)\-2'{tme}}\

b. / <— Lh где Л'|/1 = if С, goto Z,

c. С <— false;

d. перейти на цикл;

v. если 5 = 0, то алгоритм завершает работу. Рассмотрим пример построения множества трасс выполнения для структуры переходов <S'= {В0, if С] goto 0, /)2! при ограничении на длину трасс переходов М= 2.

В начале работы алгоритма R <— 0. В <— 0, С <— false, / <— 0 и 7: <— 0. На первой итерации цикла ищется первая инструкция перехода в структуре S. Такая инструкция расположена по индексу j = 1 (это единственный переход в 5). Делается присваивание i<-j= 1. Поскольку С = false, \T(i)\ <\1 (\Т( 1) = 0) и S[i] е (.S’| 11 = i f f '/ goto 0). в трассу T(i) добавляется k= false (T( 1) = {false}), в стек возвратов В добавляется индекс 1 (В= {1}), индекс / увеличивается на 1 (/ = 2). После этого осуществляется поиск перехода, начиная с индекса / = 2. Переход не найден, поэтому в результат добавляется трасса Т = {(1 —> {false})}, делается присваивание С <— true, индекс / получает значение верхушки стека В (В = {1} и / = 1).

На следующей итерации ищется переход, начиная с / = 1. Находится инструкция, расположенная по индексу j = i = 1. Поскольку С = true и в трассе 7X1) последний элемент равен false, он меняется на true (7X1) = {true}), после чего делаются присваивания / <— L\ = 0 и С <— false. Снова ищется переход — на этот раз, начиная с / = 0. Индекс найденной инструкции / = 1. Делается присваивание i<-j= 1. Поскольку С = false, \T(i)\ <\1 (\Т( 1) = 1) и ,S'|/1 е L,,. в трассу T(i) добавляется к= false (7X1) = {true,false}), в стек В добавляется индекс 1 (В ={1,1}), индекс / увеличивается на 1 0 = 2). Далее ищется переход, начиная с / = 2. Такой инструкции нет. В результат добавляется трасса Т = {(1 —> {true, false})}, после чего делаются присваивания С <— true и /' <- top(B) (В= {I, 1} и / = 1).

На очередной итерации ищется переход, начиная с / = 1. Снова j = i = 1. Поскольку С = true и в трассе 7( 1) последний элемент равен false, осуществляется его замена на true (7X1) = {true, true}). После этого выполняются присваивания / <— Ьх = 0 и С <— false. Снова ищется переход, и находится по индексу / = 1. Делается присваивание / <— j = 1. Поскольку \T(i)\ = М и В Ф 0, из трассы T(i) удаляется последний элемент (7X1) = {true}), из стека извлекается индекс 1 и присваивается / (В = {1} и / = 1). Последний элемент трассы T(i) не равен false. Трасса укорачивается (7X1) = 0), из стека извлекается индекс и присваивается / (В = 0 и / = 1). Стек пуст, поэтому алгоритм завершает свою работу. Его результатом являются две трассы:

. Т={(\^ {false})}',

• Т = {(1 —>■ {true,false})}.

4.3. Построение управляющего кода

Для построения управляющего кода, то есть кода отвечающего за проход по требуемой трассе, используется следующий подход. Инструкциям перехода сопоставляются уникальные регистры, называемые управляющими, которые не используются в других инструкциях тестового шаблона10. В определенные позиции тестового шаблона вставляются инструкции, называемые управляющими, которые изменяют значения управляющих регистров так, чтобы обеспечить требуемую трассу выполнения. Совокупность управляющих инструкций образуют управляющий код.

Чтобы обеспечить заданные значения истинности условий, для каждой инструкции условного перехода в тестовую программу добавляется массив значений и осуществляется загрузка одного или обоих управляющих регистров последовательными значениями этого массива. Возникает задача

10 При наличии зависимостей по регистрам между инструкциями переходов и другими инструкциями тестового шаблона задача построения управляющего кода существенно усложняется и не всегда разрешима.

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

Пусть дана невырожденная структура переходов S и ее трасса Т. Пусть / — индекс некоторой инструкции перехода в структуре S. Будем говорить, что трасса T(i) является пустой, если / не содержится в Т. Непустые трассы, содержащие равные между собой значения истинности условий, будем называть тривиальными. Таким образом,

• { } — пустая трасса;

• {true, true, ..., true} — тривиальная трасса;

• {false, false, ..., false} — тривиальная трасса;

Напомним, что начальные значения истинности условий переходов обеспечиваются инициализирующими инструкциями тестового шаблона, поэтому управляющий код имеет смысл только для тех инструкций условных переходов, трассы которых являются непустыми и нетривиальными. Сегментом 7:,( трассы выполнения Т называется ее фрагмент {Th Тм, ..., 7}}, заключенный между двумя соседними вхождениями одной и той же инструкции перехода. Пусть 7:, = (к, (\). а 7} = (к, С2). В этом случае говорят, ЧТО Ti j является сегментом инструкции к. Если К тому же Q = С2, сегмент называется тривиальным.

Очевидно, что управляющий код должен вызываться, по крайней мере, внутри нетривиальных сегментов. Нетрудно показать, что при достаточном числе регистров (когда каждая инструкция перехода имеет по крайней мере один управляющий регистр отличный от других) справедливо следующее утверждение.

Утверждение. Для заданной трассы выполнения структуры переходов построение управляющего кода возможно тогда и только тогда, когда внутри каждого нетривиального сегмента трассы есть хотя бы один базовый блок. Рассмотрим структуру переходов S и ее трассу выполнения Т. Пусть / — индекс некоторой инструкции условного перехода в структуре S, а I — подмножество индексов базовых блоков. Будем говорить, что множество I является покрытием инструкции / базовыми блоками, если для любого нетривиального сегмента Т’ инструкции / существует индекс /е/. лежащий в Т’. Базовые блоки, входящие в покрытие, будем называть покрывающими.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

Рассмотрим следующую задачу. Для заданной структуры переходов и ее трассы выполнения требуется найти минимальное покрытие базовыми блоками каяедой инструкции условного перехода. В некотором смысле, это

задача минимизации объема управляющего кода11. Задачи такого типа известны как задачи о минимальном покрытии. Для их эффективного решения используются приближенные алгоритмы, среди которых наиболее известен «жадный» алгоритм. Эго простой метод, идея которого, применительно к нашей задаче, состоит в следующем. Для каяедой инструкции условного перехода сначала выбирается базовый блок, который покрывает максимальное число нетривиальных сегментов рассматриваемой инструкции, затем из числа оставшихся берется блок, покрывающий максимальное число еще не покрытых сегментов и т.д. Алгоритм завершается, когда множество выбранных блоков становится покрытием.

В качестве примера рассмотрим структуру переходов

{В0, if Ci goto 6, D2, Въ, goto 1, D5,B6, if C7 goto 0, Ds}

и ее трассу выполнения

{0, (1, true), 2, 6, (7, true), 8, 0, (1, false), 2, 3, 4, 5, (1, true), 2, 6, (7, false), 8}.

Для инструкции условного перехода, расположенной по индексу 1, имеется два нетривиальных сегмента:

• {(1, true), 2, 6, (7, true), 8, 0, (1, false)}, содержащий два базовых блока 0 и 6;

• {(1, false), 2, 3, 4, 5, (1, true)}, содержащий один базовый блок 3.

Каждый базовый блок из множества {0, 3, 6} покрывает только один из двух сегментов. В качестве покрытия можно использовать {0, 3} или {3, 6}.

Пусть для каяедой инструкции условного перехода построено покрытие базовыми блоками. В каждый покрывающий блок вставляются управляющие инструкции. В частности, это могут быть инструкции вида: lw Rl,(Р1) addi PI, Р1, 4

Первая инструкция загружает в один из управляющих регистров (регистр R1) значение из заранее подготовленного массива. Вторая инструкция увеличивает текущую позицию в массиве (регистр Р1). При таком подходе значение второго управляющего регистра считается фиксированным.

11 Теоретически, управляющий код может оказывать влияние на ситуацию, создаваемую тестовым шаблоном, поэтому, чтобы снизить это влияние, желательно строить управляющий код как можно меньшего объема. Для большинства практических задач влиянием управляющего кода небольшого объема можно пренебречь.

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

Для трасс с «простой» структурой возможно построение компактного управляющего кода, состоящего из одной управляющей инструкции на одну инструкцию условного перехода. Эго удобно, поскольку управляющую инструкцию в этом случае можно разместить в слоте задержки перехода. Рассмотрим инструкцию, в которой в качестве условия перехода используется сравнение Rl > R2. Для удобства будем считать, что значение регистра R2 равно 0. Если трасса выполнения перехода имеет вид {true, ..., true, false, ...,false}, можно в управляющий регистр R1 записать положительное число (величина которого зависит от количества элементов true в трассе и числа выполнений покрывающих блоков), а в качестве управляющей инструкции использовать декремент R1. Если же трасса имеет вид {false, ..., false, true, ..., true}, можно записать в R1 неположительное значение и использовать в качестве управляющей инструкции инкремент R1.

Сравнения вида >, > и < обрабатываются аналогичным образом. При сравнении на равенство или неравенство возможно аналогичное упрощение управляющего кода для трасс выполнения перехода, содержащих одно вхождение true или false соответственно, то есть трасс вида {false, ..., false, true, false, ..., false} для равенства и

{true, ..., true, false, true, ..., true} для неравенства.

5. Инструментальная поддержка

В данном разделе дается краткое описание инструментальной поддержки предложенных методов, и приводятся примеры сгенерированных тестовых программ.

5.1. Генератор MicroTESK

Рассмотренные техники построения тестовых программ для модулей обработки переходов микропроцессоров интегрированы в генератор MicroTESK [7], разработанный в Институте системного программирования РАН. Генератор строит тестовые программы путем комбинирования цепочек инструкций ограниченной длины. Для каждой цепочки перебираются множества зависимостей и тестовых ситуаций (для инструкций перехода в

качестве ситуаций используются трассы выполнения). Для сокращения перебора используются различные эвристики, например, факторизация инструкций, ограничение глубины зависимостей и ограничение длины трасс выполнения.

MicroTESK - Demo Test Program Generator - <Unknown>. tern plate *

E.ile Generation Window tielp

LJ >

Г/1ч J ІиЗИН a- 0 tD nop

nop Q logic

о

Olui О on

И Q integer Ё - 0 a^di 0 Q branch beq

* ййі Q memory

іЗіи

□в=» a system L- □ wait

Processor DEMO (template)

Test Subgroup or Instruction @ О nop

logic

Equivalence Class Empty Instruction

Integer Arithmetic

Instructions Situations

TIT 1/1

0/3

i/i

2/2

0/2

0/1

0/0

2/2

2/2

0/0

0/0

Nothing

[ Select All I [ Clear All |

Top Groups: 3/6 (50%) Leaf Groups: 3/6 (50%) Instructions: 4/10 (40%) 5fcuations: 5/5 (100%)

<Unknown>

■QB

Generator Console

Creating package: test_00055

Hoving file: test_00055.S to tests/test_0Q055

Generating file: test_00056.S

Creating package: test_00056

Hoving file: test_00056.S to tests/test_00056

Generation is finished

Test situations: 11208

Top Groups: 0/0 (100%) Leaf Groups: 3/6 (50%) Instructions: 4/10 (40%) Situations: 5/5 (100%)

Puc. 3. Графический интерфейс демо-версии генератора MicroTESK.

Более подробная информация по методу, используемому в MicroTESK, доступна в статье [5]. Демо-версию генератора можно скачать на сайте [8].

5.2. Примеры тестовых программ

Ниже приведены фрагменты тестовых программ в системе команд MIPS, построенных автоматически генератором MicroTESK. При построении программ использовалась одна инструкция условного перехода (инструкция beq) и одна инструкция безусловного перехода (инструкция j). Для базовых блоков и слотов задержки использовались инструкции пор и addi. Рассматриваемые программы предполагают следующую обработку исключений. Если исключение возникает при выполнении инструкции базового блока, управление передается на следующую за ней инструкцию. Если исключение происходит в слоте задержки перехода, инструкция, вызвавшая исключение, заменяется на пор, после чего управление повторно передается на инструкцию перехода.

Пример 1

// Инициализация инструкции beq[3] ori $15, $0, 0x0

// Инициализация инструкции addi[5]

lui $24, 0x8000

ori $24, $24, 0x2f56

// Тестовое воздействие test_action_5 01 6:

j If // Метка: 1, Трасса: {true}, Блоки: {}

1:

пор

beq $15, $0, 4f // Метка: 4, Трасса: {true}, Блоки: {}

4 :

addi $2, $24, OxcbOl // Переполнение Пример 2

// Инициализация инструкции beq[l] ori $2, $0, 0x0

// Инициализация инструкции addi[7] lui $4, 0x7fff ori $4, $4, 0x8f01

// Инициализация инструкции beq[8]

la $25, beq trace array

ori $7, $0, 0x0

sw $7, 0($25)

addi $25, $25, 0x4

ori $7, $0, 0x0

sw $7, 0($25)

addi $25, $25, 0x4

lui $7, 0xb566

ori $7, $7, 0xa7e8

sw $7, 0($25)

addi $25, $25, 0x4

la $25, beq trace array

// Инициализация инструкции addi[9]

lui $12, 0x7fff

ori $12, $12, Oxf916

// Тестовое воздействие test_action_5197:

0:

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

beq $2, $0, 2f // Метка: 2, Трасса: {true, false,

false}, Блоки: {2}

nop 2 :

addi $2, $2, Oxffff // Управляющей код для beq[l] lw $9, 0($25) // Управляющей код для beq[8]

addi $25, $25, 0x4 // Управляющей код для beq[8]

addi $29, $4, 0x73a9 // Переполнение

beq $9, $0, Ob // Метка: 0, Трасса: {true, true, false},

Блоки: {2}

addi $15, $12, 0x5835 // Переполнение

6. Заключение

В статье рассмотрены общие задачи, возникающие при построении тестовых программ для модулей обработки переходов микропроцессоров, и методы автоматизации их решения. Предложенные методы можно использовать в инструментах генерации тестовых программ для микропроцессоров, в частности, они реализованы в генераторе MicroTESK. Описанный в статье подход не зависит от микроархитектуры микропроцессора. С одной стороны, это является достоинством, поскольку подход позволяет быстро создавать тесты на самых ранних стадиях проектирования. С другой стороны, для детального тестирования могут потребоваться более узконаправленные тесты, учитывающие специфику механизмов обработки переходов конкретной микропроцессорной архитектуры. Методы автоматизации разработки таких тестов являются темой наших дальнейших исследований.

Литература

[1] D. Patterson, J. Henessy. Computer Organization and Design. 3rd Edition, Morgan Kaufmann, 2005.

[2] Википедия (http://en.wikipedia.org), статья Branch delay slot.

[3] Википедия (http://en.wikipedia.org), статья Branch predictor.

[4] T.-Y. Yeh, Y.N. Patt. Two-Level Adaptive Training Branch Prediction. Proceedings of International Symposium on Microarchitecture, 1991.

[5] A.C. Камкин. Генерация тестовых программ для микропроцессоров. Труды ИСП РАН, т. 14, ч. 2. М., 2008. С. 23-63.

[6] MIPS64™ Architecture For Programmers. Revision 2.0. MIPS Technologies Inc., June 9,2003.

[7] A. Kamkin. MicroTESK: Automation of Test Program Generation for Microprocessors. Proceedings of East-West Design & Test Symposium, 2009.

[8] http://hardware.ispras.ru.

i Надоели баннеры? Вы всегда можете отключить рекламу.