Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.5. Статические переменные блоков

§2.5.2. Особенности использования сигналов

# Руководство программиста

## Глава 2. Создание моделей блоков

### §2.5. Статические переменные блоков

#### §2.5.2. Особенности использования сигналов

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

Рис. 17. Подсчет числа нажатий кнопки

Во многих случаях модели блока бывает нужно не передать другим блокам по связям какое-либо конкретное значение, а уведомить их о том, что произошло какое-то событие, на которое эти блоки должны отреагировать согласно логике работы своих моделей. Допустим, например, что нам для каких-то целей нужно подсчитывать число нажатий кнопки пользователем. Мы можем для этого взять из библиотеки RDS стандартные блоки: кнопку, счетчик и индикатор, и соединить их, как показано на рис. 17. Если бы таких стандартных блоков в библиотеке не было, нам пришлось бы создавать модели кнопки и счетчика самостоятельно. Рассмотрим возможные варианты решения этой задачи.

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

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

Во избежание описанных выше проблем, в RDS введен сигнальный тип переменных, специально предназначенный для передачи по связям информации о событиях. Как и логические переменные, сигнальные могут принимать значения 0 и 1, но передаются от блока к блоку они совсем по-другому. Во-первых, по связи передается только единичное значение сигнального выхода, при его нулевом значении связь не срабатывает. Фактически, передается только передний фронт сигнала: при изменении значения выхода с 0 на 1 во все входы, соединенные с этим выходом, запишется значение 1. Во-вторых, после передачи единичного значения сигнальному выходу автоматически присваивается ноль. Таким образом, отпадает необходимость в дополнительном такте расчета для обнуления значения выхода: выход обнуляется автоматически в конце такта, и в следующем же такте блок будет готов передать информацию о новом событии. Кроме того, в блоках, к входам которых подключена сигнальная связь, нет необходимости создавать дополнительные переменные для отслеживания факта изменения входа. Вместо этого при обнаружении на сигнальном входе единицы им достаточно сбросить его в ноль: поскольку выход соединенного блока будет сброшен автоматически, а нулевое значение сигнального выхода по связи не передается, единица на входе появится только при следующем событии. Следует учитывать, что если модель блока с сигнальным входом не сбросит этот вход после реакции на событие, то в следующем такте расчета, обнаружив единицу на входе, она может ошибочно посчитать, что произошло еще одно событие. По этой причине при написании моделей крайне важно следить за сбросом сигнальных входов. Однако, в некоторых случаях, единицу, не исчезающую автоматически с сигнального входа после срабатывания связи, модель может использовать в своих целях. Например, если модель выполняет какие-то действия по таймеру раз в несколько секунд, она может не беспокоиться о том, что пропустит какое-то событие: единица, появившаяся на ее входе в результате срабатывания связи, так на нем и останется до следующего вызова модели по таймеру. Конечно, в этом случае модель не сможет узнать, сколько именно событий произошло между ее вызовами, но, во многих случаях, это и не нужно. Если же число событий важно, всегда можно переписать модель так, чтобы она, как положено, сбрасывала сигнальный вход в каждом такте расчета и увеличивала какой-нибудь внутренний счетчик.

Описанные принципы передачи сигнальных переменных по связям можно проиллюстрировать условной временной диаграммой, приведенной на рис. 18. Пусть в схеме есть три блока: «Блок 1» с сигнальным выходом y, «Блок 2» с сигнальным входом x1 и «Блок 3» с сигнальным входом x2, причем выход первого блока соединен с входами второго и третьего.

Рис. 18. Временная диаграмма передачи переменных сигнального типа
на примере трех соединенных блоков

На рисунке изображены три такта расчета, каждый из которых разделен на две части: полутакт срабатывания моделей блоков и следующий за ним полутакт срабатывания связей. Пусть в первом такте модель первого блока присвоила сигнальному выходу y значение 1 (на рисунке этот момент обозначен буквой «а»), информируя соединенные блоки о каком-то событии. В конце этого такта, при срабатывании связей, эта единица будет передана на входы x1 и x2 второго и третьего блоков соответственно («б»), а значение выхода y будет автоматически обнулено («в»).

Во втором такте расчета модель второго блока обнаружит единицу на входе x1, выполнит связанные с данным событием действия, определяемые логикой модели, и обнулит вход, подготавливая его к приему информации о следующем событии («г»). Модель третьего блока в это же время по какой-то причине (по своей внутренней логике или из-за ошибки программиста) не обнулит вход x2, и в нем так и останется единичное значение («д»). В это же время модель первого блока снова присвоит выходу y единицу, чтобы сообщить соединенным блокам о втором событии («е»). Когда в конце второго такта расчета начнут срабатывать связи, эта единица снова будет передана на входы первого и второго блоков («ж»), однако, поскольку значение x2 не было сброшено моделью второго блока, там оставалась единица, полученная им в прошлом такте расчета – таким образом, значение x2 не изменится. После передачи выходу первого блока снова будет автоматически присвоено нулевое значение («з»).

В начале третьего такта расчета выход y имеет нулевое значение, а входы x1 и x2 – единичные. В этом такте первый блок не будет информировать остальные о новом событии, его выход y так и останется нулевым и не будет передаваться по связям. Модель второго блока, отреагировав на событие, информация о котором передана ей по связи в конце второго такта, снова сбросит свой вход x1 («и») и будет ждать нового события. Значение же x2, не сброшенное моделью третьего блока, так и останется единичным, поскольку нулевое значение сигнального выхода y, соединенного с ним, в конце третьего такта не будет передано по связи.

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

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

Обязательные для каждого простого блока переменные Start и Ready тоже являются сигналами. При задании набора переменных блока (см. рис. 9) можно изменить только их имена и начальные значения, при этом первая переменная блока всегда останется сигнальным входом, а вторая – сигнальным выходом. Эти сигналы управляют работой модели и ее выходных связей в режиме расчета: единица на входе Start запускает модель блока, единица на выходе Ready разрешает передачу данных выходов. Модель может работать с этими переменными как с обычными сигналами, однако следует помнить, что RDS автоматически сбрасывает сигнал Start и взводит сигнал Ready перед запуском модели для выполнения такта моделирования, то есть в режиме RDS_BFM_MODEL. Если модели необходимо запуститься в следующем такте независимо от срабатывания связей, присоединенных к ее входам, и состояния флага блока «запуск каждый такт», она может самостоятельно взвести сигнал Start. Модель также может запретить передачу данных своих выходов по связям, сбросив сигнал Ready.

Для примера рассмотрим модель блока-счетчика, который по сигналу изменяет значение выхода от 0 до 9, после чего выдает на выход сигнал переноса и начинает счет с нуля. Блок будет иметь сигнальные входы Clk и Reset, целый выход Count и сигнальный выход Carry. Блок будет работать следующим образом: по сигналу, приходящему на вход Clk, блок будет увеличивать значение выхода Count на 1 до тех пор, пока оно не достигнет девяти. При следующем срабатывании Clk модель обнулит выход Count и выдаст сигнал переноса на выход Carry. По сигналу Reset модель должна обнулить оба выхода. На рис. 19 изображены два таких счетчика, соединенные последовательно.

Рис. 19. Два последовательно соединенных счетчика

К входу Clk первого счетчика подключена кнопка «+1», позволяющая увеличивать значение его выхода на единицу. Его выход Carry подключен к входу Clk второго счетчика – в результате этого, когда первый счетчик досчитает до десяти и его выход обнулится, второй увеличит свой выход на единицу. Кнопка «Сброс» подключена к входам Reset обоих счетчиков, ее нажатие сбрасывает их одновременно. При таком соединении выходы первого и второго счетчиков можно считать двумя разрядами двузначного десятичного числа, таким образом, вместе они могут досчитать до ста.

Структура переменных такого блока-счетчика будет выглядеть следующим образом:

Смещение | Имя | Тип | Размер | Вход/выход
--- | --- | --- | --- | ---
0 | Start | Сигнал | 1 | Вход
1 | Ready | Сигнал | 1 | Выход
2 | Reset | Сигнал | 1 | Вход
3 | Clk | Сигнал | 1 | Вход
4 | Count | int | 4 | Выход
8 | Carry | Сигнал | 1 | Выход

Модель блока будет такой:

      extern "C" __declspec(dllexport)
        int RDSCALL TestCounter(int CallMode,
          RDS_PBLOCKDATA BlockData,
          LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))                         // 0
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))               // 1
      #define Reset (*((char *)(pStart+2*RDS_VSZ_S)))             // 2
      #define Clk (*((char *)(pStart+3*RDS_VSZ_S)))               // 3
      #define Count (*((RDSINT32 *)(pStart+4*RDS_VSZ_S)))         // 4
      #define Carry (*((char *)(pStart+4*RDS_VSZ_S+RDS_VSZ_I)))   // 5
        switch(CallMode)
          { // Проверка типа переменных
            case RDS_BFM_VARCHECK:
              if(strcmp((char*)ExtParam,"{SSSSIS}")==0)
                return RDS_BFR_DONE;
              return RDS_BFR_BADVARSMSG;

            // Выполнение такта моделирования
            case RDS_BFM_MODEL:
              if(Reset) // Поступил сигнал Reset
                { Reset=0;  // Сброс этого сигнала
                  Clk=0;    // Сброс сигнала Clk
                  Count=0;  // Обнуление счетчика
                  Carry=0;  // Сброс сигнала переноса
                }
              else if(Clk) // Поступил сигнал Clk
                { Clk=0;    // Сброс этого сигнала
                  Count++;  // Увеличение счетчика
                  if(Count>=10)  // Досчитали до 10
                    { Count=0;      // Обнуление счетчика
                      Carry=1;      // Выдача сигнала переноса
                    }
                }
              break;
          }

        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef Carry
      #undef Count
      #undef Clk
      #undef Reset
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================

При вызове этой модели с параметром RDS_BFM_VARCHECK производится сравнение переданной строки типа переменных блока со строкой «{SSSSIS}». Все переменные блока – сигналы («S»), кроме целой переменной Count, которой соответствует символ «I». Сравнив строки, модель возвращает RDS соответствующую константу – RDS_BFR_DONE, если структура переменных блока правильная, и RDS_BFR_BADVARSMSG в противном случае.

При вызове модели с параметром RDS_BFM_MODEL для выполнения такта расчета сначала проверяется, не поступил ли на вход блока сигнал сброса Reset. Этот сигнал считается более важным, поэтому его проверка производится до проверки сигнала Clk. Если в одном такте на вход блока придут оба сигнала, Reset будет иметь приоритет. Если Reset имеет значение 1, он обнуляется, чтобы модель могла среагировать на него еще раз (иначе единица останется на этом входе навсегда). Сигнал Clk также сбрасывается – если он поступил одновременно с Reset, его нужно игнорировать. Затем обнуляется счетчик Count и сбрасывается сигнал переноса Carry. Сброс сигнала переноса может показаться излишним, поскольку выходные сигналы автоматически сбрасываются при передаче по связям. Однако, если к выходу Carry не было подключено ни одной связи, его значение не будет никуда передаваться, и, следовательно, сбрасываться он также не будет. Если пользователь остановит расчет и соединит сигнал Carry с другими блоками, сохранившаяся в нем единица будет немедленно передана на их входы при повторном запуске расчета, даже если на вход данного блока поступит сигнал Reset. Поэтому лучше подстраховаться и принудительно обнулить Carry при поступлении сигнала сброса.

Если сигнал сброса не поступал (Reset имеет значение 0), модель проверяет значение сигнала Clk. Если он имеет ненулевое значение, он обнуляется, чтобы модель могла среагировать на него в дальнейшем, и значение счетчика Count увеличивается на 1. Если значение Count достигло 10, оно сбрасывается в 0 и взводится сигнал переноса Carry. Сбрасывать сигнал переноса не нужно – он либо сбросится автоматически, если его значение будет передано по связям, либо останется взведенным, запомнив факт переполнения счетчика, до тех пор, пока к нему не будет подключена какая-либо связь.

Для блока с этой моделью необходимо либо включить запуск каждый такт расчета, либо установить флаги «Пуск» (см. рис. 9) для входов Reset и Clk. Если этого не сделать, модель будет запускаться только при срабатывании связи, соединенной со входом Start, что для данного блока не имеет никакого смысла. У блока есть два собственных управляющих сигнала, поэтому его модель должна либо запускаться при их срабатывании (если для них установлены флаги «Пуск»), либо проверять их значения в каждом такте расчета.

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

          // Выполнение такта моделирования
          case RDS_BFM_MODEL:
            if(Reset)     // Поступил сигнал Reset
              { Reset=0;  // Сброс этого сигнала
                Count=0;  // Обнуление счетчика
                Carry=0;  // Сброс сигнала переноса
                // Сигнал Clk теперь не сбрасывается
              }
            // "else if(Clk)" заменено на "if(Clk)"
            if(Clk)  // Поступил сигнал Clk
              { Clk=0;    // Сброс этого сигнала
                Count++;  // Увеличение счетчика
                if(Count>=10) // Досчитали до 10
                  { Count=0;  // Обнуление счетчика
                    Carry=1;  // Выдача сигнала переноса
                  }
              }
            break;

Как и в предыдущем варианте, модель сначала проверяет значение сигнала Reset и, если оно равно единице, обнуляет счетчик. Однако, если раньше модель в этом случае сбрасывала Clk и завершалась, теперь она проверяет значение Clk и увеличивает счетчик независимо от состояния Reset. Теперь при одновременном поступлении сигналов значение Count будет равно 1, а не 0, поскольку сначала оно будет сброшено из-за сигнала Reset, а потом увеличено на единицу из-за сигнала Clk.

Для проверки работы созданной модели можно собрать схему с рис. 19. Если запустить расчет и нажимать на кнопку «+1», каждое нажатие должно приводить к увеличению числа на выходе первого счетчика – оно отображается на верхнем индикаторе. Когда счетчик досчитает до девяти, следующее нажатие на кнопку «+1» должно привести к обнулению выхода первого счетчика и увеличению выхода второго (нижний индикатор). Нажатие кнопки «Сброс» должно приводить к обнулению выходов обоих счетчиков.