Моя прошлая запись «Верификация простейших свойств микроархитектуры: пространство состояний» удостоилась ряда критических высказываний за то, что повествование велось на уровне «мышления» инструментов формальной верификации. :) Вопрос же о том, используется ли вообще высокоуровневая информация о структуре исследуемых моделей, и если используется, то как, остался вне поля зрения.
Но прежде чем рассказывать о том, как мы используем структурную информацию, потребуется некоторое вступление. Собственно, цель этой записи рассказать подробнее о языке моделирования xMAS, который используется нами для моделирования микроархитектуры (предыдущегоболее чем краткого рассказа недостаточно, по причине отсутствия технических деталей.).
Язык моделирования
xMAS – это язык формального моделирования микроархитектуры, позволяющий создавать наглядные модели коммуникационных фабрик путем композиции небольшого набора стандартных блоков (примитивов), соединяемых каналамидля передачи пакетов с данными. Каждый канал соединяет ровно два примитива, один из которых является инициатором передачи, а другой – ее получателем. Все примитивы модели работают синхронно по одному тактовому сигналу. В данной записи используется базовый набор примитивов xMAS (рис. 1).
![]()
Рисунок 1. Базовый набор примитивов xMAS.
Исток (source) порождает пакеты с заданным значением e. В зависимости от типа истока очередной пакет может появляться на каждом такте (eager), с недетерминированной задержкой (non-det) или не появляться никогда (dead).
В процессе обработки пакеты хранятся в очередях (queue) конечного размера. Очередь реализует дисциплину FIFO. Новый пакет может быть помещен в очередь только при наличии в ней свободного места. Задержка прохождения пакета через пустую очередь равна одному такту.
Примитив преобразования (func) изменяет значение пакета, используя функцию f. Пакет на выходе барьера (join) появляется только при наличии пакета на каждом из его входов, при этом выходной пакет вычисляется функцией hот входных пакетов. При прохождении через форк (fork) входной пакет преобразуется в два выходных пакета с помощью функций fи g. Примитив ветвления (switch) вычисляет предикат sот входного пакета, и в зависимости от его значения перенаправляет пакет на один из двух выходов. При слиянии (merge) входные пакеты передаются на выход без изменений. Если два пакета поступают на входы слияния одновременно, посылается один из них, а второй задерживается. Для выбора между пакетами используется справедливый алгоритм арбитража. Допускается слияние с произвольным числом входных каналов.
Сток (sink) поглощает пакеты, завершая их обработку в модели. Аналогично истоку, существуют три разновидности стока (eager, non-det, dead).
Канальный протокол передачи данных
Протокол передачи данных на канале u использует два логических управляющих сигнала: u.irdy (от “initiator ready”) и u.trdy (от “target ready”) (рис. 2). Сигнал u.irdyравен True, если инициатор готов передать один пакет с данными. Аналогично, сигнал u.trdyравен True, если получатель готов принять один пакет с данными. Передача пакета происходит при u.xfer=u.irdy∙u.trdy=True. Значение пакета задается сигналом u.data и может быть любого конечного типа.
![]()
Рисунок 2. Управляющие сигналы внутри канала xMAS.
Диаграмма переходов протокола передачи данных xMAS показана на рис. 3. Запрещенные переходы (например, из FwRetryв Inactive) гарантируют, что однажды инициированная передача пакета не может быть отменена. Протокол, обладающий таким свойством, назовем устойчивым (persistent). Семантика примитивов xMAS определена так, чтобы гарантировать устойчивость на всех каналах в модели.
![]()
Рисунок 3. Диаграмма состояний протокола xMAS. а) разрешенные переходы (также допустим переход из каждого состояния в себя); б) запрещенные переходы.
Все примитивы xMAS обладают простой синхронной семантикой, которая может быть выражена на языке битовых уравнений, связывающих значения сигналов irdy, trdy и data на примыкающих к примитиву каналах.
Используя уравнения примитивов, любой модели xMAS можно поставить в соответствие синхронную логическую сеть, называемую синхронной моделью. Для описания синхронной модели достаточно элементарных возможностей любого синхронного языка HDL (например, языка Verilog). Выполнением (или исполнением) модели xMAS назовем любую допустимую последовательность состояний ее синхронной модели. Значения регистровых переменных в начальном состоянии считаются заданными (например, все очереди пусты).
Уравнения примитивов
Исток(source) имеет единственный выход o, параметризуется выражением eи описывается следующими уравнениями:
Здесь oracleобозначает недетерминированное значение. Управляя поведением oracleможно получить различные варианты source (eager/non-det/dead). Второй терм в уравнении для o.irdyпозволяет гарантировать устойчивость на канале o. Здесь и далее pre()используется для указания значения выражения на предыдущем такте.
Сток(sink) – двойственный истоку примитив. Он имеет единственный вход iи описывается следующим уравнением:
Примитив преобразования(func) используется для моделирования трансформации данных. Он параметризуется функцией f, имеет один вход i, один выход o. Описывается уравнениями:
Заметим, что здесь f– комбинационная функция, применяемая к данным на входе.
Форк(fork) – примитив с одним входом i, двумя выходами a, bи параметризованный функциями fи g, определяющими значения пакетов на выходах как функцию данных пакета на входе. Его уравнения:
Такая, несколько неочевидная конструкция уравнений, с одной стороны, гарантирует, что передачи на каналах i, a, bпроисходят одновременно. А с другой стороны – удовлетворяет требованиям канального протокола связанным с устойчивостью.
Барьер(join) – двойственный форку примитив, выполняющий функции синхронизации. Имеет два входа: aи b, выход o, и параметризован функцией h. Формально:
Примитив ветвления(switch) выполняют функции маршрутизации. Он имеет единственный вход i, два выхода aи b, а также параметризован предикатом s. Уравнения:
Слияние(merge) используется для моделирования арбитража между конкурирующими пакетами. Примитив имеет два входа: aи b, и выход o. Уравнения.
В уравнениях используется дополнительный внутренний сигнал sсо значениями {0,1}, описывающий состояние реализуемого алгоритма арбитража. Для большинства задач, не связанных с точной временной оценкой, подходит любой разумный алгоритм арбитража, удовлетворяющий требованию справедливости. Как правило, мы используем круговой алгоритм арбитража (round-robin).
Очередь(queue) – единственный примитив, реализующий хранение пакетов внутри модели. Параметризуется числом вмещаемых пакетов k, имеет единственный вход и выход. Уравнения:
Здесь enqи deq - комбинационные сигналы событий помещения очередного пакета внутрь очередь и выхода пакета из очереди. А numобозначает число пакетов внутри очереди и изменяется следующим образом:
Пример
В завершение вступительной записи рассмотрим примеры простых моделей на рис. 4. Надеюсь, это поможет интуитивно понять работу отдельных примитивов в составе моделей.
Модель M1, состоит из истока, стока и двух очередей. Пакеты с 8-битным значением 0 порождаются истоком, проходят последовательно очереди q1и q2 (в каждой очереди пакет находится не менее одного такта) и поглощаются стоком. Заметим, что исток и сток являются справедливыми, т.е. порождают и поглощают пакеты через произвольные конечные интервалы времени.
![]()
Рисунок 4. Примеры моделей
В модели M2каждый новый пакет удваивается, проходя через форк. Одна копия пакета попадает в цепочку из двух очередей q1, q2, вторая – в очередь q3. Барьер на выходе снова собирает две копии пакета вместе и передает результат стоку. Очевидно, что общее число пакетов в верхней цепи q1, q2равно числу пакетов в очереди q3:
Заметим, что приведенное выше соотношение между числом пакетов в очередях является ничем иным, как вспомогательным инвариантом, полученным на основе высокоуровневой информации о структуре модели. Но если в случае модели M2данное соотношение очевидно (для человека, не для инструментов верификации!), то для более сложных моделей поиск аналогичных инвариантов представляет непростую задачу.
На этом я заканчиваю вступление. О том, как автоматически находить подобные инварианты, связывающие множество очередей читайте в следующем выпуске. :)
Но прежде чем рассказывать о том, как мы используем структурную информацию, потребуется некоторое вступление. Собственно, цель этой записи рассказать подробнее о языке моделирования xMAS, который используется нами для моделирования микроархитектуры (предыдущегоболее чем краткого рассказа недостаточно, по причине отсутствия технических деталей.).
Язык моделирования
xMAS – это язык формального моделирования микроархитектуры, позволяющий создавать наглядные модели коммуникационных фабрик путем композиции небольшого набора стандартных блоков (примитивов), соединяемых каналамидля передачи пакетов с данными. Каждый канал соединяет ровно два примитива, один из которых является инициатором передачи, а другой – ее получателем. Все примитивы модели работают синхронно по одному тактовому сигналу. В данной записи используется базовый набор примитивов xMAS (рис. 1).
![](http://software.intel.com//sites/default/files/m/2/6/7/p7_1.png)
Рисунок 1. Базовый набор примитивов xMAS.
Исток (source) порождает пакеты с заданным значением e. В зависимости от типа истока очередной пакет может появляться на каждом такте (eager), с недетерминированной задержкой (non-det) или не появляться никогда (dead).
В процессе обработки пакеты хранятся в очередях (queue) конечного размера. Очередь реализует дисциплину FIFO. Новый пакет может быть помещен в очередь только при наличии в ней свободного места. Задержка прохождения пакета через пустую очередь равна одному такту.
Примитив преобразования (func) изменяет значение пакета, используя функцию f. Пакет на выходе барьера (join) появляется только при наличии пакета на каждом из его входов, при этом выходной пакет вычисляется функцией hот входных пакетов. При прохождении через форк (fork) входной пакет преобразуется в два выходных пакета с помощью функций fи g. Примитив ветвления (switch) вычисляет предикат sот входного пакета, и в зависимости от его значения перенаправляет пакет на один из двух выходов. При слиянии (merge) входные пакеты передаются на выход без изменений. Если два пакета поступают на входы слияния одновременно, посылается один из них, а второй задерживается. Для выбора между пакетами используется справедливый алгоритм арбитража. Допускается слияние с произвольным числом входных каналов.
Сток (sink) поглощает пакеты, завершая их обработку в модели. Аналогично истоку, существуют три разновидности стока (eager, non-det, dead).
Канальный протокол передачи данных
Протокол передачи данных на канале u использует два логических управляющих сигнала: u.irdy (от “initiator ready”) и u.trdy (от “target ready”) (рис. 2). Сигнал u.irdyравен True, если инициатор готов передать один пакет с данными. Аналогично, сигнал u.trdyравен True, если получатель готов принять один пакет с данными. Передача пакета происходит при u.xfer=u.irdy∙u.trdy=True. Значение пакета задается сигналом u.data и может быть любого конечного типа.
![](http://software.intel.com//sites/default/files/m/5/7/5/p7_2-1024x447.png)
Рисунок 2. Управляющие сигналы внутри канала xMAS.
Диаграмма переходов протокола передачи данных xMAS показана на рис. 3. Запрещенные переходы (например, из FwRetryв Inactive) гарантируют, что однажды инициированная передача пакета не может быть отменена. Протокол, обладающий таким свойством, назовем устойчивым (persistent). Семантика примитивов xMAS определена так, чтобы гарантировать устойчивость на всех каналах в модели.
![](http://software.intel.com//sites/default/files/m/6/4/2/p7_3-1024x339.png)
Рисунок 3. Диаграмма состояний протокола xMAS. а) разрешенные переходы (также допустим переход из каждого состояния в себя); б) запрещенные переходы.
Все примитивы xMAS обладают простой синхронной семантикой, которая может быть выражена на языке битовых уравнений, связывающих значения сигналов irdy, trdy и data на примыкающих к примитиву каналах.
Используя уравнения примитивов, любой модели xMAS можно поставить в соответствие синхронную логическую сеть, называемую синхронной моделью. Для описания синхронной модели достаточно элементарных возможностей любого синхронного языка HDL (например, языка Verilog). Выполнением (или исполнением) модели xMAS назовем любую допустимую последовательность состояний ее синхронной модели. Значения регистровых переменных в начальном состоянии считаются заданными (например, все очереди пусты).
Уравнения примитивов
Исток(source) имеет единственный выход o, параметризуется выражением eи описывается следующими уравнениями:
o.irdy := oracle or pre(o.irdy and not o.trdy)
o.data := e
Здесь oracleобозначает недетерминированное значение. Управляя поведением oracleможно получить различные варианты source (eager/non-det/dead). Второй терм в уравнении для o.irdyпозволяет гарантировать устойчивость на канале o. Здесь и далее pre()используется для указания значения выражения на предыдущем такте.
Сток(sink) – двойственный истоку примитив. Он имеет единственный вход iи описывается следующим уравнением:
i.trdy := oracle or pre(i.trdy and not i.irdy)
Примитив преобразования(func) используется для моделирования трансформации данных. Он параметризуется функцией f, имеет один вход i, один выход o. Описывается уравнениями:
o.irdy := i.irdy
o.data := f(i.data)
i.trdy := o.trdy
Заметим, что здесь f– комбинационная функция, применяемая к данным на входе.
Форк(fork) – примитив с одним входом i, двумя выходами a, bи параметризованный функциями fи g, определяющими значения пакетов на выходах как функцию данных пакета на входе. Его уравнения:
a.irdy := i.irdy and b.trdy a.data := f(i.data)
b.irdy := i.irdy and a.trdy b.data := g(i.data)
i.trdy := a.trdy and b.trdy
Такая, несколько неочевидная конструкция уравнений, с одной стороны, гарантирует, что передачи на каналах i, a, bпроисходят одновременно. А с другой стороны – удовлетворяет требованиям канального протокола связанным с устойчивостью.
Барьер(join) – двойственный форку примитив, выполняющий функции синхронизации. Имеет два входа: aи b, выход o, и параметризован функцией h. Формально:
a.trdy := o.trdy and b.irdy
b.trdy := o.trdy and a.irdy
o.irdy := a.irdy and b.irdy o.data := h(a.data, b.data)
Примитив ветвления(switch) выполняют функции маршрутизации. Он имеет единственный вход i, два выхода aи b, а также параметризован предикатом s. Уравнения:
a.irdy := i.irdy and s(i.data) a.data := i.data
b.irdy := i.irdy and not s(i.data) b.data := i.data
i.trdy := (a.irdy and a.trdy) or (b.irdy and b.trdy)
Слияние(merge) используется для моделирования арбитража между конкурирующими пакетами. Примитив имеет два входа: aи b, и выход o. Уравнения.
o.data∶=if s=0 then a.data else b.data
o.irdy∶=a.irdy and b.irdy
a.trdy∶=a.irdy and o.trdy and (s=0)
b.trdy∶=b.irdy and o.trdy and (s=1)
В уравнениях используется дополнительный внутренний сигнал sсо значениями {0,1}, описывающий состояние реализуемого алгоритма арбитража. Для большинства задач, не связанных с точной временной оценкой, подходит любой разумный алгоритм арбитража, удовлетворяющий требованию справедливости. Как правило, мы используем круговой алгоритм арбитража (round-robin).
Очередь(queue) – единственный примитив, реализующий хранение пакетов внутри модели. Параметризуется числом вмещаемых пакетов k, имеет единственный вход и выход. Уравнения:
o.irdy := (pre(num) != 0)
i.trdy := (pre(num) != k)
enq := i.irdy and i.trdy
deq := o.irdy and o.trdy
Здесь enqи deq - комбинационные сигналы событий помещения очередного пакета внутрь очередь и выхода пакета из очереди. А numобозначает число пакетов внутри очереди и изменяется следующим образом:
num :=
pre(num) + 1 if enq and not deq
pre(num) - 1 if deq and not enq
pre(num) otherwise
Пример
В завершение вступительной записи рассмотрим примеры простых моделей на рис. 4. Надеюсь, это поможет интуитивно понять работу отдельных примитивов в составе моделей.
Модель M1, состоит из истока, стока и двух очередей. Пакеты с 8-битным значением 0 порождаются истоком, проходят последовательно очереди q1и q2 (в каждой очереди пакет находится не менее одного такта) и поглощаются стоком. Заметим, что исток и сток являются справедливыми, т.е. порождают и поглощают пакеты через произвольные конечные интервалы времени.
![](http://software.intel.com//sites/default/files/m/5/1/c/p7_4.png)
Рисунок 4. Примеры моделей
В модели M2каждый новый пакет удваивается, проходя через форк. Одна копия пакета попадает в цепочку из двух очередей q1, q2, вторая – в очередь q3. Барьер на выходе снова собирает две копии пакета вместе и передает результат стоку. Очевидно, что общее число пакетов в верхней цепи q1, q2равно числу пакетов в очереди q3:
q1.num + q2.num = q3.num
Заметим, что приведенное выше соотношение между числом пакетов в очередях является ничем иным, как вспомогательным инвариантом, полученным на основе высокоуровневой информации о структуре модели. Но если в случае модели M2данное соотношение очевидно (для человека, не для инструментов верификации!), то для более сложных моделей поиск аналогичных инвариантов представляет непростую задачу.
На этом я заканчиваю вступление. О том, как автоматически находить подобные инварианты, связывающие множество очередей читайте в следующем выпуске. :)