пятница, 26 ноября 2010 г.

Зачем нужны clocking-блоки в SystemVerilog?

Если в Verilog взаимодействие между блоками описывается с помощью портов модуля, то в Systemverilog добавились интерфейсы. Эта конструкция позволяет объединить данные о реализации взаимодействие между блоками в отдельном interface-модуле (другими словами, инкапсулировать данные о взаимодействии блоков). При использовании интерфейсов можно с легкостью менять уровень абстракции для моделирования межмодульного взаимодействия.
Интерфейс описывает сигналы, с помощью которых testbench взаимодействует с тестируемым устройством (DUT). Однако, интерфейсы явно не описывают временные ограничения и синхронизацию. Для этого в SystemVerilog добавлены clocking-блоки. Они отделяют все, что связано с временными параметрами и синхронизацией от других элементов testbench'а. С помощью clocking-блоков можно группировать сигналы, изменяющиеся по одному тактовому сигналу.

пятница, 19 ноября 2010 г.

В чем различие между типами данных wire, reg и logic?

    Для того, чтобы понять различие между wire, reg и logic, сначала рассмотрим какие в SystemVerilog есть типы данных и какие классы объектов. Сразу скажу, что это не одно и тоже.
    Тип данных определяет набор значений и набор операций над этими значениями.

bit | logic | reg | byte | shortint | int | longint | integer | time | byte | shortint | int | longint | integer | time | struct | union | enum | string | event и т.д. - все это типы данных.

reg и logic тута. А где wire? Попробуем разобрать, что такое классы объектов, может там найдется wire.
    Итак, в SV выделяют следующие классы объектов: литералы, параметры, константы, переменные, сигналы и атрибуты. Все они описаны в стандарте. Мы же рассмотрим поподробнее классы объектов "переменные" (variables) и "сигналы" (nets).
    К классу "сигналы" относятся -

supply0 | supply1 | tri | triand | trior | trireg | tri0 | tri1 | uwire | wire | wand | wor.

    Вот и нашелся wire. Объекты класса "сигналы" по умолчанию относятся к типу данных logic и поэтому:

wire a;

и

wire logic a;

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

logic a;

или с добавлением ключевого слова var:

var logic a;

Это эквивалентные записи. При использовании var тип данных можно не указывать, тогда по умолчание тип данных будет logic.

wire

  1. Wire используется для соединения различных элементов
  2. Wire представляет собой физический провод (цепь)
  3. Wire можно считывать или присваивать
  4. Wire не может хранить значение
  5. Для определения состояния сигнала в цепи, у нее должен быть источник сигнала (драйвер), который будет непрерывно управлять цепью
  6. Wire может иметь несколько драйверов. Что в этом случае происходит, читаем здесь
  7. Назначить драйвер для wire можно с помощью оператора непрерывного присваивания assign или через порт модуля
  8. При синтезе wire всегда воспринимается как провод

reg

  1. Reg в Verilog/SystemVerilog представляет элементы хранения данных (аналог переменной в языках программирования)
  2. Reg не всегда представляет физический регистр. При синтезе может получиться регистр (flip-flop), регистр-защелка (latch) или комбинационная логика (еще есть вариант, когда может вообще ничего не получиться - это называется несинтезируемая конструкция).

logic

  1. Ключевое слово logic было добавлено в стандарт, чтобы разработчики больше не думали, что используя переменную reg, они получат физический регистр. logic - это эквивалент reg.
  2. Если к переменной типа logic одновременно осуществляется несколько присваиваний, то выбирается последнее.
  3. Присваивание переменной типа logic можно осуществить с помощью оператора непрерывного присваивания assign (тогда она воспринимается, как wire), через порт модуля или в процедурном блоке:

logic sum;
assign sum = a ^ b; // как wire
always_comb sum = a + b; // как reg
adder adder_inst(.out(sum), .op1(a), op2(b)); // как wire

Дополнительная информация по logic в статье.

четверг, 18 ноября 2010 г.

Что такое Factory Pattern в SystemVerilog?

    О том, что такое паттерн, уже писалось здесь. Вкратце напомню, что паттерн в ООП - это устоявшееся решение проблем, которые возникали, возникают и будут возникать. У некоторых из этих решений есть свое название. Что же такое factory pattern (или фабричный паттерн)? Сперва обратимся к определению фабричного метода из wiki:

Фабричный метод (англ. Factory Method) — порождающий шаблон проектирования, предоставляющий подклассам интерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какой класс инстанциировать. Иными словами, Фабрика делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами на более высоком уровне. Также известен под названием виртуальный конструктор.

    Фабричный паттерн нацелен на решение проблемы создания объекта. Есть еще набор паттернов, которые имеют отношение к созданию объекта, предназначенные для различных случаев. Они известны под названием creational patterns (порождающие паттерны).
    Рассмотрим пример использования порождающего паттерна на SystemVerilog. Создадим класс для "Фабрики роботов", который необходим, чтобы создавать разные типы роботов (Андроид (android), Боевой робот (robokiller), Бытовой робот (robomama), Персональный робот (robocomp)). Тип робота будет выбираться с помощью входной строки.
    Для каждого типа робота нам потребуется свой класс. Создадим базовый класс ROBO, который будет содержать общие для всех классов переменные, подзадачи task, функции function. Остальные классы будут наследовать базовый класс:

class ROBO;
    // Общие объекты данных
string type;

// Общие методы
virtual function string get_type();
endclass : ROBO

class ANDROID extends ROBO;
function new();
    this.string = "Android";
endfunction : new

string function string get_type();
    return this.string;
endfunction : get_type
endclass : ANDROID

class ROBOKILLER extends ROBO;
function new();
    this.string = "ROBOKILLER";
endfunction : new

string function string get_type();
    return this.string;
endfunction : get_type
endclass : ROBOKILLER

    Теперь опишем класс "фабрики роботов" (ROBO_FACTORY). Для простоты пронумеруем каждый тип робота, чтобы было легче выбирать, какого робота создавать.

class ROBO_FACTORY;
ROBO my_robo

// Общие методы
function ROBO get_robo(int type);
    if(type == 1) this.my_robo = new ANDROID();
    if(type == 2) this.my_robo = new ROBOKILLER();
    return this.my_robo;
endfunction : get_robo
endclass : ROBO_FACTORY

    Дополнительно читайте здесь.

вторник, 16 ноября 2010 г.

Что такое Callback в SystemVerilog?

 Начнем с определения из википедии:

Callback (англ. call — вызов, англ. back — обратный) или фу́нкция обра́тного вы́зова в программировании — передача исполняемого кода в качестве одного из параметров другого кода.

    Например, вам необходимо передать одну функцию в качестве входного параметра другой функции. Это и есть callback.

    Как это реализуется с помощью средств ООП в SystemVerilog? Рассмотрим пример:
Есть базовый класс my_transactor, который принимает транзакции от драйвера и пересылает их в монитор:

class my_transactor;
    ...
    task main();
        ...
        forever begin
            ...
            // Достаем из почтового ящика транзакцию,
            // которую прислал драйвер
            driver2xactor.get(in_tr);
            // Выполняем необходимые действия
            ...
            // Отправляем транзакцию в блок монитора
            xactor2monitor.put(out_tr);
            ...
        end
    endtask
endclass

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

class my_transactor;
    ...
    task main();
        ...
        forever begin
            ...
            // Достаем из почтового ящика транзакцию,
            // которую прислал драйвер
            driver2xactor.get(in_tr);
            $display(" Input trans: a = %h, b = %h", in_tr.a, in_tr.b);
            // Выполняем необходимые действия
            ...
            // Отправляем транзакцию в блок монитора
            $display(" Output trans: a = %h, b = %h", out_tr.a, out_tr.b);
            xactor2monitor.put(out_tr);
            ...
        end
    endtask
endclass

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

class my_transactor;
    virtual task pre_exec(in_trans tr);
    endtask
    virtual task post_exec(out_trans tr);
    endtask
    ...
    task main();
        ...
        forever begin
            ...
            // Достаем из почтового ящика транзакцию,
            // которую прислал драйвер
            driver2xactor.get(in_tr);
            this.pre_exec(in_tr);
            // Выполняем необходимые действия
            ...
            // Отправляем транзакцию в блок монитора
            this.post_exec(out_tr);
            xactor2monitor.put(out_tr);
            ...
        end
    endtask
endclass

class my_transactor_child extend my_transactor;
    virtual task pre_exec(in_trans tr);
        $display(" Input trans: a = %h, b = %h", tr.a, tr.b);
    endtask
    virtual task post_exec(out_trans tr);
        $display(" Output trans: a = %h, b = %h", tr.a, tr.b);
    endtask
    ...
endclass

    Как это работает? В базовом классе my_transactor есть 3 подзадачи task, 2 из которых виртуальные и не реализованы. Класс-потомок my_transactor_child наследует методы базового класса и может и заполняет виртуальные методы в соответствии со своими нуждами. Тем самым мы можем вносить изменения в метод main, не изменяя его код.
    Callback методы содержат фрагменты кода, функции и подзадачи, которые меняются от теста к тесту. Они очень полезны, когда идет речь о создании базового класса, который планируется использовать в нескольких тестовых окружениях.
    У приведенного выше примера callback есть некоторые ограничения, которые можно решить с помощью паттернов. Паттерны в ООП это не базовые классы или библиотеки. Это некий шаблон проектирования или техника для построения объектно-ориентированного кода, которая позволяет решить сложные задачи. Подробнее об этом в статье - Janick Bergeron "How to use VMM callbacks"