PSL/A Practical Introduction to PSL/Basic Temporal Properties/ru
- 1. Introduction (en)
- 2. Basic Temporal Properties (en)
- 3. Some Philosophy (en)
- 4. Weak vs. Strong Temporal Operators (en)
- 5. SERE Style (en)
Содержание |
Основные временные свойства
В то время как Булевый слой состоит из Булевых выражений, которые выполняются или не выполняются в данном цикле, временной слой предоставляет путь для описания взаимоотношений между Булевыми выражениями по времени. PSL утверждение обычно представлено только в одном направлении - вперед, от первого цикла (также можно двигаться в обратном направлении, используя встроенные функции, такие как prev()
, rose()
и fell()
). Таким образом, простое PSL утверждение assert a;
показывает, что a должно утверждаться в самом первом цикле, в то время как PSL утверждение assert always a
, показывает, что a должно утверждаться в перовм цикле и в каждом следующем цикле, а это значит, что в каждом цикле.
Комбинированием временных операторов различными путями, мы можем установить свойства, такие как “каждый запрос получает подтверждение”, “каждому подтвержденному запросу предоставляется от четырех до семи циклов, в случаи, если запрос не приостановится первым”, “две последовательные записи не должны располагать по одному и тому же адресу” и “когда мы видим запрос на чтение с тэгом i, на следующих четырех передах данных ожидается увидеть тэг i
”.
Временной слой состоит из фундаментального языка (FL) и дополнительного расширения ветвления (OBE). FL используется для выражения свойств одного тракта, а также используется для моделирования или формальной верификации. OBE используется для выражения свойств, относящихся к набору трактов, например, “существует тракт, такой как ...”, и также используется для формальной верификации. В этой книге мы сконцентрируемся на фундаментальном языке.
Фундаментальный язык состоит из двух комплементарных стилей - LTL стиль, названный из-за временной логики LTL, на которой базируется PSL, и SERE стиля, названного из-за последовательного расширения регулярных выражений PSL, или SEREs. В этой главе мы представим основные временные операторы LTL стиля. Мы предоставим то, что будет достаточно для понимания основной идеи, и чтобы дать некий контекст, для философских замечаний,обсуждаемых далее.
В этой книге, мы широко используем примеры. Каждый пример свойства или утверждения связан с временной диаграммой (которую мы называем тракт) сгруппирован вместе на рисунке. Такой рисунок будет содержать один или более трактов, нумерованных в скобках строчными римскими цифрами, одного или более свойств, нумерованных добавление строчной буквы к номеру рисунка. Например, Рисунок 2.1 содержит тракт 2.1(i) и утверждения 2.1а, 2.1b, 2.1c.
2.1 Операторы always
и never
Нам уже встречались основные временные операторы always
и never
. Большинство свойств PSL начинаются с одного или с другого. Это, потому что “голое” (Булево) PSL свойство относится только к первому циклу тракта. Например, утверждение 2.1a требует только того, чтобы Булево выражение !(a && b)
выполнялось в первом цикле. Таким образом, утверждение 2.1а выполняется в тракте 2.1(i), потому что Булево выражение !(a && b)
выполняется в цикле 0. Для того, чтобы сформулировать, что мы хотим, чтобы оно выполнялось в каждом цикле проекта, мы должны добавить временной оператор always
, и получить утверждение 2.1b. Утверждение 2.1b не поддерживает тракт 2.1(i), потому что Булево выражение !(a && b)
не выполняется в цикле 5. Эквивалентно, мы можем поменять оператор always
и Булево отрицание !
с never
, чтобы получить утверждение 2.1с.
![]() |
---|
(i) Утверждение 2.1a выполняется, но 2.1b and 2.1c нет |
assert !(a && b); (2.1a) assert always !(a && b); (2.1b) assert never (a && b); (2.1c) |
Рис. 2.1: Операторы always и never |
Оба утверждения 2.1b и 2.1c показывают, что сигналы a и b взаимоисключающиеся. Очевидно, все, что может использоваться с оператором always
, может также использоваться с оператором never
и наоборот, происходит простое отрицанием операнда, когда происходит переключение между always
и never
. PSL, для удобства, предоставляет два варианта использования, иногда более удобно описать свойство положительным (если свойство выполняется на всех циклах), а иногда удобней отрицательным (если свойство не выполняется ни в каком цикле). В общем, существует много путей описания свойств в PSL. Мы рассмотрим другие примеры далее.
2.2 Оператор next
Другой временной оператор - это оператор next
. Он показывает, что свойство свойство должно выполняться, если его операнд выполняется в следующем цикле. Например, утверждение 2.2a показывает, что когда бы a не выполнялось, b должно выполниться в следующем цикле. Утверждение 2.2a использует другой важный оператор, логический оператор импликации (->
). В то время как оператор логической импликации Булевый, а не временной (он не свяжет два подсвойства по времени), это играет очень важную роль во многих временных свойствах. Логическая импликация prop1 -> prop2
выполняется, если prop1 не выполняется или prop2 выполняется. Это хорошо представляется, если принять это, как выражение вида if-then, с частью else. prop1 -> prop2
можно понимать, как если prop1, то далее следует prop2, иначе возвращается значение правда. Таким образом, под-свойство a -> next b
, в нашем примере, выполняется, если a не выполняется (потому что свойство по умолчание имеет значение правда) или, если a выполняется, а также и next b выполняется. Добавляя оператор always, мы получаем свойство, которое выполняется, если под-свойство a -> next b
выполняется в каждом цикле. Таким образом, утверждение 2.2а показывает, что когда бы ни выполнялось a
, b
должно выполняться в следующем цикле. Утверждение 2.2а выполняется по сигналу b
. Это показано в “if” и “then” примечании на тракте 2.2(ii). “Дополнительные” утверждения сигнала b
на циклах 1 и 10, спровоцированы утверждение 2.2а, потому что в нем ничего не сказано о значениях b в цикле, кроме того, что следует из из утверждения a
.
Отметим, что циклы вовлеченные в выполнение одного утверждения сигнала, могут перекрываться с теми,которые вовлечены в выполнение другого утверждения. Например, учитывая тракт 2.2(iii), который является простым трактом 2.2(ii) с if-then пронумерованными парами. Имеем четыре утверждения сигнала a
на тракте 2.2(iii), и таким образом четыре связанных цикла, в которых b
должен утверждаться. Каждая пара циклов (утверждение a
следует из утверждения b
) нумерованы в тракте 2.2(iii). Рассматривая пары 2 и 3. Сигнал a утверждается в 4 цикле пары 2, таким образом сигнал b должен утвердиться в 5 цикле, для того, чтобы утверждение 2.2а выполнялось. Сигнал а утверждается в цикле 5 пары 3, таким образом требуется утверждение сигнала b в 6 цикле. Пары 2 и 3 перекрываются, потому что, в то время как мы ищем утверждение сигнала b
в 5 цикле, для выполнения утверждения a
в 4 цикле, также, мы видим дополнительное утверждение сигнала a
, который рассматривается.
Утверждение 2.2а не выполняется в тракте 2.2(iv), потому что третье утверждение сигнала a
, в цикле 5, т.к. отсутствует утверждение сигнала b
в следующем цикле.
![]() |
---|
assert always (a -> next b); (2.2a) |
Рис.2.2: Операторы next и логическая импликация |
2.3 вариации next
, включая next_event
Свойство next
выполняется, если его операнд выполняется в следующем цикле. Вариации оператора next
позволяют указывать n-й следующий цикл и диапазоны будущих циклов. Свойство next[n]
выполняется, если его операнд выполняется в n-й следующий цикл. Например, утверждение 2.3а показывает, что когда бы сигнал a
не выполнялся, сигнал b
выполниться тремя циклами позже. Утверждение 2.3а выполняется в тактах 2.3(i), 2.3(iii), 2.3(iv), но не выполняется в трактах 2.3(ii) или 2.3(v), потому что отсутствует утверждение сигнала b
в цикле 7, и не выполняется в тракте 2.3(vi), потому что отсутствует утверждение сигнала b
в цикле 5.
![]() |
---|
assert always (a -> next[3] (b)); (2.3a) assert always (a -> next_a[3:5] (b)); (2.3b) assert always (a -> next_e[3:5] (b)); (2.3c) |
Рис. 2.3: Операторы next[n] , next_a[i:j] и next_e[i:j]
|
Свойство next_a[i:j] выполняется если его операнд выполняется во всех циклах от i-го следующего, до j-го следующего цикла включительно. Например, утверждение 2.3b показывает, что когда бы сигнал a не выполнялся, сигнал b выполняется тремя, четырьмя, пятью циклами позже. Выполняется в тракте 2.3(iii) и не выполняется в трактах 2.3(i), 2.3(ii), 2.3(iv), 2.3(v) или 2.3(vi).
Ранее мы обсуждали то факт, что циклы, вовлеченные в выполнение одного утверждения сигнала, могут перекрываться с циклами, вовлеченными для выполнения других утверждений сигнала. Тракт 2.3(iii) был представлен, чтобы с акцентировать это для утверждения 2.3b. Сигнал b
должен утверждаться в циклах с 5 до 7 (помечено “1”), в связи с утверждением сигнала a в цикле 2, и должен утверждаться в циклах с 7 до 9 (помечено “2”), в связи с утверждением в цикле 4.
Свойство next_e[i:j] выполняется, если существует цикл со следующего i
до следующих j
циклов, в которых выполняются операнды. Например, утверждение 2.3с показывает, что когда бы сигнал a
не выполнялся, сигнал b
выполняется тремя, четырьмя или пятью циклами позже. Нету ничего в утверждении 2.3с, что предотвращает единичное утверждение сигнала b
из выполнения многочисленных утверждений сигнала a
, таким образом это выполняется в тракте 2.3(vi), потому что утверждение сигнала b
, в цикле 7, появляется через 5 циклов после утверждения сигнала a
в цикле 2, и через три цикла после утверждения сигнала a
в цикле 4.
Утверждение 2.3с также выполняется на трактах 2.3(i), 2.3(iii), 2.3(iv) и 2.3(v), т.к. есть достаточное количество утверждений сигнала b
в соответствующее время. В трактах 2.3(i), 2.3(iii) и 2.3(iv) более, чем достаточно утверждений сигнал b
для выполнения утвержденного свойства (в тракте 2.3(i) утверждения сигнала b
в цикле 7 достаточно, потому что проходит 5 циклов после утверждения сигнала a
в цикле 2, и три цикла после утверждения сигнала a
в цикле 4). В тракте 2.3(v) достаточно утверждений сигнала b
для выполнения требований утверждения 2.3c.
Оператор next_event
концептуально расширенный оператор next
. В то время как next
относиться к следующему циклу, next_event
относиться к следующему циклу, в котором выполняется некоторое Булево условие. Например, утверждение 2.4а выражает требование того, что когда бы ни принимался запрос с высоким приоритетом (сигнал high_pri_req
утвержден), то следующим (утверждение сигнала gnt
) должен быть, также, запрос с высоким приоритетом (сигнал high_pri_ack
утвержден). Утверждение 2.4а выполняется в тракте 2.4(i). Существует два утверждения сигнала high_pri_req
, первое в цикле 1 и второе в цикле 10. Связанные утверждения gnt
встречаются в циклах 4 и 11, соответственно, и high_pri_ack
выполняется в этих циклах.
Оператор next_event
включает текущие циклы. Это значит, утверждения сигнала b
в текущем цикле должно считаться следующим утверждением сигнала b
в свойстве next_event(b)(p)
. Например, рассмотрим тракт 2.4(ii). Тракт 2.4(ii) так же, как тракт 2.4(i) ожидает, что существуют дополнительные утверждения high_pri_req
в цикле 8 и два дополнительных утверждения gnt
в циклах 8 и 9, одно из которых связанно с high_pri_ack
. Утверждение 2.4а выполняется на тракте Trace 2.4(ii), потому что утверждение gnt
в цикле 8 считается следующим утверждением gnt
для утверждения high_pri_req
в цикле 8. Если вы хотите исключить текущий цикл, можно вставить оператор next
для перемещения текущего цикла оператора next_event
на один цикл далее, как в утверждении 2.4b. Утверждение 2.4b не выполняется в тракте 2.4(ii). Потому что вставка оператора next
привела к смене циклов утверждений gnt
, с циклов 4, 8 и 11 для утверждения 2.4a на циклы 4, 9 и 11 для утверждения 2.4b, а в цикле 9 нету утверждений high_pri_ack
в тракте 2.4(ii).
Так как мы можем использовать next[i]
для индикации i-го следующего цикла, мы можем использовать next_event(b)[i]
для индикации i-го вхождения сигнала b
. Например, для выражения требования,что всегда выдается запрос (signal req
утвержден), готовность signal_last
должна быть утверждена на четвертом утверждении сигнала ready
. Утверждение 2.5а выполняется на тракте 2.5(i). Для первого утверждения req
, в цикле 1, четыре утверждения ready
происходят немедленно в следующих циклах. Для второго утверждения req
, в цикле 7, четыре утверждения ready
не происходят немедленно и не происходят последовательно - они распространяются через семь циклов, пресекаясь с циклами, в которых ready
не подтвержден. Однако, отметим,что в обоих случаях, сигнал last_ready
утверждается на четвертом утверждении ready
, таким образом утверждение 2.5a выполняется в тракте 2.5(i).
![]() |
---|
(i) Утверждение 2.5a выполняется |
assert always (req -> (2.5a) next event(ready)[4](last ready)); |
Рис. 2.5: next event[n] |
Как и с next_a[i:j]
и next_e[i:j]
, оператор next_event
также появляется в форме, которая позволяет выражать весь диапазон следующих циклов, или наличие будущего цикла в этом диапазоне. Форма next_event_a(b)[i:j](f)
показывает, что мы ожидаем выполнения f
на всех i-х через j-е вхождения b
. Например, утверждение 2.6a показывает, что когда мы видим запрос на чтение (утверждение сигнала read_request
) с тэгом эквивалентным i
, далее на следующих четырех передачах данных (утверждение сигнала data
), мы ожидаем увидеть тэг i
. Утверждение 2.6а использует конструкцию forall
, которую мы рассмотрим в деталях позже. Сейчас достаточно сказать, что утверждение 2.6а показывает требование, что должно выполняться для всех возможных значениях сигнала tag[2:0]
. Утверждение 2.6а выполняется в тракте 2.6(i), потому что после первого утверждения сигнала read_request
, где значение tag[2:0]
4, значение tag[2:0]
, также, 4 на четырех следующих утверждениях сигнала data
(в циклах 5, 9, 10 и 11). Более того, на втором утверждении сигнала read_request
, где tag[2:0]
имеет значение 5, значение tag[2:0]
, также, 5 на следующих четырех утверждениях сигнала data
(циклы с 17 по 20).
Для того, чтобы указать, что мы ожидаем чего-либо в одном из следующих циклов с i-го по j-й, мы можем использовать оператор next_event_e(b)[i:j](f)
, который показывает, что мы ожидаем выполнения f
в одном из циклов, с i-го по j-й, вхождения b
. Например, рассмотри опять утверждение 2.4a. Оно требует того, что когда бы ни приходил запрос высокого приоритета, следующим должен обработаться запрос с высоким приоритетом. Предположим вместо этого, что мы требуем, чтобы один из следующих двух грантов был для запроса с высоким приоритетом. Мы можем выразить это, используя утверждение 2.7a. Утверждение 2.7a выполняется в тракте 2.7(i), потому что всегда, когда сигнал high_pri_req
утверждается, сигнал high_pri_ack
утверждается на одном из следующих двух утверждений gnt
.
Синтаксис спецификации диапазона для всех операторов, включая тех, которые мы еще не рассматривали, зависит от фундаментального языка. В вариантах Verilog, SystemVerilog и SystemC, это код [i:j]
. Для VHDL - это код [i to j]
. Для GDL это - [i..j]
.
![]() |
---|
(i) Утверждение 2.6a выполняется |
assert forall i in {0:7}: (2.6a) always ((read_request && tag[2:0]==i) -> next_event_a(data)[1:4](tag[2:0]==i)); |
Рис. 2.6 next_event a[i:j]
|
2.4 Операторы until
и before
![]() |
---|
(i) Утверждение 2.7a выполняется |
assert always (high_pri_req -> (2.7a) next_event_e(gnt)[1:2](high_pri_ack)); |
Рис. 2.7: next_event e[i:j]
|
Оператор until
предоставляет другой путь движения вперед, здесь накладываются требования на циклы, в которые мы движемся. Например, утверждение 2.8а показывает, что когда бы сигнал req
не был утвержден, далее, начиная со следующего цикла, сигнал busy
должен быть утвержден до тех пор, пока не утвердиться сигнал done
. Утверждение 2.8а требует, чтобы busy
был утвержден до, но необязательно включая, цикла, в котором утверждается done
. Для включения этого цикла, используется оператор until_
. Подчеркивание (_
) служит для того, чтобы указать дополнительный цикл, для которого мы требуем, чтобы busy
оставался утвержденным, таким образом утверждение 2.8b показывает, что кода бы сигнал req
не утверждался, далее, начиная со следующего цикла, busy
должен быть утвержден, и должен оставаться в таком состоянии до цикла, в котором утверждается done
, включительно. Например, утверждение 2.8а выполняется на тракте 2.8(i), а утверждение 2.8b нет, потому что busy
не утверждается в циклах 4 и 10. Оба утверждения 2.8a и 2.8b выполняются на тракте 2.8(ii): утверждение 2.8a не запрещает утверждения сигнала busy
в циклах 4 и 10 - это просто не требуется.
Если сигнал done
утверждается на следующем цикле после утверждения req
, утверждение 2.8a не требует, чтобы сигнал busy
, в принципе, утверждался, в отличие от утверждения 2.8b. То есть, утверждение 2.8а выполняется на тракте 2.8(iii) - факт того, что done
утверждается немедленно после утверждения req
, не оставляет циклов для утверждения busy
. Утверждение 2.8b не выполняется в тракте 2.8(iii), из-за пропущенного утверждения busy
в цикле, в котором утверждается done
.
![]() |
---|
assert always (req -> next (busy until done)); (2.8a) assert always (req -> next(busy until_ done)); (2.8b) |
Рис. 2.8: Операторы until и until_
|
Семейство операторов before
предоставляет легкий путь для показания требований, чтобы некоторый сигнал утверждался перед каким-либо другим сигналом. Например, предположим, что у нас есть импульсный сигнал req
, и мы имеем требование, что перед тем, как мы сможем сделать второй запрос, первый должен быть определен. Мы можем выразить это в PSL используя утверждение 2.9а. Нам нужен оператор next
для передвижения на один цикл вперед, таким образом req
в (ack before req
) точно будет ссылаться на другие req
, а не на тот, который был только что. Чтобы это понять, исследуем недостаточную версию этой спецификации. Рассматривая утверждение 2.9b. Оно требует, чтобы (ack before req
) выполнялось на каждом цикле, в котором выполняется req
. Например, цикл 1 тракта 2.9(i). Сигнал req
утверждается. Поэтому, (ack before req
) должен утвердиться в цикле 1. Однако, этого не происходит, потому что, начав с первого цикла и следуя далее, изначально мы увидим утверждения сигнала req
(в цикле 1), и только после этого утверждение сигнала ack
(в цикле 3) - таким образом req
утверждается перед ack
, а не наоборот. Утверждение 2.9а, с другой стороны, показывает, что мы хотим: в цикле 1, например, мы требуем выполнения next
(ack before req
). Поэтому, мы требуем, чтобы (ack before req
) выполнлся на цикле 2. Начиная с цикла 2 и двигаясь вперед, изначально мы увидим утверждение ack
(на цикле 3), и только после этого утверждение req
(в цикле 6).
Оператор before
требует, чтобы первый операнд выполнялся строго перед вторым. Для того, чтобы показать, что что-то должно выполниться перед или в том же цикле, как и что-либо иное, используется оператор before_
. Подчеркивание (_
) предназначено для представления цикла, в котором мы допускаем прекрытие между левой и правой сторонами. Например, для того чтобы показать, что поведениее соответствует тем, которые показаны в тракте 2.9(i) и в тракте Trace 2.9(ii), используется утверждение 2.9с.
Что если утверждению ack
позволенно выполниться не вместе со следующим утверждением req
, но вместе с установленным запросом? Другими словами, что если в дополнение к поведению, показаному в тракте 2.9(i), мы хотим допустить поведение показанное в тракте 2.9(iii)? Как было отмечно ранее, утверждение 2.9b это не ответ. Скорее, мы напишем код утверждения 2.9d.
2.5 Оператор eventually!
Оператор eventually!
позволяет показать, что что-либо должно произойти в будущем, без конкретного уточнения когда. Например, утверждение 2.10а показывает, что каждый запрос (утверждение req
) должен следовать в то же время, что и установленный (assertion of ack
). Нету ничего в утверждении 2.10а, чтобы предотвратить единичного установления из удовлетворяющих требования для множественных запросов, таким образом утверждение 2.10а выполняется на тракте 2.10(i). Мы проверили этот вопрос спецификации взаимно однозначных соответствий между сигналами в разделе 13.4.2.
Восклицательный знак (!
) в операторе eventually!
показывает, что это сильный оператор. Обсуждения сильных и слабых оператров пойдет в главе 4.
![]() |
---|
(i) Утверждение 2.10a выполняется |
assert always (req -> eventually! ack); (2.10a) |
Рис. 2.10: Оператор eventually!
|