Журнал фильтра правок

Фильтры правок (обсуждение) — это автоматизированный механизм проверок правок участников.
(Список | Последние изменения фильтров | Изучение правок | Журнал срабатываний)
Перейти к навигации Перейти к поиску
Подробности записи журнала 1 962 284

11:30, 16 ноября 2015: 99 «Кусок текста» Ahlininv (обсуждение | вклад) на странице MyHDL, меры: Предупреждение (просмотреть)

Изменения, сделанные в правке

== Юнит-тестирование ==

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

Есть подход к программированию, который стал популярен
относительно недавно. Он называется «Экстремальное программирование» (''Extreme Programming,
ХР). Это
впечатляющий набор способов и руководств, который идет против традиционных
подходов к разработке. В других случаях экстремальное программирование будто бы
подчеркивает здравый смысл, который не всегда присутствует в стандартных
методах. Например, экстремальное программирование делает упор на рабочую
неделю, что позволяет иметь всегда свежую голову. Это необходимо для
качественной разработки.''

''Эта
статья не является попыткой написать учебник по экстремальному
программированию. Наоборот, в этой статье я подчеркну лишь одну черту
экстремального программирования, которая необходима в аппаратной разработке:
важность юнит-тестирования и его методики.''

=== ''Важность юнит-тестирования'' ===
''Юнит-тестирование
– это один из краеугольных камней экстремального программирования. Другие идеи
этой технологии, такие как коллективное владение кодом и непрерывное повышение
качества возможны тогда и только тогда, когда реализовано юнит-тестирование.
Более того, экстремальное программирование подчеркивает, что написание
юнит-тестов должно быть автоматизировано, что должны быть протестированы все
элементы всех классов и они должны работать идеально всё время.''

''Я
верю, что эта концепция применима как раз в аппаратной разработке. К томуже,
юнит-тесты – это способ управлять временем симуляции. Например, верификация
конечного автомата, который очень медленно работает на редких событиях, может
быть затруднена или невозможна с учетом ограниченности времени, даже на самом
быстром симуляторе.''

''Понятно,
что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны,
если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда
будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно,
в теории экстремального программирования подчеркивается необходимость
стандартного юнит-тестирования, которое поддерживает все описанные задачи. В
этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для
аппаратных проектов.''

=== ''Разработка юнит-тестов.'' ===
''В
этой главе мы будем неформально изучать применение юнит-тестирования к
аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем
тестировать устройство, переводящее двоичный код в код Грея. Это устройство
было описано в разделе Bit indexing.''

==== Определение требований ====
Мы начинаем с определения требований. Для кодировщика в код
Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея.
Давайте определим код как список кодовых слов, где кодовое слово – это строка
из битов. Код длины n
может определять 2**n слов.

Широко известные характеристики – это то, для чего
предназначен код Грея:

Последовательные кодовые слова в коде Грея должны отличаться
ровно в одном бите.

Этого достаточно? Не совсем. Например, под наши нынешние
требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это,
очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода
Грея превосходила длину битового слова.

Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно
одному слову в коде Грея такого же порядка.

Когда написаны требования, мы можем продолжать.

Сначала – тесты

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

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

Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий
юнит-тесты. С unittest
тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как
методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются
модулем как тесты из комплекта тестов.

Мы зададим тесты для требований кода Грея, после чего
напишем тесты для каждого из требований. Итогом создания комплекта тестов таков:

'''from''' unittest '''import''' TestCase

'''class''' '''TestGrayCodeProperties'''(TestCase):

'''def''' '''testSingleBitChange'''(self):

""" Check that only one bit changes in successive codewords
"""

'''....'''

'''def''' '''testUniqueCodeWords'''(self):

""" Check that all codewords occur exactly once
"""

'''....'''

Каждый метод будет небольшим тестом, который проверяет
заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого
кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую
реализацию, включающую только интерфейс:
'''def''' '''bin2gray'''(B, G, width):

    ''### NOT IMPLEMENTED YET! ###''

    '''yield''' None
Для первого требования мы напишем тест, который подает на
вход последовательно все цифры, а потом сравнивает между собой выходные данные
кодировщика и проверяет, что действительно последовательные слова отличаются на
один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного
порядка MAX_WIDTH.
'''def''' '''testSingleBitChange'''(self):

    """ Check that only one bit changes in successive codewords """

    '''def''' '''test'''(B, G, width):

        B'''.'''next '''=''' intbv(0)

        '''yield''' delay(10)

        '''for''' i '''in''' range(1, 2'''**'''width):

            G_Z'''.'''next '''=''' G

            B'''.'''next '''=''' intbv(i)

            '''yield''' delay(10)

            diffcode '''=''' bin(G '''^''' G_Z)

            self'''.'''assertEqual(diffcode'''.'''count('1'), 1)

    '''for''' width '''in''' range(1, MAX_WIDTH):

        B '''=''' Signal(intbv('''-'''1))

        G '''=''' Signal(intbv(0))

        G_Z '''=''' Signal(intbv(0))

        dut '''=''' bin2gray(B, G, width)

        check '''=''' test(B, G, width)

        sim '''=''' Simulation(dut, check)

        sim.run(quiet=1)

Обратите внимание на то, как проверка равенства
осуществляется с помощью метода self.assertEqual ,
определенного в unittest.TestCase.

Точно так же мы пишем тест для проверки второго требования.
Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из
кодировщика, сортируем их и проверяем совпадение с начальным набором.
'''def''' '''testUniqueCodeWords'''(self):

    """ Check that all codewords occur exactly once """

    '''def''' '''test'''(B, G, width):

        actual '''=''' []

        '''for''' i '''in''' range(2'''**'''width):

            B'''.'''next '''=''' intbv(i)

            '''yield''' delay(10)

            actual'''.'''append(int(G))

        actual'''.'''sort()

        expected '''=''' range(2'''**'''width)

        self'''.'''assertEqual(actual, expected)

    '''for''' width '''in''' range(1, MAX_WIDTH):

        B '''=''' Signal(intbv('''-'''1))

        G '''=''' Signal(intbv(0))

        dut '''=''' bin2gray(B, G, width)

        check '''=''' test(B, G, width)

        sim '''=''' Simulation(dut, check)

        sim'''.'''run(quiet'''='''1)

Разработка от тестирования

Когда написаны тесты, мы начинаем реализацию. Для
демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и
посмотрим, как тесты будут себя вести.
unittest'''.'''main()
Давайте запустим этот тест, используя неработающий, еще
пустой объект кодировщика, показанный выше:
% python test_gray.py -v

Check that only one bit changes in successive codewords ... FAIL

Check that all codewords occur exactly once ... FAIL

<trace backs not shown>

Как и ожидалось, он полностью проваливается. Теперь
попытаемся протестировать неправильную реализацию, которая выдает последний бит
числа (то есть удовлетворяет одному условию):
'''def''' '''bin2gray'''(B, G, width):

    ''### INCORRECT - DEMO PURPOSE ONLY! ###''

    @always_comb

    '''def''' '''logic'''():

        G'''.'''next '''=''' B[0]

    '''return''' logic
Запуск тестов дает нам:
% python test_gray.py -v

Check that only one bit changes in successive codewords ... ok

Check that all codewords occur exactly once ... FAIL

======================================================================

FAIL: Check that all codewords occur exactly once

----------------------------------------------------------------------

Traceback (most recent call last):

  File "test_gray.py", line 109, in testUniqueCodeWords

    sim.run(quiet=1)

...

  File "test_gray.py", line 104, in test

    self.assertEqual(actual, expected)

  File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual

    raise self.failureException, \

AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3]

----------------------------------------------------------------------

Ran 2 tests in 0.785s

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

В конце концов, если мы тестируем правильную версию bin2gray:
'''def''' '''bin2gray'''(B, G, width):

    """ Gray encoder.

    B -- input intbv signal, binary encoded

    G -- output intbv signal, gray encoded

    width -- bit width

    """

    @always_comb

    '''def''' '''logic'''():

        '''for''' i '''in''' range(width):

            G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i]

    '''return''' logic
Вывод получается такой:
% python test_gray.py -v

Check that only one bit changes in successive codewords ... ok

Check that all codewords occur exactly once ... ok

----------------------------------------------------------------------

Ran 2 tests in 6.364s

OK

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

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

Мы обозначим код Грея длины n за Ln. Примеры:
L1 = ['0', '1']

L2 = ['00', '01', '11', '10']

L3 = ['000', '001', '011', '010', '110', '111', '101', 100']
Можно определить эти коды рекурсивным алгоритмом, например
вот так:

1.    L1 = [‘0’, ‘1’]

2.   
Ln+1 может быть получен
из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому
кодовому слову Ln  ‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1.

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

'''def''' '''nextLn'''(Ln):

    """
Return Gray code Ln+1, given Ln. """

Ln0 '''=''' ['0' '''+''' codeword '''for''' codeword '''in''' Ln]

Ln1 '''=''' ['1' '''+''' codeword '''for''' codeword '''in''' Ln]

Ln1'''.'''reverse()

'''return'''
Ln0 '''+''' Ln1

Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков,
создаваемых с помощью цикла с простыми действиями.

Сейчас требования состоят в том, что код
точно соответствует Ln. Мы используем
функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так:

'''class''' '''TestOriginalGrayCode'''(TestCase):

'''def''' '''testOriginalGrayCode'''(self):

""" Check that the code is an original Gray code
"""

Rn '''=''' []

'''def'''
'''stimulus'''(B, G, n):

'''for'''
i '''in''' range(2'''**'''n):

B'''.'''next '''=''' intbv(i)

'''yield'''
delay(10)

Rn'''.'''append(bin(G, width'''='''n))

Ln '''=''' ['0', '1'] ''# n == 1''

'''for'''
n '''in''' range(2, MAX_WIDTH):

Ln '''=''' nextLn(Ln)

'''del'''
Rn[:]

B '''=''' Signal(intbv('''-'''1))

G '''=''' Signal(intbv(0))

dut '''=''' bin2gray(B, G, n)

stim '''=''' stimulus(B, G, n)

sim '''=''' Simulation(dut, stim)

sim'''.'''run(quiet'''='''1)

self'''.'''assertEqual(Ln, Rn)

Если так сделать, то реализованное нами
устройство выдает действительно настоящий код Грея.

%python test_gray.py -v TestOriginalGrayCode

Check that the code is an original Gray code ... ok

<nowiki>----------------------------------------------------------------------</nowiki>

Ran 1 tests in 3.091s

OK

== Одновременная с верилогом симуляция, Сосимуляция(Co-simulation) ==

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

В наши дни известно, что язык,
предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать
современные технологии прораммирования, такие, как обьектно-ориентированность.
Все потому, что верификация – это наиболее сложная и затратная по времени часть
разработки аппаратного обеспечения. Следовательно, ценна любая новая
технология. Более того, тесты не обязательно должны быть имплементируемыми
(реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода,
они не имеют ограничений в реализации.

Технически верификация аппаратного
обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В
данный момент MyHDL включает PLI-модуль для двух
симуляторов: Icarus и Cver.

=== На стороне HDL ===
Чтобы продемонстрировать сосимуляцию мы будем использовать
снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать
его, написав для этого на Verilog.
Конечно же, мы захотим снова использовать те же тесты, что были для него уже
написаны.

Для начала давайте освежим в памяти, как выглядит кодировщик
на MyHDL:

'''def''' '''bin2gray'''(B, G, width):

""" Gray encoder.

B -- input
intbv signal, binary encoded

G -- output
intbv signal, gray encoded

width -- bit
width

"""

@always_comb

'''def''' '''logic'''():

'''for'''
i '''in''' range(width):

G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i]

'''return''' logic

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

module bin2gray(B, G);

   parameter width = 8;

   input
[width-1:0]  B;

output
[width-1:0] G;

....

Чтобы написать тесты кто-то создает новый модуль, который
представляет собой Design Under Test
(DUT). Тестбенч
определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых
сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том
же HDL, но мы будем
писать их не на HDL, а на MyHDL.
чтобы соединить их с входами Verilog-модуля
нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции
управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере
этоделается так:

module dut_bin2gray;

   reg [`width-1:0] B;

   wire
[`width-1:0] G;

initial begin

$from_myhdl(B);

$to_myhdl(G);

end

bin2gray dut
(.B(B), .G(G));

defparam dut.width = `width;

endmodule

$from_myhdl говорит о том, что эти провода
принимают сигналы от MyHDL,
а $to_myhdl означает, что данные сигналы будут
считаны тестовым окружением MyHDL.
Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого
поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле
myhdl.vpi , который скомпилирован из кода, написанного
на С.

=== На стороне MyHDL ===
MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать,
как запускать симуляцию HDL.
следовательно, первый аргумент для его конструктора – это командная строка для
исполнения симуляции.

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

% iverilog -o bin2gray -Dwidth=4 bin2gray.v
dut_bin2gray.v

Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов.

Симуляция его запускается с помощью команды vvp:

% vvp -m ./myhdl.vpi
bin2gray

Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример
использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation)

Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы
пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так:

'''import''' os

'''from''' myhdl '''import''' Cosimulation

cmd '''=''' "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v"

'''def''' '''bin2gray'''(B, G, width):

os'''.'''system(cmd '''%''' width)

'''return'''
Cosimulation("vvp -m ./myhdl.vpi
bin2gray", B'''='''B, G'''='''G)

После аргумента-исполняемой команды Cosimulation принимает
все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти
имена указаны в $to_myhdl и
$from_myhdl. Аргументы этих функций – сигналы
из MyHDL.

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

Взглянем на этот Verilog-проект:

module bin2gray(B, G);

parameter
width = 8;

input
[width-1:0]  B;

output
[width-1:0] G;

reg
[width-1:0] G;

integer i;

wire
[width:0] extB;

assign extB =
{1'b0, B}; // zero-extend input

always
@(extB) begin

for (i=0;
i < width; i=i+1)

G[i]
<= extB[i+1] ^ extB[i];

end

endmodule

Если мы запустим тест, мы получим:

% python test_bin2gray.py

Check that only one bit changes in successive
codewords ... ok

Check that all codewords occur exactly once ... ok

Check that the code is an original Gray code ... ok

<nowiki>----------------------------------------------------------------------</nowiki>

Ran 3 tests in 2.729s

OK

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

По некоторым причинам невозможно добиться полной общности.
Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения
отдельного симулятора и различия между разными симуляторами могут быть
ошеломляющими. К тому же полная универсальность может требовать
непропорционального времени под разработку по сравнению с немного менее общим
решением, которого может быть достаточно для данной задачи.

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

Результатом компромисса было наложение на HDL нескольких
ограничений. В этой главе будет рассказано о них.

==== Только пассивный HDL может быть сосимулирован. ====
Наиболее важное ограничение MyHDL на
сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк
с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В
частности, сигнал clk
должен быть сгенерирован на стороне MyHDL.

Сначала это кажется важным ограничением, но если рассмотреть
реальные случаи, оно оказывается не очень важным.

MyHDL поддерживает сосимуляцию так, что тесты должны быть именно
на Python. Давайте
рассмотрим природу тестируемых HDL-проектов.
Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в
плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом.
Это и есть одна из важных задач MyHDL.
Точно так же прошиваемые проекты с заявленными временными ограничениями – не
то, что нас интересует: статический анализ тайминга – намного более хороший
способ для таких проектов.

Итог: HDL-описания,
которые нас интересуют – это реальные модели аппаратуры, которые будут
синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы
в синтезируемом коде не важны(нет строго заданного в проекте тайминга),
ограничение не влияет на выполнение наших задач.

==== 1.      Гонка ====
В обычном RTL
некоторые события иногда вызывают другие события на том же clk (на том же временном интервале).
Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика,
насколько я помню). This is done by the
concept of “delta” cycles. In a fully general, race free co-simulation, the
co-simulators would communicate at the level of delta cycles. However, in MyHDL
co-simulation, this is not entirely the case.

Дельта-циклы из MyHDL-симулятора в HDL-симулятор
запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются
в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе.

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

Другая ситуация наступает, когда
мы хотим использовать сигнал, который управляется из HDL (и
соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно
изменять сигналы в MyHDL со сдвигом по времени, избегая
таким образом возможность произвольного взаиморасположения положительного
фронта clk и события, избегая таким образом гонки.

ЕЩЕ ТУТ БЫЛО ЧТО-ТО.

== Трансляция в Verilog и VHDL ==

=== Введение ===
Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL.

Эта глава описывает основы этой
трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. <nowiki>http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage</nowiki>

=== Описание продукта ===
Чтобы быть конвертируемым, проект
должен удовлетворять некоторым ограничениям, которые мы будем называть
конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте
«Конвертируемое подмножество».

Конвертируемый проект может быть
сконвертирован в эквивалентную модель на Verilog или VHDL с помощью
функций toVerilog(), toVHDL() библиотеки
MyHDL.

Когда проект предназначается для
имплементации, синтез производит другое средство, предназначенное для
синтезирования из Verilog и VHDL
проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести
проект, написанный на Python, до уровня прошивки платы.

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

=== Особенности ===

==== Конверсия после обработки ====
Под обработкой в данном случае понимается изначальная
обработка проекта для достижения представления о устройстве, в том числе, может
ли оно быть сконвертировано, синтезировано и имплементировано. В частности,
структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для
обработки используется интерпретатор Питона. Объект Simulation строится
на основе своих аргументов - обработанных экземпляров. Таким же образом
конверсия работает с уже собранным экземпляром всего проекта. Таким образом,
интерпретатор Питона используется максимально активно.

==== Обработка структур любой сложности ====
Так как конверсия работает с уже обработанными экземплярами,
все условия(constraints)
моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами,
нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина
иерархии, естественно, тоже не ограничена.

==== Генераторы переводятся в Verilog и VHDL ====
Конвертор анализирует код каждого генератора и переводит его
в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки.
В VHDL они будут
переведены в process-блоки
одновременных присваиваний сигналов.

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

==== Интерфейсы конвертируемы ====
Интерфейс – это объект с несколькими сигналами и их
свойствами. Конвертер поддерживает их с помощью имен и mangling.

==== Вызов функций преобразуется в Verilog и VHDL-подпрограммы ====
Конвертер анализирует функцию и ее код. Каждая функция
переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы
поддерживать возможности функций в Питоне полностью, на каждый вызов функции в
Питоне генерируется отдельная подпрограмма

==== if-then-else-структуры могут быть переведены в case-блоки ====
Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции,
где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими
свойствами в случае синтеза (вероятно, мультиплексор – авт.)

==== Выбор способов перевода enum’ов – перечислимых типов ====
Функция enum()
в MyHDL возвращает
перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет
способ ее перевода в HDL:
бинарный, one hot,
или one cold.

==== Память с произвольным доступом RAM ====
Большинство средств синтеза могут переводить Verilog и
VHDL, определяющий
блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию,
конвертер преобразует list из Signal’ов
в Verilog-памяти или VHDL-массивы.

==== Память ROM ====
Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает
перевод в такие конструкции автоматически, основываясь на высокоуровневом
описании. ROM
описывается одной строчкой, посредством индексирования tuple’а.

==== Знаковая арифметика ====
В MyHDL
работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми
ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и
беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными
числами, пользователь должен строго указать, что эта переменная – знаковая. Но
когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё
становится сложнее.

Когда в Verilog
в одном выражении есть знаковые и беззнаковые числа, все считаются
беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик
вынужден будет добавлять знаковые расширения и преобразования типов для решения
этой проблемы.

В VHDL
знаковые и беззнаковые числа в одном выражении просто не будут работать.
Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только
после этого включать в выражение.

В MyHDL
эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того,
конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и  VHDL. Он использует знаковые и беззнаковые типы в зависимости от
ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя
граница, верхняя граница=верхняя граница).

==== Пользовательский код ====
Если необходимо, пользователь может обходить конверсию и
писать свой собственный код, который вставится непосредственно в конченый
проект.

== Конвертируемое множество ==

=== Введение ===
Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не
сильно значительные, конвертируемое подмножество намного шире, чем
синтезируемое подмножество, которое является производственным стандартом.
Другими словами, код на MyHDL,
написанный в соответствии с правилами для синтезируемых устройств, будет
конвертируемым. Хотя возможно также написать конвертируемый код для
несинтезируемых моделей и тестбенчей.

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

=== Кодстайл ===
Действительно важное ограничение на конвертируемый код – это
то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку
или по другому сигналу.

Для чистого моделирования неважно, какие генераторы Вы
используете. Хотя в конвертируемом коде они должны быть созданы с помощью
декораторов из MyHDL:   '''instance()''', '''always()''', '''always_seq()''', или '''always_comb()'''.

=== Поддерживаемые типы ===
Наиболее значимое ограничение
накладывается на типы. Только ограниченное количество типов может быть
скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены
длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL.

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

index '''=''' intbv(0, min'''='''MIN, max'''='''MAX)

Verilog-конвертер поддерживает intbv с отрицательными значениями.

Еще можно создавать его с помощью
слайсинга вот так:

index '''=''' intbv(0)[N:]

Это выражение даст нам
число 0 с максимальным значением 2**N, то есть длиной N.

Конвертер вообще
поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) http://docs.myhdl.org/en/stable/manual/index.html

Параметры действия

ПеременнаяЗначение
Число правок участника ($1) (user_editcount)
0
Имя учётной записи ($1) (user_name)
'Ahlininv'
Возраст учётной записи ($1) (user_age)
78722327
Группы (включая неявные) в которых состоит участник ($1) (user_groups)
[ 0 => '*', 1 => 'user' ]
Редактирует ли участник через мобильный интерфейс ($1) (user_mobile)
false
ID страницы ($1) (page_id)
0
Пространство имён страницы ($1) (page_namespace)
0
Название страницы (без пространства имён) ($1) (page_title)
'MyHDL'
Полное название страницы ($1) (page_prefixedtitle)
'MyHDL'
Действие ($1) (action)
'edit'
Описание правки/причина ($1) (summary)
'Вторая часть официального мануала к MyHDL более-менее хорошо переведена и читабельна.'
Была ли правка отмечена как «малое изменение» (больше не используется) (minor_edit)
false
Вики-текст старой страницы до правки ($1) (old_wikitext)
''
Вики-текст новой страницы после правки ($1) (new_wikitext)
'== Юнит-тестирование == === Введение === Многие аспекты разработки современных цифровых аппаратных средств могут быть рассмотрены как отдельное направление разработки ПО. Рассматривая ситуацию с такой точки зрения, это спорный вопрос – могут ли технологии, применимые в разработке ПО, применяться в разработке аппаратного обеспечения. Есть подход к программированию, который стал популярен относительно недавно. Он называется «Экстремальное программирование» (''Extreme Programming, ХР). Это впечатляющий набор способов и руководств, который идет против традиционных подходов к разработке. В других случаях экстремальное программирование будто бы подчеркивает здравый смысл, который не всегда присутствует в стандартных методах. Например, экстремальное программирование делает упор на рабочую неделю, что позволяет иметь всегда свежую голову. Это необходимо для качественной разработки.'' ''Эта статья не является попыткой написать учебник по экстремальному программированию. Наоборот, в этой статье я подчеркну лишь одну черту экстремального программирования, которая необходима в аппаратной разработке: важность юнит-тестирования и его методики.'' === ''Важность юнит-тестирования'' === ''Юнит-тестирование – это один из краеугольных камней экстремального программирования. Другие идеи этой технологии, такие как коллективное владение кодом и непрерывное повышение качества возможны тогда и только тогда, когда реализовано юнит-тестирование. Более того, экстремальное программирование подчеркивает, что написание юнит-тестов должно быть автоматизировано, что должны быть протестированы все элементы всех классов и они должны работать идеально всё время.'' ''Я верю, что эта концепция применима как раз в аппаратной разработке. К томуже, юнит-тесты – это способ управлять временем симуляции. Например, верификация конечного автомата, который очень медленно работает на редких событиях, может быть затруднена или невозможна с учетом ограниченности времени, даже на самом быстром симуляторе.'' ''Понятно, что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны, если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно, в теории экстремального программирования подчеркивается необходимость стандартного юнит-тестирования, которое поддерживает все описанные задачи. В этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для аппаратных проектов.'' === ''Разработка юнит-тестов.'' === ''В этой главе мы будем неформально изучать применение юнит-тестирования к аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем тестировать устройство, переводящее двоичный код в код Грея. Это устройство было описано в разделе Bit indexing.'' ==== Определение требований ==== Мы начинаем с определения требований. Для кодировщика в код Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея. Давайте определим код как список кодовых слов, где кодовое слово – это строка из битов. Код длины n может определять 2**n слов. Широко известные характеристики – это то, для чего предназначен код Грея: Последовательные кодовые слова в коде Грея должны отличаться ровно в одном бите. Этого достаточно? Не совсем. Например, под наши нынешние требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это, очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода Грея превосходила длину битового слова. Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно одному слову в коде Грея такого же порядка. Когда написаны требования, мы можем продолжать. Сначала – тесты Отличная идея экстремального программирования состоит в том, что нужно писать юнит-тесты до написания кода. Таким образом, до того, как реализовывать что-то, сначала напишите тест, который это что-то должно проходить. Это противоречит стандартной практике и вообще логичному ходу событий. Многие разработчики предпочитают в первую очередь создать работающий продукт, а после этого уже ее тестировать. Но если подумать, что логично сделать верификацию сначала. Верификация – это просто условия. Таким образом, ваши мысли еще не заняты деталями реализации. Юнит-тесты – это работающая реализация описания и требований, поэтому реализация их в коде позволяет их более хорошо понять, поэтому это стоит сделать. После этого реализация будет проще. Возможно, самым важным является то, что тесты доступны уже во время разработки, и их может запускать любой из разработчиков проекта, чтобы проверять свои изменения. Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий юнит-тесты. С unittest тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются модулем как тесты из комплекта тестов. Мы зададим тесты для требований кода Грея, после чего напишем тесты для каждого из требований. Итогом создания комплекта тестов таков: '''from''' unittest '''import''' TestCase '''class''' '''TestGrayCodeProperties'''(TestCase): '''def''' '''testSingleBitChange'''(self): """ Check that only one bit changes in successive codewords """ '''....''' '''def''' '''testUniqueCodeWords'''(self): """ Check that all codewords occur exactly once """ '''....''' Каждый метод будет небольшим тестом, который проверяет заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую реализацию, включающую только интерфейс: '''def''' '''bin2gray'''(B, G, width):     ''### NOT IMPLEMENTED YET! ###''     '''yield''' None Для первого требования мы напишем тест, который подает на вход последовательно все цифры, а потом сравнивает между собой выходные данные кодировщика и проверяет, что действительно последовательные слова отличаются на один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного порядка MAX_WIDTH. '''def''' '''testSingleBitChange'''(self):     """ Check that only one bit changes in successive codewords """     '''def''' '''test'''(B, G, width):         B'''.'''next '''=''' intbv(0)         '''yield''' delay(10)         '''for''' i '''in''' range(1, 2'''**'''width):             G_Z'''.'''next '''=''' G             B'''.'''next '''=''' intbv(i)             '''yield''' delay(10)             diffcode '''=''' bin(G '''^''' G_Z)             self'''.'''assertEqual(diffcode'''.'''count('1'), 1)     '''for''' width '''in''' range(1, MAX_WIDTH):         B '''=''' Signal(intbv('''-'''1))         G '''=''' Signal(intbv(0))         G_Z '''=''' Signal(intbv(0))         dut '''=''' bin2gray(B, G, width)         check '''=''' test(B, G, width)         sim '''=''' Simulation(dut, check)         sim.run(quiet=1) Обратите внимание на то, как проверка равенства осуществляется с помощью метода self.assertEqual , определенного в unittest.TestCase. Точно так же мы пишем тест для проверки второго требования. Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из кодировщика, сортируем их и проверяем совпадение с начальным набором. '''def''' '''testUniqueCodeWords'''(self):     """ Check that all codewords occur exactly once """     '''def''' '''test'''(B, G, width):         actual '''=''' []         '''for''' i '''in''' range(2'''**'''width):             B'''.'''next '''=''' intbv(i)             '''yield''' delay(10)             actual'''.'''append(int(G))         actual'''.'''sort()         expected '''=''' range(2'''**'''width)         self'''.'''assertEqual(actual, expected)     '''for''' width '''in''' range(1, MAX_WIDTH):         B '''=''' Signal(intbv('''-'''1))         G '''=''' Signal(intbv(0))         dut '''=''' bin2gray(B, G, width)         check '''=''' test(B, G, width)         sim '''=''' Simulation(dut, check)         sim'''.'''run(quiet'''='''1) Разработка от тестирования Когда написаны тесты, мы начинаем реализацию. Для демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и посмотрим, как тесты будут себя вести. unittest'''.'''main() Давайте запустим этот тест, используя неработающий, еще пустой объект кодировщика, показанный выше: % python test_gray.py -v Check that only one bit changes in successive codewords ... FAIL Check that all codewords occur exactly once ... FAIL <trace backs not shown> Как и ожидалось, он полностью проваливается. Теперь попытаемся протестировать неправильную реализацию, которая выдает последний бит числа (то есть удовлетворяет одному условию): '''def''' '''bin2gray'''(B, G, width):     ''### INCORRECT - DEMO PURPOSE ONLY! ###''     @always_comb     '''def''' '''logic'''():         G'''.'''next '''=''' B[0]     '''return''' logic Запуск тестов дает нам: % python test_gray.py -v Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... FAIL ====================================================================== FAIL: Check that all codewords occur exactly once ---------------------------------------------------------------------- Traceback (most recent call last):   File "test_gray.py", line 109, in testUniqueCodeWords     sim.run(quiet=1) ...   File "test_gray.py", line 104, in test     self.assertEqual(actual, expected)   File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual     raise self.failureException, \ AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3] ---------------------------------------------------------------------- Ran 2 tests in 0.785s Теперь проходятся тесты, проверяющие первое из требований, но не проходятся - проверяющие второе. Нужно смотреть сообщения, которые выдает тест, чтобы понять, что не так с нашим кодом. В конце концов, если мы тестируем правильную версию bin2gray: '''def''' '''bin2gray'''(B, G, width):     """ Gray encoder.     B -- input intbv signal, binary encoded     G -- output intbv signal, gray encoded     width -- bit width     """     @always_comb     '''def''' '''logic'''():         '''for''' i '''in''' range(width):             G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i]     '''return''' logic Вывод получается такой: % python test_gray.py -v Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... ok ---------------------------------------------------------------------- Ran 2 tests in 6.364s OK ==== Изменение требований: ==== В предыдущей главе мы сосредоточились на основных требованиях к коду Грея. Их возможно определить не глядя на код самого устройства. Легко увидеть, что есть не одно решение (в виде программного кода модуля кодировщика), проходящее эти требования. В хорошем экстремальном программировании мы проверяем только требования и ничего больше. Может случиться так, что нужно больше контроля. Например, требования могут заключаться и в том, чтобы он выдавал заданные коды, а не удовлетворял двум общим признакам. Например, покажем, как тестировать код Грея, который является частным случаем, который удовлетворяет этим условиям (что описаны в предыдущей главе). В этом случае этот тест будет действительно проще предыдущего. Мы обозначим код Грея длины n за Ln. Примеры: L1 = ['0', '1'] L2 = ['00', '01', '11', '10'] L3 = ['000', '001', '011', '010', '110', '111', '101', 100'] Можно определить эти коды рекурсивным алгоритмом, например вот так: 1.    L1 = [‘0’, ‘1’] 2.    Ln+1 может быть получен из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому кодовому слову Ln  ‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1. Python хорошо известен как язык с красивыми описаниями алгоритмов, и это хороший тому пример. Вот как выглядит этот алгоритм на Python: '''def''' '''nextLn'''(Ln):     """ Return Gray code Ln+1, given Ln. """ Ln0 '''=''' ['0' '''+''' codeword '''for''' codeword '''in''' Ln] Ln1 '''=''' ['1' '''+''' codeword '''for''' codeword '''in''' Ln] Ln1'''.'''reverse() '''return''' Ln0 '''+''' Ln1 Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков, создаваемых с помощью цикла с простыми действиями. Сейчас требования состоят в том, что код точно соответствует Ln. Мы используем функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так: '''class''' '''TestOriginalGrayCode'''(TestCase): '''def''' '''testOriginalGrayCode'''(self): """ Check that the code is an original Gray code """ Rn '''=''' [] '''def''' '''stimulus'''(B, G, n): '''for''' i '''in''' range(2'''**'''n): B'''.'''next '''=''' intbv(i) '''yield''' delay(10) Rn'''.'''append(bin(G, width'''='''n)) Ln '''=''' ['0', '1'] ''# n == 1'' '''for''' n '''in''' range(2, MAX_WIDTH): Ln '''=''' nextLn(Ln) '''del''' Rn[:] B '''=''' Signal(intbv('''-'''1)) G '''=''' Signal(intbv(0)) dut '''=''' bin2gray(B, G, n) stim '''=''' stimulus(B, G, n) sim '''=''' Simulation(dut, stim) sim'''.'''run(quiet'''='''1) self'''.'''assertEqual(Ln, Rn) Если так сделать, то реализованное нами устройство выдает действительно настоящий код Грея. %python test_gray.py -v TestOriginalGrayCode Check that the code is an original Gray code ... ok <nowiki>----------------------------------------------------------------------</nowiki> Ran 1 tests in 3.091s OK == Одновременная с верилогом симуляция, Сосимуляция(Co-simulation) == === Введение === Одна из наиболее важных возможностей MyHDL заключается в том, что его можно использовать для верификации аппаратных устройств, написанных на Verilog и VHDL, писать с его помощью тесты и контролировать симуляцию. В наши дни известно, что язык, предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать современные технологии прораммирования, такие, как обьектно-ориентированность. Все потому, что верификация – это наиболее сложная и затратная по времени часть разработки аппаратного обеспечения. Следовательно, ценна любая новая технология. Более того, тесты не обязательно должны быть имплементируемыми (реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода, они не имеют ограничений в реализации. Технически верификация аппаратного обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В данный момент MyHDL включает PLI-модуль для двух симуляторов: Icarus и Cver. === На стороне HDL === Чтобы продемонстрировать сосимуляцию мы будем использовать снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать его, написав для этого на Verilog. Конечно же, мы захотим снова использовать те же тесты, что были для него уже написаны. Для начала давайте освежим в памяти, как выглядит кодировщик на MyHDL: '''def''' '''bin2gray'''(B, G, width): """ Gray encoder. B -- input intbv signal, binary encoded G -- output intbv signal, gray encoded width -- bit width """ @always_comb '''def''' '''logic'''(): '''for''' i '''in''' range(width): G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i] '''return''' logic Чтобы показать, что происходит при симуляции, нам не нужна реализация на Verilog, а нужен только ее интерфейс. Кодировщик будет иметь следующий интерфейс: module bin2gray(B, G);    parameter width = 8;    input [width-1:0]  B; output [width-1:0] G; .... Чтобы написать тесты кто-то создает новый модуль, который представляет собой Design Under Test (DUT). Тестбенч определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том же HDL, но мы будем писать их не на HDL, а на MyHDL. чтобы соединить их с входами Verilog-модуля нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере этоделается так: module dut_bin2gray;    reg [`width-1:0] B;    wire [`width-1:0] G; initial begin $from_myhdl(B); $to_myhdl(G); end bin2gray dut (.B(B), .G(G)); defparam dut.width = `width; endmodule $from_myhdl говорит о том, что эти провода принимают сигналы от MyHDL, а $to_myhdl означает, что данные сигналы будут считаны тестовым окружением MyHDL. Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле myhdl.vpi , который скомпилирован из кода, написанного на С. === На стороне MyHDL === MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать, как запускать симуляцию HDL. следовательно, первый аргумент для его конструктора – это командная строка для исполнения симуляции. Способ генерации и запуска исполняемого файла симуляции зависит от симулятора. Например, в Icarus Verilog исполняемый файл для нашего примера может быть получен посредством запуска компилятора iverilog, как показано ниже: % iverilog -o bin2gray -Dwidth=4 bin2gray.v dut_bin2gray.v Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов. Симуляция его запускается с помощью команды vvp: % vvp -m ./myhdl.vpi bin2gray Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation) Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так: '''import''' os '''from''' myhdl '''import''' Cosimulation cmd '''=''' "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v" '''def''' '''bin2gray'''(B, G, width): os'''.'''system(cmd '''%''' width) '''return''' Cosimulation("vvp -m ./myhdl.vpi bin2gray", B'''='''B, G'''='''G) После аргумента-исполняемой команды Cosimulation принимает все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти имена указаны в $to_myhdl и $from_myhdl. Аргументы этих функций – сигналы из MyHDL. Когда мы изучили всё это, мы можем попробовать использовать существующий юнит-тест для верификации Verilog-описания. Обратите внимание, что мы используем одинаковые имена и параметры для функции bin2gray: все, что нам нужно, - это обеспечить другое определение существующему юнит-тесту. Взглянем на этот Verilog-проект: module bin2gray(B, G); parameter width = 8; input [width-1:0]  B; output [width-1:0] G; reg [width-1:0] G; integer i; wire [width:0] extB; assign extB = {1'b0, B}; // zero-extend input always @(extB) begin for (i=0; i < width; i=i+1) G[i] <= extB[i+1] ^ extB[i]; end endmodule Если мы запустим тест, мы получим: % python test_bin2gray.py Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... ok Check that the code is an original Gray code ... ok <nowiki>----------------------------------------------------------------------</nowiki> Ran 3 tests in 2.729s OK === Ограничения === В идеальной ситуации можно симулировать любое HDL-описание с помощью MyHDL, не прилагая дополнительных усилий. К тому же соединенные сигналы с обеих сторон работают как один, делая границу между тестовым окружением и модулем абсолютно прозрачной. По некоторым причинам невозможно добиться полной общности. Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения отдельного симулятора и различия между разными симуляторами могут быть ошеломляющими. К тому же полная универсальность может требовать непропорционального времени под разработку по сравнению с немного менее общим решением, которого может быть достаточно для данной задачи. Далее, я попробовал достичь решения, которое будет достаточно простым, чтобы можно было довольно уверенно ожидать, что любой симулятор, поддерживающий PLI, сможет его поддерживать и чтобы было довольно просто верифицировать его и поддерживать. В то же время, решение достаточно общее, чтобы заполнить всю область ожидаемого применения. Результатом компромисса было наложение на HDL нескольких ограничений. В этой главе будет рассказано о них. ==== Только пассивный HDL может быть сосимулирован. ==== Наиболее важное ограничение MyHDL на сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В частности, сигнал clk должен быть сгенерирован на стороне MyHDL. Сначала это кажется важным ограничением, но если рассмотреть реальные случаи, оно оказывается не очень важным. MyHDL поддерживает сосимуляцию так, что тесты должны быть именно на Python. Давайте рассмотрим природу тестируемых HDL-проектов. Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом. Это и есть одна из важных задач MyHDL. Точно так же прошиваемые проекты с заявленными временными ограничениями – не то, что нас интересует: статический анализ тайминга – намного более хороший способ для таких проектов. Итог: HDL-описания, которые нас интересуют – это реальные модели аппаратуры, которые будут синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы в синтезируемом коде не важны(нет строго заданного в проекте тайминга), ограничение не влияет на выполнение наших задач. ==== 1.      Гонка ==== В обычном RTL некоторые события иногда вызывают другие события на том же clk (на том же временном интервале). Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика, насколько я помню). This is done by the concept of “delta” cycles. In a fully general, race free co-simulation, the co-simulators would communicate at the level of delta cycles. However, in MyHDL co-simulation, this is not entirely the case. Дельта-циклы из MyHDL-симулятора в HDL-симулятор запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе. Что это значит? Давайте начнем с хооших новостей. Как уже объяснялось в предыдущей главе, концепция MyHDL предусматривает, что clk генерируется на стороне MyHDL. когда мы используем clk от MyHDL и соответствующий ему сигнал в HDL, сосимуляция не может иметь гонок. То есть используя полность подход MyHDL, мы не можем столкнуться с гонкой. Другая ситуация наступает, когда мы хотим использовать сигнал, который управляется из HDL (и соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно изменять сигналы в MyHDL со сдвигом по времени, избегая таким образом возможность произвольного взаиморасположения положительного фронта clk и события, избегая таким образом гонки. ЕЩЕ ТУТ БЫЛО ЧТО-ТО. == Трансляция в Verilog и VHDL == === Введение === Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL. Эта глава описывает основы этой трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. <nowiki>http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage</nowiki> === Описание продукта === Чтобы быть конвертируемым, проект должен удовлетворять некоторым ограничениям, которые мы будем называть конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте «Конвертируемое подмножество». Конвертируемый проект может быть сконвертирован в эквивалентную модель на Verilog или VHDL с помощью функций toVerilog(), toVHDL() библиотеки MyHDL. Когда проект предназначается для имплементации, синтез производит другое средство, предназначенное для синтезирования из Verilog и VHDL проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести проект, написанный на Python, до уровня прошивки платы. Конверсия начинается не с исходного кода, а с инстанцированного проекта, который воспринимается интерпретатором Python. Конвертер использует профилировщик Питона, чтобы понимать все операции интерпретатора Питона и чтобы определить структуру проекта и пространство имен. Далее он выборочно компилирует части исходного кода для дополнительного анализа и для конверсии. === Особенности === ==== Конверсия после обработки ==== Под обработкой в данном случае понимается изначальная обработка проекта для достижения представления о устройстве, в том числе, может ли оно быть сконвертировано, синтезировано и имплементировано. В частности, структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для обработки используется интерпретатор Питона. Объект Simulation строится на основе своих аргументов - обработанных экземпляров. Таким же образом конверсия работает с уже собранным экземпляром всего проекта. Таким образом, интерпретатор Питона используется максимально активно. ==== Обработка структур любой сложности ==== Так как конверсия работает с уже обработанными экземплярами, все условия(constraints) моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами, нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина иерархии, естественно, тоже не ограничена. ==== Генераторы переводятся в Verilog и VHDL ==== Конвертор анализирует код каждого генератора и переводит его в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки. В VHDL они будут переведены в process-блоки одновременных присваиваний сигналов. ==== Назначение портов определяется в зависимости от сигналов ==== В MyHDL нет понятия направления порта, нет входных и выходных портов, определенных строго и единожды. Конвертер проверяет, как используется сигнал: как входной порт, выходной порт или как внутренний сигнал. ==== Интерфейсы конвертируемы ==== Интерфейс – это объект с несколькими сигналами и их свойствами. Конвертер поддерживает их с помощью имен и mangling. ==== Вызов функций преобразуется в Verilog и VHDL-подпрограммы ==== Конвертер анализирует функцию и ее код. Каждая функция переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы поддерживать возможности функций в Питоне полностью, на каждый вызов функции в Питоне генерируется отдельная подпрограмма ==== if-then-else-структуры могут быть переведены в case-блоки ==== Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции, где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими свойствами в случае синтеза (вероятно, мультиплексор – авт.) ==== Выбор способов перевода enum’ов – перечислимых типов ==== Функция enum() в MyHDL возвращает перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет способ ее перевода в HDL: бинарный, one hot, или one cold. ==== Память с произвольным доступом RAM ==== Большинство средств синтеза могут переводить Verilog и VHDL, определяющий блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию, конвертер преобразует list из Signal’ов в Verilog-памяти или VHDL-массивы. ==== Память ROM ==== Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает перевод в такие конструкции автоматически, основываясь на высокоуровневом описании. ROM описывается одной строчкой, посредством индексирования tuple’а. ==== Знаковая арифметика ==== В MyHDL работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными числами, пользователь должен строго указать, что эта переменная – знаковая. Но когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё становится сложнее. Когда в Verilog в одном выражении есть знаковые и беззнаковые числа, все считаются беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик вынужден будет добавлять знаковые расширения и преобразования типов для решения этой проблемы. В VHDL знаковые и беззнаковые числа в одном выражении просто не будут работать. Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только после этого включать в выражение. В MyHDL эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того, конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и  VHDL. Он использует знаковые и беззнаковые типы в зависимости от ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя граница, верхняя граница=верхняя граница). ==== Пользовательский код ==== Если необходимо, пользователь может обходить конверсию и писать свой собственный код, который вставится непосредственно в конченый проект. == Конвертируемое множество == === Введение === Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не сильно значительные, конвертируемое подмножество намного шире, чем синтезируемое подмножество, которое является производственным стандартом. Другими словами, код на MyHDL, написанный в соответствии с правилами для синтезируемых устройств, будет конвертируемым. Хотя возможно также написать конвертируемый код для несинтезируемых моделей и тестбенчей. Конвертер пытается выдавать понятные сообщения об ошибках, когда натыкается на конструкцию, которую сконвертировать невозможно. === Кодстайл === Действительно важное ограничение на конвертируемый код – это то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку или по другому сигналу. Для чистого моделирования неважно, какие генераторы Вы используете. Хотя в конвертируемом коде они должны быть созданы с помощью декораторов из MyHDL:   '''instance()''', '''always()''', '''always_seq()''', или '''always_comb()'''. === Поддерживаемые типы === Наиболее значимое ограничение накладывается на типы. Только ограниченное количество типов может быть скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL. Объекты Intbv должны быть сконструированы с той длиной, которую они определяют сами с помощью нижнего и верхнего ограничения, которое задаем мы сами: index '''=''' intbv(0, min'''='''MIN, max'''='''MAX) Verilog-конвертер поддерживает intbv с отрицательными значениями. Еще можно создавать его с помощью слайсинга вот так: index '''=''' intbv(0)[N:] Это выражение даст нам число 0 с максимальным значением 2**N, то есть длиной N. Конвертер вообще поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) http://docs.myhdl.org/en/stable/manual/index.html'
Унифицированная разница изменений правки ($1) (edit_diff)
'@@ -1,2 +1,914 @@ +== Юнит-тестирование == +=== Введение === +Многие аспекты разработки современных цифровых аппаратных +средств могут быть рассмотрены как отдельное направление разработки ПО. +Рассматривая ситуацию с такой точки зрения, это спорный вопрос – могут ли +технологии, применимые в разработке ПО, применяться в разработке аппаратного +обеспечения. + +Есть подход к программированию, который стал популярен +относительно недавно. Он называется «Экстремальное программирование» (''Extreme Programming, +ХР). Это +впечатляющий набор способов и руководств, который идет против традиционных +подходов к разработке. В других случаях экстремальное программирование будто бы +подчеркивает здравый смысл, который не всегда присутствует в стандартных +методах. Например, экстремальное программирование делает упор на рабочую +неделю, что позволяет иметь всегда свежую голову. Это необходимо для +качественной разработки.'' + +''Эта +статья не является попыткой написать учебник по экстремальному +программированию. Наоборот, в этой статье я подчеркну лишь одну черту +экстремального программирования, которая необходима в аппаратной разработке: +важность юнит-тестирования и его методики.'' + +=== ''Важность юнит-тестирования'' === +''Юнит-тестирование +– это один из краеугольных камней экстремального программирования. Другие идеи +этой технологии, такие как коллективное владение кодом и непрерывное повышение +качества возможны тогда и только тогда, когда реализовано юнит-тестирование. +Более того, экстремальное программирование подчеркивает, что написание +юнит-тестов должно быть автоматизировано, что должны быть протестированы все +элементы всех классов и они должны работать идеально всё время.'' + +''Я +верю, что эта концепция применима как раз в аппаратной разработке. К томуже, +юнит-тесты – это способ управлять временем симуляции. Например, верификация +конечного автомата, который очень медленно работает на редких событиях, может +быть затруднена или невозможна с учетом ограниченности времени, даже на самом +быстром симуляторе.'' + +''Понятно, +что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны, +если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда +будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно, +в теории экстремального программирования подчеркивается необходимость +стандартного юнит-тестирования, которое поддерживает все описанные задачи. В +этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для +аппаратных проектов.'' + +=== ''Разработка юнит-тестов.'' === +''В +этой главе мы будем неформально изучать применение юнит-тестирования к +аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем +тестировать устройство, переводящее двоичный код в код Грея. Это устройство +было описано в разделе Bit indexing.'' + +==== Определение требований ==== +Мы начинаем с определения требований. Для кодировщика в код +Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея. +Давайте определим код как список кодовых слов, где кодовое слово – это строка +из битов. Код длины n +может определять 2**n слов. + +Широко известные характеристики – это то, для чего +предназначен код Грея: + +Последовательные кодовые слова в коде Грея должны отличаться +ровно в одном бите. + +Этого достаточно? Не совсем. Например, под наши нынешние +требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это, +очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода +Грея превосходила длину битового слова. + +Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно +одному слову в коде Грея такого же порядка. + +Когда написаны требования, мы можем продолжать. + +Сначала – тесты + +Отличная идея экстремального программирования состоит в том, +что нужно писать юнит-тесты до написания кода. Таким образом, до того, как +реализовывать что-то, сначала напишите тест, который это что-то должно +проходить. Это противоречит стандартной практике и вообще логичному ходу +событий. Многие разработчики предпочитают в первую очередь создать работающий +продукт, а после этого уже ее тестировать. + +Но если подумать, что логично сделать верификацию сначала. +Верификация – это просто условия. Таким образом, ваши мысли еще не заняты +деталями реализации. Юнит-тесты – это работающая реализация описания и +требований, поэтому реализация их в коде позволяет их более хорошо понять, +поэтому это стоит сделать. После этого реализация будет проще. Возможно, самым +важным является то, что тесты доступны уже во время разработки, и их может +запускать любой из разработчиков проекта, чтобы проверять свои изменения. + +Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий +юнит-тесты. С unittest +тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как +методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются +модулем как тесты из комплекта тестов. + +Мы зададим тесты для требований кода Грея, после чего +напишем тесты для каждого из требований. Итогом создания комплекта тестов таков: + +'''from''' unittest '''import''' TestCase + +'''class''' '''TestGrayCodeProperties'''(TestCase): + +'''def''' '''testSingleBitChange'''(self): + +""" Check that only one bit changes in successive codewords +""" + +'''....''' + +'''def''' '''testUniqueCodeWords'''(self): + +""" Check that all codewords occur exactly once +""" + +'''....''' + +Каждый метод будет небольшим тестом, который проверяет +заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого +кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую +реализацию, включающую только интерфейс: + '''def''' '''bin2gray'''(B, G, width): + +     ''### NOT IMPLEMENTED YET! ###'' + +     '''yield''' None +Для первого требования мы напишем тест, который подает на +вход последовательно все цифры, а потом сравнивает между собой выходные данные +кодировщика и проверяет, что действительно последовательные слова отличаются на +один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного +порядка MAX_WIDTH. + '''def''' '''testSingleBitChange'''(self): + +     """ Check that only one bit changes in successive codewords """ + +     '''def''' '''test'''(B, G, width): + +         B'''.'''next '''=''' intbv(0) + +         '''yield''' delay(10) + +         '''for''' i '''in''' range(1, 2'''**'''width): + +             G_Z'''.'''next '''=''' G + +             B'''.'''next '''=''' intbv(i) + +             '''yield''' delay(10) + +             diffcode '''=''' bin(G '''^''' G_Z) + +             self'''.'''assertEqual(diffcode'''.'''count('1'), 1) + +     '''for''' width '''in''' range(1, MAX_WIDTH): + +         B '''=''' Signal(intbv('''-'''1)) + +         G '''=''' Signal(intbv(0)) + +         G_Z '''=''' Signal(intbv(0)) + +         dut '''=''' bin2gray(B, G, width) + +         check '''=''' test(B, G, width) + +         sim '''=''' Simulation(dut, check) + +         sim.run(quiet=1) + +Обратите внимание на то, как проверка равенства +осуществляется с помощью метода self.assertEqual , +определенного в unittest.TestCase. + +Точно так же мы пишем тест для проверки второго требования. +Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из +кодировщика, сортируем их и проверяем совпадение с начальным набором. + '''def''' '''testUniqueCodeWords'''(self): + +     """ Check that all codewords occur exactly once """ + +     '''def''' '''test'''(B, G, width): + +         actual '''=''' [] + +         '''for''' i '''in''' range(2'''**'''width): + +             B'''.'''next '''=''' intbv(i) + +             '''yield''' delay(10) + +             actual'''.'''append(int(G)) + +         actual'''.'''sort() + +         expected '''=''' range(2'''**'''width) + +         self'''.'''assertEqual(actual, expected) + +     '''for''' width '''in''' range(1, MAX_WIDTH): + +         B '''=''' Signal(intbv('''-'''1)) + +         G '''=''' Signal(intbv(0)) + +         dut '''=''' bin2gray(B, G, width) + +         check '''=''' test(B, G, width) + +         sim '''=''' Simulation(dut, check) + +         sim'''.'''run(quiet'''='''1) + +Разработка от тестирования + +Когда написаны тесты, мы начинаем реализацию. Для +демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и +посмотрим, как тесты будут себя вести. + unittest'''.'''main() +Давайте запустим этот тест, используя неработающий, еще +пустой объект кодировщика, показанный выше: + % python test_gray.py -v + + Check that only one bit changes in successive codewords ... FAIL + + Check that all codewords occur exactly once ... FAIL + + <trace backs not shown> + +Как и ожидалось, он полностью проваливается. Теперь +попытаемся протестировать неправильную реализацию, которая выдает последний бит +числа (то есть удовлетворяет одному условию): + '''def''' '''bin2gray'''(B, G, width): + +     ''### INCORRECT - DEMO PURPOSE ONLY! ###'' + +     @always_comb + +     '''def''' '''logic'''(): + +         G'''.'''next '''=''' B[0] + +     '''return''' logic +Запуск тестов дает нам: + % python test_gray.py -v + + Check that only one bit changes in successive codewords ... ok + + Check that all codewords occur exactly once ... FAIL + + ====================================================================== + + FAIL: Check that all codewords occur exactly once + + ---------------------------------------------------------------------- + + Traceback (most recent call last): + +   File "test_gray.py", line 109, in testUniqueCodeWords + +     sim.run(quiet=1) + + ... + +   File "test_gray.py", line 104, in test + +     self.assertEqual(actual, expected) + +   File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual + +     raise self.failureException, \ + + AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3] + + ---------------------------------------------------------------------- + + Ran 2 tests in 0.785s + +Теперь проходятся тесты, проверяющие первое из требований, +но не проходятся - проверяющие второе. Нужно смотреть сообщения, которые выдает +тест, чтобы понять, что не так с нашим кодом. + +В конце концов, если мы тестируем правильную версию bin2gray: + '''def''' '''bin2gray'''(B, G, width): + +     """ Gray encoder. + +     B -- input intbv signal, binary encoded + +     G -- output intbv signal, gray encoded + +     width -- bit width + +     """ + +     @always_comb + +     '''def''' '''logic'''(): + +         '''for''' i '''in''' range(width): + +             G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i] + +     '''return''' logic +Вывод получается такой: + % python test_gray.py -v + + Check that only one bit changes in successive codewords ... ok + + Check that all codewords occur exactly once ... ok + + ---------------------------------------------------------------------- + + Ran 2 tests in 6.364s + + OK + +==== Изменение требований: ==== +В предыдущей главе мы сосредоточились на основных +требованиях к коду Грея. Их возможно определить не глядя на код самого +устройства. Легко увидеть, что есть не одно решение (в виде программного кода +модуля кодировщика), проходящее эти требования. В хорошем экстремальном +программировании мы проверяем только требования и ничего больше. + +Может случиться так, что нужно больше контроля. Например, +требования могут заключаться и в том, чтобы он выдавал заданные коды, а не +удовлетворял двум общим признакам. Например, покажем, как тестировать код Грея, +который является частным случаем, который удовлетворяет этим условиям (что +описаны в предыдущей главе). В этом случае этот тест будет действительно проще +предыдущего. + +Мы обозначим код Грея длины n за Ln. Примеры: + L1 = ['0', '1'] + + L2 = ['00', '01', '11', '10'] + + L3 = ['000', '001', '011', '010', '110', '111', '101', 100'] +Можно определить эти коды рекурсивным алгоритмом, например +вот так: + +1.    L1 = [‘0’, ‘1’] + +2.    +Ln+1 может быть получен +из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому +кодовому слову Ln  ‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1. + +Python хорошо известен как +язык с красивыми описаниями алгоритмов, и это хороший тому пример. Вот как +выглядит этот алгоритм на Python: + +'''def''' '''nextLn'''(Ln): + +    """ +Return Gray code Ln+1, given Ln. """ + +Ln0 '''=''' ['0' '''+''' codeword '''for''' codeword '''in''' Ln] + +Ln1 '''=''' ['1' '''+''' codeword '''for''' codeword '''in''' Ln] + +Ln1'''.'''reverse() + +'''return''' +Ln0 '''+''' Ln1 + +Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков, +создаваемых с помощью цикла с простыми действиями. + +Сейчас требования состоят в том, что код +точно соответствует Ln. Мы используем +функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так: + +'''class''' '''TestOriginalGrayCode'''(TestCase): + +'''def''' '''testOriginalGrayCode'''(self): + +""" Check that the code is an original Gray code +""" + +Rn '''=''' [] + +'''def''' +'''stimulus'''(B, G, n): + +'''for''' +i '''in''' range(2'''**'''n): + +B'''.'''next '''=''' intbv(i) + +'''yield''' +delay(10) + +Rn'''.'''append(bin(G, width'''='''n)) + +Ln '''=''' ['0', '1'] ''# n == 1'' + +'''for''' +n '''in''' range(2, MAX_WIDTH): + +Ln '''=''' nextLn(Ln) + +'''del''' +Rn[:] + +B '''=''' Signal(intbv('''-'''1)) + +G '''=''' Signal(intbv(0)) + +dut '''=''' bin2gray(B, G, n) + +stim '''=''' stimulus(B, G, n) + +sim '''=''' Simulation(dut, stim) + +sim'''.'''run(quiet'''='''1) + +self'''.'''assertEqual(Ln, Rn) + +Если так сделать, то реализованное нами +устройство выдает действительно настоящий код Грея. + +%python test_gray.py -v TestOriginalGrayCode + +Check that the code is an original Gray code ... ok + +<nowiki>----------------------------------------------------------------------</nowiki> + +Ran 1 tests in 3.091s + +OK + +== Одновременная с верилогом симуляция, Сосимуляция(Co-simulation) == + +=== Введение === +Одна из наиболее важных возможностей MyHDL заключается в том, что его можно использовать для верификации аппаратных +устройств, написанных на Verilog и VHDL, писать с его помощью тесты и контролировать симуляцию. + +В наши дни известно, что язык, +предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать +современные технологии прораммирования, такие, как обьектно-ориентированность. +Все потому, что верификация – это наиболее сложная и затратная по времени часть +разработки аппаратного обеспечения. Следовательно, ценна любая новая +технология. Более того, тесты не обязательно должны быть имплементируемыми +(реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода, +они не имеют ограничений в реализации. + +Технически верификация аппаратного +обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В +данный момент MyHDL включает PLI-модуль для двух +симуляторов: Icarus и Cver. + +=== На стороне HDL === +Чтобы продемонстрировать сосимуляцию мы будем использовать +снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать +его, написав для этого на Verilog. +Конечно же, мы захотим снова использовать те же тесты, что были для него уже +написаны. + +Для начала давайте освежим в памяти, как выглядит кодировщик +на MyHDL: + +'''def''' '''bin2gray'''(B, G, width): + +""" Gray encoder. + +B -- input +intbv signal, binary encoded + +G -- output +intbv signal, gray encoded + +width -- bit +width + +""" + +@always_comb + +'''def''' '''logic'''(): + +'''for''' +i '''in''' range(width): + +G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i] + +'''return''' logic + +Чтобы показать, что происходит при симуляции, нам не нужна +реализация на Verilog, +а нужен только ее интерфейс. Кодировщик будет иметь следующий интерфейс: + +module bin2gray(B, G); + +   parameter width = 8; + +   input +[width-1:0]  B; + +output +[width-1:0] G; + +.... + +Чтобы написать тесты кто-то создает новый модуль, который +представляет собой Design Under Test +(DUT). Тестбенч +определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых +сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том +же HDL, но мы будем +писать их не на HDL, а на MyHDL. +чтобы соединить их с входами Verilog-модуля +нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции +управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере +этоделается так: + +module dut_bin2gray; + +   reg [`width-1:0] B; + +   wire +[`width-1:0] G; + +initial begin + +$from_myhdl(B); + +$to_myhdl(G); + +end + +bin2gray dut +(.B(B), .G(G)); + +defparam dut.width = `width; + +endmodule + +$from_myhdl говорит о том, что эти провода +принимают сигналы от MyHDL, +а $to_myhdl означает, что данные сигналы будут +считаны тестовым окружением MyHDL. +Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого +поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле +myhdl.vpi , который скомпилирован из кода, написанного +на С. + +=== На стороне MyHDL === +MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать, +как запускать симуляцию HDL. +следовательно, первый аргумент для его конструктора – это командная строка для +исполнения симуляции. + +Способ генерации и запуска исполняемого файла симуляции +зависит от симулятора. Например, в Icarus Verilog исполняемый файл для нашего +примера может быть получен посредством запуска компилятора iverilog, как показано ниже: + +% iverilog -o bin2gray -Dwidth=4 bin2gray.v +dut_bin2gray.v + +Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов. + +Симуляция его запускается с помощью команды vvp: + +% vvp -m ./myhdl.vpi +bin2gray + +Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример +использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation) + +Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы +пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так: + +'''import''' os + +'''from''' myhdl '''import''' Cosimulation + +cmd '''=''' "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v" + +'''def''' '''bin2gray'''(B, G, width): + +os'''.'''system(cmd '''%''' width) + +'''return''' +Cosimulation("vvp -m ./myhdl.vpi +bin2gray", B'''='''B, G'''='''G) + +После аргумента-исполняемой команды Cosimulation принимает +все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти +имена указаны в $to_myhdl и +$from_myhdl. Аргументы этих функций – сигналы +из MyHDL. + +Когда мы изучили всё это, мы можем попробовать использовать +существующий юнит-тест для верификации Verilog-описания. Обратите внимание, что мы используем одинаковые +имена и параметры для функции bin2gray: все, что нам нужно, - +это обеспечить другое определение существующему юнит-тесту. + +Взглянем на этот Verilog-проект: + +module bin2gray(B, G); + +parameter +width = 8; + +input +[width-1:0]  B; + +output +[width-1:0] G; + +reg +[width-1:0] G; + +integer i; + +wire +[width:0] extB; + +assign extB = +{1'b0, B}; // zero-extend input + +always +@(extB) begin + +for (i=0; +i < width; i=i+1) + +G[i] +<= extB[i+1] ^ extB[i]; + +end + +endmodule + +Если мы запустим тест, мы получим: + +% python test_bin2gray.py + +Check that only one bit changes in successive +codewords ... ok + +Check that all codewords occur exactly once ... ok + +Check that the code is an original Gray code ... ok + +<nowiki>----------------------------------------------------------------------</nowiki> + +Ran 3 tests in 2.729s + +OK + +=== Ограничения === +В идеальной ситуации можно симулировать любое HDL-описание с помощью MyHDL, не прилагая +дополнительных усилий. К тому же соединенные сигналы с обеих сторон работают +как один, делая границу между тестовым окружением и модулем абсолютно +прозрачной. + +По некоторым причинам невозможно добиться полной общности. +Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения +отдельного симулятора и различия между разными симуляторами могут быть +ошеломляющими. К тому же полная универсальность может требовать +непропорционального времени под разработку по сравнению с немного менее общим +решением, которого может быть достаточно для данной задачи. + +Далее, я попробовал достичь решения, которое будет +достаточно простым, чтобы можно было довольно уверенно ожидать, что любой +симулятор, поддерживающий PLI, +сможет его поддерживать и чтобы было довольно просто верифицировать его и +поддерживать. В то же время, решение достаточно общее, чтобы заполнить всю +область ожидаемого применения. + +Результатом компромисса было наложение на HDL нескольких +ограничений. В этой главе будет рассказано о них. + +==== Только пассивный HDL может быть сосимулирован. ==== +Наиболее важное ограничение MyHDL на +сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк +с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В +частности, сигнал clk +должен быть сгенерирован на стороне MyHDL. + +Сначала это кажется важным ограничением, но если рассмотреть +реальные случаи, оно оказывается не очень важным. + +MyHDL поддерживает сосимуляцию так, что тесты должны быть именно +на Python. Давайте +рассмотрим природу тестируемых HDL-проектов. +Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в +плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом. +Это и есть одна из важных задач MyHDL. +Точно так же прошиваемые проекты с заявленными временными ограничениями – не +то, что нас интересует: статический анализ тайминга – намного более хороший +способ для таких проектов. + +Итог: HDL-описания, +которые нас интересуют – это реальные модели аппаратуры, которые будут +синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы +в синтезируемом коде не важны(нет строго заданного в проекте тайминга), +ограничение не влияет на выполнение наших задач. + +==== 1.      Гонка ==== +В обычном RTL +некоторые события иногда вызывают другие события на том же clk (на том же временном интервале). +Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика, +насколько я помню). This is done by the +concept of “delta” cycles. In a fully general, race free co-simulation, the +co-simulators would communicate at the level of delta cycles. However, in MyHDL +co-simulation, this is not entirely the case. + +Дельта-циклы из MyHDL-симулятора в HDL-симулятор +запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются +в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе. + +Что это значит? Давайте начнем с +хооших новостей. Как уже объяснялось в предыдущей главе, концепция MyHDL предусматривает, что clk +генерируется на стороне MyHDL. когда мы +используем clk от MyHDL и +соответствующий ему сигнал в HDL, сосимуляция +не может иметь гонок. То есть используя полность подход MyHDL, мы не можем столкнуться с гонкой. + +Другая ситуация наступает, когда +мы хотим использовать сигнал, который управляется из HDL (и +соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно +изменять сигналы в MyHDL со сдвигом по времени, избегая +таким образом возможность произвольного взаиморасположения положительного +фронта clk и события, избегая таким образом гонки. + +ЕЩЕ ТУТ БЫЛО ЧТО-ТО. + +== Трансляция в Verilog и VHDL == + +=== Введение === +Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL. + +Эта глава описывает основы этой +трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. <nowiki>http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage</nowiki> + +=== Описание продукта === +Чтобы быть конвертируемым, проект +должен удовлетворять некоторым ограничениям, которые мы будем называть +конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте +«Конвертируемое подмножество». + +Конвертируемый проект может быть +сконвертирован в эквивалентную модель на Verilog или VHDL с помощью +функций toVerilog(), toVHDL() библиотеки +MyHDL. + +Когда проект предназначается для +имплементации, синтез производит другое средство, предназначенное для +синтезирования из Verilog и VHDL +проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести +проект, написанный на Python, до уровня прошивки платы. + +Конверсия начинается не с +исходного кода, а с инстанцированного проекта, который воспринимается +интерпретатором Python. Конвертер использует +профилировщик Питона, чтобы понимать все операции интерпретатора Питона и чтобы +определить структуру проекта и пространство имен. Далее он выборочно +компилирует части исходного кода для дополнительного анализа и для конверсии. + +=== Особенности === + +==== Конверсия после обработки ==== +Под обработкой в данном случае понимается изначальная +обработка проекта для достижения представления о устройстве, в том числе, может +ли оно быть сконвертировано, синтезировано и имплементировано. В частности, +структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для +обработки используется интерпретатор Питона. Объект Simulation строится +на основе своих аргументов - обработанных экземпляров. Таким же образом +конверсия работает с уже собранным экземпляром всего проекта. Таким образом, +интерпретатор Питона используется максимально активно. + +==== Обработка структур любой сложности ==== +Так как конверсия работает с уже обработанными экземплярами, +все условия(constraints) +моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами, +нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина +иерархии, естественно, тоже не ограничена. + +==== Генераторы переводятся в Verilog и VHDL ==== +Конвертор анализирует код каждого генератора и переводит его +в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки. +В VHDL они будут +переведены в process-блоки +одновременных присваиваний сигналов. + +==== Назначение портов определяется в зависимости от сигналов ==== +В MyHDL +нет понятия направления порта, нет входных и выходных портов, определенных +строго и единожды. Конвертер проверяет, как используется сигнал: как входной +порт, выходной порт или как внутренний сигнал. + +==== Интерфейсы конвертируемы ==== +Интерфейс – это объект с несколькими сигналами и их +свойствами. Конвертер поддерживает их с помощью имен и mangling. + +==== Вызов функций преобразуется в Verilog и VHDL-подпрограммы ==== +Конвертер анализирует функцию и ее код. Каждая функция +переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы +поддерживать возможности функций в Питоне полностью, на каждый вызов функции в +Питоне генерируется отдельная подпрограмма + +==== if-then-else-структуры могут быть переведены в case-блоки ==== +Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции, +где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими +свойствами в случае синтеза (вероятно, мультиплексор – авт.) + +==== Выбор способов перевода enum’ов – перечислимых типов ==== +Функция enum() +в MyHDL возвращает +перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет +способ ее перевода в HDL: +бинарный, one hot, +или one cold. + +==== Память с произвольным доступом RAM ==== +Большинство средств синтеза могут переводить Verilog и +VHDL, определяющий +блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию, +конвертер преобразует list из Signal’ов +в Verilog-памяти или VHDL-массивы. + +==== Память ROM ==== +Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает +перевод в такие конструкции автоматически, основываясь на высокоуровневом +описании. ROM +описывается одной строчкой, посредством индексирования tuple’а. + +==== Знаковая арифметика ==== +В MyHDL +работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми +ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и +беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными +числами, пользователь должен строго указать, что эта переменная – знаковая. Но +когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё +становится сложнее. + +Когда в Verilog +в одном выражении есть знаковые и беззнаковые числа, все считаются +беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик +вынужден будет добавлять знаковые расширения и преобразования типов для решения +этой проблемы. + +В VHDL +знаковые и беззнаковые числа в одном выражении просто не будут работать. +Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только +после этого включать в выражение. + +В MyHDL +эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того, +конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и  VHDL. Он использует знаковые и беззнаковые типы в зависимости от +ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя +граница, верхняя граница=верхняя граница). + +==== Пользовательский код ==== +Если необходимо, пользователь может обходить конверсию и +писать свой собственный код, который вставится непосредственно в конченый +проект. + +== Конвертируемое множество == + +=== Введение === +Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не +сильно значительные, конвертируемое подмножество намного шире, чем +синтезируемое подмножество, которое является производственным стандартом. +Другими словами, код на MyHDL, +написанный в соответствии с правилами для синтезируемых устройств, будет +конвертируемым. Хотя возможно также написать конвертируемый код для +несинтезируемых моделей и тестбенчей. + +Конвертер пытается выдавать понятные сообщения об ошибках, +когда натыкается на конструкцию, которую сконвертировать невозможно. + +=== Кодстайл === +Действительно важное ограничение на конвертируемый код – это +то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку +или по другому сигналу. + +Для чистого моделирования неважно, какие генераторы Вы +используете. Хотя в конвертируемом коде они должны быть созданы с помощью +декораторов из MyHDL:   '''instance()''', '''always()''', '''always_seq()''', или '''always_comb()'''. + +=== Поддерживаемые типы === +Наиболее значимое ограничение +накладывается на типы. Только ограниченное количество типов может быть +скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены +длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL. + +Объекты Intbv должны быть +сконструированы с той длиной, которую они определяют сами с помощью нижнего и +верхнего ограничения, которое задаем мы сами: + +index '''=''' intbv(0, min'''='''MIN, max'''='''MAX) + +Verilog-конвертер поддерживает intbv с отрицательными значениями. + +Еще можно создавать его с помощью +слайсинга вот так: + +index '''=''' intbv(0)[N:] + +Это выражение даст нам +число 0 с максимальным значением 2**N, то есть длиной N. + +Конвертер вообще +поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) http://docs.myhdl.org/en/stable/manual/index.html '
Новый размер страницы ($1) (new_size)
51290
Старый размер страницы ($1) (old_size)
0
Изменение размера в правке ($1) (edit_delta)
51290
Добавленные в правке строки ($1) (added_lines)
[ 0 => '== Юнит-тестирование ==', 1 => '=== Введение ===', 2 => 'Многие аспекты разработки современных цифровых аппаратных', 3 => 'средств могут быть рассмотрены как отдельное направление разработки ПО.', 4 => 'Рассматривая ситуацию с такой точки зрения, это спорный вопрос – могут ли', 5 => 'технологии, применимые в разработке ПО, применяться в разработке аппаратного', 6 => 'обеспечения.', 7 => false, 8 => 'Есть подход к программированию, который стал популярен', 9 => 'относительно недавно. Он называется «Экстремальное программирование» (''Extreme Programming,', 10 => 'ХР). Это', 11 => 'впечатляющий набор способов и руководств, который идет против традиционных', 12 => 'подходов к разработке. В других случаях экстремальное программирование будто бы', 13 => 'подчеркивает здравый смысл, который не всегда присутствует в стандартных', 14 => 'методах. Например, экстремальное программирование делает упор на рабочую', 15 => 'неделю, что позволяет иметь всегда свежую голову. Это необходимо для', 16 => 'качественной разработки.''', 17 => false, 18 => '''Эта', 19 => 'статья не является попыткой написать учебник по экстремальному', 20 => 'программированию. Наоборот, в этой статье я подчеркну лишь одну черту', 21 => 'экстремального программирования, которая необходима в аппаратной разработке:', 22 => 'важность юнит-тестирования и его методики.''', 23 => false, 24 => '=== ''Важность юнит-тестирования'' ===', 25 => '''Юнит-тестирование', 26 => '– это один из краеугольных камней экстремального программирования. Другие идеи', 27 => 'этой технологии, такие как коллективное владение кодом и непрерывное повышение', 28 => 'качества возможны тогда и только тогда, когда реализовано юнит-тестирование.', 29 => 'Более того, экстремальное программирование подчеркивает, что написание', 30 => 'юнит-тестов должно быть автоматизировано, что должны быть протестированы все', 31 => 'элементы всех классов и они должны работать идеально всё время.''', 32 => false, 33 => '''Я', 34 => 'верю, что эта концепция применима как раз в аппаратной разработке. К томуже,', 35 => 'юнит-тесты – это способ управлять временем симуляции. Например, верификация', 36 => 'конечного автомата, который очень медленно работает на редких событиях, может', 37 => 'быть затруднена или невозможна с учетом ограниченности времени, даже на самом', 38 => 'быстром симуляторе.''', 39 => false, 40 => '''Понятно,', 41 => 'что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны,', 42 => 'если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда', 43 => 'будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно,', 44 => 'в теории экстремального программирования подчеркивается необходимость', 45 => 'стандартного юнит-тестирования, которое поддерживает все описанные задачи. В', 46 => 'этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для', 47 => 'аппаратных проектов.''', 48 => false, 49 => '=== ''Разработка юнит-тестов.'' ===', 50 => '''В', 51 => 'этой главе мы будем неформально изучать применение юнит-тестирования к', 52 => 'аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем', 53 => 'тестировать устройство, переводящее двоичный код в код Грея. Это устройство', 54 => 'было описано в разделе Bit indexing.''', 55 => false, 56 => '==== Определение требований ====', 57 => 'Мы начинаем с определения требований. Для кодировщика в код', 58 => 'Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея.', 59 => 'Давайте определим код как список кодовых слов, где кодовое слово – это строка', 60 => 'из битов. Код длины n', 61 => 'может определять 2**n слов.', 62 => false, 63 => 'Широко известные характеристики – это то, для чего', 64 => 'предназначен код Грея:', 65 => false, 66 => 'Последовательные кодовые слова в коде Грея должны отличаться', 67 => 'ровно в одном бите.', 68 => false, 69 => 'Этого достаточно? Не совсем. Например, под наши нынешние', 70 => 'требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это,', 71 => 'очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода', 72 => 'Грея превосходила длину битового слова. ', 73 => false, 74 => 'Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно', 75 => 'одному слову в коде Грея такого же порядка.', 76 => false, 77 => 'Когда написаны требования, мы можем продолжать.', 78 => false, 79 => 'Сначала – тесты', 80 => false, 81 => 'Отличная идея экстремального программирования состоит в том,', 82 => 'что нужно писать юнит-тесты до написания кода. Таким образом, до того, как', 83 => 'реализовывать что-то, сначала напишите тест, который это что-то должно', 84 => 'проходить. Это противоречит стандартной практике и вообще логичному ходу', 85 => 'событий. Многие разработчики предпочитают в первую очередь создать работающий', 86 => 'продукт, а после этого уже ее тестировать.', 87 => false, 88 => 'Но если подумать, что логично сделать верификацию сначала.', 89 => 'Верификация – это просто условия. Таким образом, ваши мысли еще не заняты', 90 => 'деталями реализации. Юнит-тесты – это работающая реализация описания и', 91 => 'требований, поэтому реализация их в коде позволяет их более хорошо понять,', 92 => 'поэтому это стоит сделать. После этого реализация будет проще. Возможно, самым', 93 => 'важным является то, что тесты доступны уже во время разработки, и их может', 94 => 'запускать любой из разработчиков проекта, чтобы проверять свои изменения.', 95 => false, 96 => 'Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий', 97 => 'юнит-тесты. С unittest', 98 => 'тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как', 99 => 'методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются', 100 => 'модулем как тесты из комплекта тестов.', 101 => false, 102 => 'Мы зададим тесты для требований кода Грея, после чего', 103 => 'напишем тесты для каждого из требований. Итогом создания комплекта тестов таков:', 104 => false, 105 => ''''from''' unittest '''import''' TestCase', 106 => false, 107 => ''''class''' '''TestGrayCodeProperties'''(TestCase):', 108 => false, 109 => ''''def''' '''testSingleBitChange'''(self):', 110 => false, 111 => '""" Check that only one bit changes in successive codewords', 112 => '"""', 113 => false, 114 => ''''....'''', 115 => false, 116 => ''''def''' '''testUniqueCodeWords'''(self):', 117 => false, 118 => '""" Check that all codewords occur exactly once', 119 => '"""', 120 => false, 121 => ''''....'''', 122 => false, 123 => 'Каждый метод будет небольшим тестом, который проверяет', 124 => 'заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого', 125 => 'кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую', 126 => 'реализацию, включающую только интерфейс:', 127 => ' '''def''' '''bin2gray'''(B, G, width):', 128 => false, 129 => '     ''### NOT IMPLEMENTED YET! ###''', 130 => false, 131 => '     '''yield''' None', 132 => 'Для первого требования мы напишем тест, который подает на', 133 => 'вход последовательно все цифры, а потом сравнивает между собой выходные данные', 134 => 'кодировщика и проверяет, что действительно последовательные слова отличаются на', 135 => 'один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного', 136 => 'порядка MAX_WIDTH.', 137 => ' '''def''' '''testSingleBitChange'''(self):', 138 => false, 139 => '     """ Check that only one bit changes in successive codewords """', 140 => false, 141 => '     '''def''' '''test'''(B, G, width):', 142 => false, 143 => '         B'''.'''next '''=''' intbv(0)', 144 => false, 145 => '         '''yield''' delay(10)', 146 => false, 147 => '         '''for''' i '''in''' range(1, 2'''**'''width):', 148 => false, 149 => '             G_Z'''.'''next '''=''' G', 150 => false, 151 => '             B'''.'''next '''=''' intbv(i)', 152 => false, 153 => '             '''yield''' delay(10)', 154 => false, 155 => '             diffcode '''=''' bin(G '''^''' G_Z)', 156 => false, 157 => '             self'''.'''assertEqual(diffcode'''.'''count('1'), 1)', 158 => false, 159 => '     '''for''' width '''in''' range(1, MAX_WIDTH):', 160 => false, 161 => '         B '''=''' Signal(intbv('''-'''1))', 162 => false, 163 => '         G '''=''' Signal(intbv(0))', 164 => false, 165 => '         G_Z '''=''' Signal(intbv(0))', 166 => false, 167 => '         dut '''=''' bin2gray(B, G, width)', 168 => false, 169 => '         check '''=''' test(B, G, width)', 170 => false, 171 => '         sim '''=''' Simulation(dut, check)', 172 => false, 173 => '         sim.run(quiet=1)', 174 => false, 175 => 'Обратите внимание на то, как проверка равенства', 176 => 'осуществляется с помощью метода self.assertEqual ,', 177 => 'определенного в unittest.TestCase.', 178 => false, 179 => 'Точно так же мы пишем тест для проверки второго требования.', 180 => 'Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из', 181 => 'кодировщика, сортируем их и проверяем совпадение с начальным набором.', 182 => ' '''def''' '''testUniqueCodeWords'''(self):', 183 => false, 184 => '     """ Check that all codewords occur exactly once """', 185 => false, 186 => '     '''def''' '''test'''(B, G, width):', 187 => false, 188 => '         actual '''=''' []', 189 => false, 190 => '         '''for''' i '''in''' range(2'''**'''width):', 191 => false, 192 => '             B'''.'''next '''=''' intbv(i)', 193 => false, 194 => '             '''yield''' delay(10)', 195 => false, 196 => '             actual'''.'''append(int(G))', 197 => false, 198 => '         actual'''.'''sort()', 199 => false, 200 => '         expected '''=''' range(2'''**'''width)', 201 => false, 202 => '         self'''.'''assertEqual(actual, expected)', 203 => false, 204 => '     '''for''' width '''in''' range(1, MAX_WIDTH):', 205 => false, 206 => '         B '''=''' Signal(intbv('''-'''1))', 207 => false, 208 => '         G '''=''' Signal(intbv(0))', 209 => false, 210 => '         dut '''=''' bin2gray(B, G, width)', 211 => false, 212 => '         check '''=''' test(B, G, width)', 213 => false, 214 => '         sim '''=''' Simulation(dut, check)', 215 => false, 216 => '         sim'''.'''run(quiet'''='''1)', 217 => false, 218 => 'Разработка от тестирования', 219 => false, 220 => 'Когда написаны тесты, мы начинаем реализацию. Для', 221 => 'демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и', 222 => 'посмотрим, как тесты будут себя вести. ', 223 => ' unittest'''.'''main()', 224 => 'Давайте запустим этот тест, используя неработающий, еще', 225 => 'пустой объект кодировщика, показанный выше:', 226 => ' % python test_gray.py -v', 227 => false, 228 => ' Check that only one bit changes in successive codewords ... FAIL', 229 => false, 230 => ' Check that all codewords occur exactly once ... FAIL', 231 => false, 232 => ' <trace backs not shown>', 233 => false, 234 => 'Как и ожидалось, он полностью проваливается. Теперь', 235 => 'попытаемся протестировать неправильную реализацию, которая выдает последний бит', 236 => 'числа (то есть удовлетворяет одному условию):', 237 => ' '''def''' '''bin2gray'''(B, G, width):', 238 => false, 239 => '     ''### INCORRECT - DEMO PURPOSE ONLY! ###''', 240 => false, 241 => '     @always_comb', 242 => false, 243 => '     '''def''' '''logic'''():', 244 => false, 245 => '         G'''.'''next '''=''' B[0]', 246 => false, 247 => '     '''return''' logic', 248 => 'Запуск тестов дает нам:', 249 => ' % python test_gray.py -v', 250 => false, 251 => ' Check that only one bit changes in successive codewords ... ok', 252 => false, 253 => ' Check that all codewords occur exactly once ... FAIL', 254 => false, 255 => ' ======================================================================', 256 => false, 257 => ' FAIL: Check that all codewords occur exactly once', 258 => false, 259 => ' ----------------------------------------------------------------------', 260 => false, 261 => ' Traceback (most recent call last):', 262 => false, 263 => '   File "test_gray.py", line 109, in testUniqueCodeWords', 264 => false, 265 => '     sim.run(quiet=1)', 266 => false, 267 => ' ...', 268 => false, 269 => '   File "test_gray.py", line 104, in test', 270 => false, 271 => '     self.assertEqual(actual, expected)', 272 => false, 273 => '   File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual', 274 => false, 275 => '     raise self.failureException, \', 276 => false, 277 => ' AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3]', 278 => false, 279 => ' ----------------------------------------------------------------------', 280 => false, 281 => ' Ran 2 tests in 0.785s', 282 => false, 283 => 'Теперь проходятся тесты, проверяющие первое из требований,', 284 => 'но не проходятся - проверяющие второе. Нужно смотреть сообщения, которые выдает', 285 => 'тест, чтобы понять, что не так с нашим кодом.', 286 => false, 287 => 'В конце концов, если мы тестируем правильную версию bin2gray:', 288 => ' '''def''' '''bin2gray'''(B, G, width):', 289 => false, 290 => '     """ Gray encoder.', 291 => false, 292 => '     B -- input intbv signal, binary encoded', 293 => false, 294 => '     G -- output intbv signal, gray encoded', 295 => false, 296 => '     width -- bit width', 297 => false, 298 => '     """', 299 => false, 300 => '     @always_comb', 301 => false, 302 => '     '''def''' '''logic'''():', 303 => false, 304 => '         '''for''' i '''in''' range(width):', 305 => false, 306 => '             G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i]', 307 => false, 308 => '     '''return''' logic', 309 => 'Вывод получается такой:', 310 => ' % python test_gray.py -v', 311 => false, 312 => ' Check that only one bit changes in successive codewords ... ok', 313 => false, 314 => ' Check that all codewords occur exactly once ... ok', 315 => false, 316 => ' ----------------------------------------------------------------------', 317 => false, 318 => ' Ran 2 tests in 6.364s', 319 => false, 320 => ' OK', 321 => false, 322 => '==== Изменение требований: ====', 323 => 'В предыдущей главе мы сосредоточились на основных', 324 => 'требованиях к коду Грея. Их возможно определить не глядя на код самого', 325 => 'устройства. Легко увидеть, что есть не одно решение (в виде программного кода', 326 => 'модуля кодировщика), проходящее эти требования. В хорошем экстремальном', 327 => 'программировании мы проверяем только требования и ничего больше.', 328 => false, 329 => 'Может случиться так, что нужно больше контроля. Например,', 330 => 'требования могут заключаться и в том, чтобы он выдавал заданные коды, а не', 331 => 'удовлетворял двум общим признакам. Например, покажем, как тестировать код Грея,', 332 => 'который является частным случаем, который удовлетворяет этим условиям (что', 333 => 'описаны в предыдущей главе). В этом случае этот тест будет действительно проще', 334 => 'предыдущего. ', 335 => false, 336 => 'Мы обозначим код Грея длины n за Ln. Примеры:', 337 => ' L1 = ['0', '1']', 338 => false, 339 => ' L2 = ['00', '01', '11', '10']', 340 => false, 341 => ' L3 = ['000', '001', '011', '010', '110', '111', '101', 100']', 342 => 'Можно определить эти коды рекурсивным алгоритмом, например', 343 => 'вот так:', 344 => false, 345 => '1.    L1 = [‘0’, ‘1’]', 346 => false, 347 => '2.   ', 348 => 'Ln+1 может быть получен', 349 => 'из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому', 350 => 'кодовому слову Ln  ‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1.', 351 => false, 352 => 'Python хорошо известен как', 353 => 'язык с красивыми описаниями алгоритмов, и это хороший тому пример. Вот как', 354 => 'выглядит этот алгоритм на Python:', 355 => false, 356 => ''''def''' '''nextLn'''(Ln):', 357 => false, 358 => '    """', 359 => 'Return Gray code Ln+1, given Ln. """', 360 => false, 361 => 'Ln0 '''=''' ['0' '''+''' codeword '''for''' codeword '''in''' Ln]', 362 => false, 363 => 'Ln1 '''=''' ['1' '''+''' codeword '''for''' codeword '''in''' Ln]', 364 => false, 365 => 'Ln1'''.'''reverse()', 366 => false, 367 => ''''return'''', 368 => 'Ln0 '''+''' Ln1', 369 => false, 370 => 'Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков,', 371 => 'создаваемых с помощью цикла с простыми действиями. ', 372 => false, 373 => 'Сейчас требования состоят в том, что код', 374 => 'точно соответствует Ln. Мы используем', 375 => 'функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так:', 376 => false, 377 => ''''class''' '''TestOriginalGrayCode'''(TestCase):', 378 => false, 379 => ''''def''' '''testOriginalGrayCode'''(self):', 380 => false, 381 => '""" Check that the code is an original Gray code', 382 => '"""', 383 => false, 384 => 'Rn '''=''' []', 385 => false, 386 => ''''def'''', 387 => ''''stimulus'''(B, G, n):', 388 => false, 389 => ''''for'''', 390 => 'i '''in''' range(2'''**'''n):', 391 => false, 392 => 'B'''.'''next '''=''' intbv(i)', 393 => false, 394 => ''''yield'''', 395 => 'delay(10)', 396 => false, 397 => 'Rn'''.'''append(bin(G, width'''='''n))', 398 => false, 399 => 'Ln '''=''' ['0', '1'] ''# n == 1''', 400 => false, 401 => ''''for'''', 402 => 'n '''in''' range(2, MAX_WIDTH):', 403 => false, 404 => 'Ln '''=''' nextLn(Ln)', 405 => false, 406 => ''''del'''', 407 => 'Rn[:]', 408 => false, 409 => 'B '''=''' Signal(intbv('''-'''1))', 410 => false, 411 => 'G '''=''' Signal(intbv(0))', 412 => false, 413 => 'dut '''=''' bin2gray(B, G, n)', 414 => false, 415 => 'stim '''=''' stimulus(B, G, n)', 416 => false, 417 => 'sim '''=''' Simulation(dut, stim)', 418 => false, 419 => 'sim'''.'''run(quiet'''='''1)', 420 => false, 421 => 'self'''.'''assertEqual(Ln, Rn)', 422 => false, 423 => 'Если так сделать, то реализованное нами', 424 => 'устройство выдает действительно настоящий код Грея.', 425 => false, 426 => '%python test_gray.py -v TestOriginalGrayCode', 427 => false, 428 => 'Check that the code is an original Gray code ... ok', 429 => false, 430 => '<nowiki>----------------------------------------------------------------------</nowiki>', 431 => false, 432 => 'Ran 1 tests in 3.091s', 433 => false, 434 => 'OK', 435 => false, 436 => '== Одновременная с верилогом симуляция, Сосимуляция(Co-simulation) ==', 437 => false, 438 => '=== Введение ===', 439 => 'Одна из наиболее важных возможностей MyHDL заключается в том, что его можно использовать для верификации аппаратных', 440 => 'устройств, написанных на Verilog и VHDL, писать с его помощью тесты и контролировать симуляцию.', 441 => false, 442 => 'В наши дни известно, что язык,', 443 => 'предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать', 444 => 'современные технологии прораммирования, такие, как обьектно-ориентированность.', 445 => 'Все потому, что верификация – это наиболее сложная и затратная по времени часть', 446 => 'разработки аппаратного обеспечения. Следовательно, ценна любая новая', 447 => 'технология. Более того, тесты не обязательно должны быть имплементируемыми', 448 => '(реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода,', 449 => 'они не имеют ограничений в реализации.', 450 => false, 451 => 'Технически верификация аппаратного', 452 => 'обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В', 453 => 'данный момент MyHDL включает PLI-модуль для двух', 454 => 'симуляторов: Icarus и Cver.', 455 => false, 456 => '=== На стороне HDL ===', 457 => 'Чтобы продемонстрировать сосимуляцию мы будем использовать', 458 => 'снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать', 459 => 'его, написав для этого на Verilog.', 460 => 'Конечно же, мы захотим снова использовать те же тесты, что были для него уже', 461 => 'написаны.', 462 => false, 463 => 'Для начала давайте освежим в памяти, как выглядит кодировщик', 464 => 'на MyHDL:', 465 => false, 466 => ''''def''' '''bin2gray'''(B, G, width):', 467 => false, 468 => '""" Gray encoder.', 469 => false, 470 => 'B -- input', 471 => 'intbv signal, binary encoded', 472 => false, 473 => 'G -- output', 474 => 'intbv signal, gray encoded', 475 => false, 476 => 'width -- bit', 477 => 'width', 478 => false, 479 => '"""', 480 => false, 481 => '@always_comb', 482 => false, 483 => ''''def''' '''logic'''():', 484 => false, 485 => ''''for'''', 486 => 'i '''in''' range(width):', 487 => false, 488 => 'G'''.'''next[i] '''=''' B[i'''+'''1] '''^''' B[i]', 489 => false, 490 => ''''return''' logic', 491 => false, 492 => 'Чтобы показать, что происходит при симуляции, нам не нужна', 493 => 'реализация на Verilog,', 494 => 'а нужен только ее интерфейс. Кодировщик будет иметь следующий интерфейс:', 495 => false, 496 => 'module bin2gray(B, G);', 497 => false, 498 => '   parameter width = 8;', 499 => false, 500 => '   input', 501 => '[width-1:0]  B;', 502 => false, 503 => 'output', 504 => '[width-1:0] G;', 505 => false, 506 => '....', 507 => false, 508 => 'Чтобы написать тесты кто-то создает новый модуль, который', 509 => 'представляет собой Design Under Test', 510 => '(DUT). Тестбенч', 511 => 'определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых', 512 => 'сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том', 513 => 'же HDL, но мы будем', 514 => 'писать их не на HDL, а на MyHDL.', 515 => 'чтобы соединить их с входами Verilog-модуля', 516 => 'нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции', 517 => 'управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере', 518 => 'этоделается так:', 519 => false, 520 => 'module dut_bin2gray;', 521 => false, 522 => '   reg [`width-1:0] B;', 523 => false, 524 => '   wire', 525 => '[`width-1:0] G;', 526 => false, 527 => 'initial begin', 528 => false, 529 => '$from_myhdl(B);', 530 => false, 531 => '$to_myhdl(G);', 532 => false, 533 => 'end', 534 => false, 535 => 'bin2gray dut', 536 => '(.B(B), .G(G));', 537 => false, 538 => 'defparam dut.width = `width;', 539 => false, 540 => 'endmodule', 541 => false, 542 => '$from_myhdl говорит о том, что эти провода', 543 => 'принимают сигналы от MyHDL,', 544 => 'а $to_myhdl означает, что данные сигналы будут', 545 => 'считаны тестовым окружением MyHDL.', 546 => 'Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого', 547 => 'поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле', 548 => 'myhdl.vpi , который скомпилирован из кода, написанного', 549 => 'на С.', 550 => false, 551 => '=== На стороне MyHDL ===', 552 => 'MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать,', 553 => 'как запускать симуляцию HDL.', 554 => 'следовательно, первый аргумент для его конструктора – это командная строка для', 555 => 'исполнения симуляции.', 556 => false, 557 => 'Способ генерации и запуска исполняемого файла симуляции', 558 => 'зависит от симулятора. Например, в Icarus Verilog исполняемый файл для нашего', 559 => 'примера может быть получен посредством запуска компилятора iverilog, как показано ниже:', 560 => false, 561 => '% iverilog -o bin2gray -Dwidth=4 bin2gray.v', 562 => 'dut_bin2gray.v', 563 => false, 564 => 'Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов.', 565 => false, 566 => 'Симуляция его запускается с помощью команды vvp:', 567 => false, 568 => '% vvp -m ./myhdl.vpi', 569 => 'bin2gray', 570 => false, 571 => 'Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример', 572 => 'использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation)', 573 => false, 574 => 'Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы', 575 => 'пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так:', 576 => false, 577 => ''''import''' os', 578 => false, 579 => ''''from''' myhdl '''import''' Cosimulation', 580 => false, 581 => 'cmd '''=''' "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v"', 582 => false, 583 => ''''def''' '''bin2gray'''(B, G, width):', 584 => false, 585 => 'os'''.'''system(cmd '''%''' width)', 586 => false, 587 => ''''return'''', 588 => 'Cosimulation("vvp -m ./myhdl.vpi', 589 => 'bin2gray", B'''='''B, G'''='''G)', 590 => false, 591 => 'После аргумента-исполняемой команды Cosimulation принимает', 592 => 'все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти', 593 => 'имена указаны в $to_myhdl и', 594 => '$from_myhdl. Аргументы этих функций – сигналы', 595 => 'из MyHDL.', 596 => false, 597 => 'Когда мы изучили всё это, мы можем попробовать использовать', 598 => 'существующий юнит-тест для верификации Verilog-описания. Обратите внимание, что мы используем одинаковые', 599 => 'имена и параметры для функции bin2gray: все, что нам нужно, -', 600 => 'это обеспечить другое определение существующему юнит-тесту.', 601 => false, 602 => 'Взглянем на этот Verilog-проект:', 603 => false, 604 => 'module bin2gray(B, G);', 605 => false, 606 => 'parameter', 607 => 'width = 8;', 608 => false, 609 => 'input', 610 => '[width-1:0]  B;', 611 => false, 612 => 'output', 613 => '[width-1:0] G;', 614 => false, 615 => 'reg', 616 => '[width-1:0] G;', 617 => false, 618 => 'integer i;', 619 => false, 620 => 'wire', 621 => '[width:0] extB;', 622 => false, 623 => 'assign extB =', 624 => '{1'b0, B}; // zero-extend input', 625 => false, 626 => 'always', 627 => '@(extB) begin', 628 => false, 629 => 'for (i=0;', 630 => 'i < width; i=i+1)', 631 => false, 632 => 'G[i]', 633 => '<= extB[i+1] ^ extB[i];', 634 => false, 635 => 'end', 636 => false, 637 => 'endmodule', 638 => false, 639 => 'Если мы запустим тест, мы получим: ', 640 => false, 641 => '% python test_bin2gray.py', 642 => false, 643 => 'Check that only one bit changes in successive', 644 => 'codewords ... ok', 645 => false, 646 => 'Check that all codewords occur exactly once ... ok', 647 => false, 648 => 'Check that the code is an original Gray code ... ok', 649 => false, 650 => '<nowiki>----------------------------------------------------------------------</nowiki>', 651 => false, 652 => 'Ran 3 tests in 2.729s', 653 => false, 654 => 'OK', 655 => false, 656 => '=== Ограничения ===', 657 => 'В идеальной ситуации можно симулировать любое HDL-описание с помощью MyHDL, не прилагая', 658 => 'дополнительных усилий. К тому же соединенные сигналы с обеих сторон работают', 659 => 'как один, делая границу между тестовым окружением и модулем абсолютно', 660 => 'прозрачной.', 661 => false, 662 => 'По некоторым причинам невозможно добиться полной общности.', 663 => 'Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения', 664 => 'отдельного симулятора и различия между разными симуляторами могут быть', 665 => 'ошеломляющими. К тому же полная универсальность может требовать', 666 => 'непропорционального времени под разработку по сравнению с немного менее общим', 667 => 'решением, которого может быть достаточно для данной задачи.', 668 => false, 669 => 'Далее, я попробовал достичь решения, которое будет', 670 => 'достаточно простым, чтобы можно было довольно уверенно ожидать, что любой', 671 => 'симулятор, поддерживающий PLI,', 672 => 'сможет его поддерживать и чтобы было довольно просто верифицировать его и', 673 => 'поддерживать. В то же время, решение достаточно общее, чтобы заполнить всю', 674 => 'область ожидаемого применения.', 675 => false, 676 => 'Результатом компромисса было наложение на HDL нескольких', 677 => 'ограничений. В этой главе будет рассказано о них.', 678 => false, 679 => '==== Только пассивный HDL может быть сосимулирован. ====', 680 => 'Наиболее важное ограничение MyHDL на', 681 => 'сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк', 682 => 'с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В', 683 => 'частности, сигнал clk', 684 => 'должен быть сгенерирован на стороне MyHDL. ', 685 => false, 686 => 'Сначала это кажется важным ограничением, но если рассмотреть', 687 => 'реальные случаи, оно оказывается не очень важным.', 688 => false, 689 => 'MyHDL поддерживает сосимуляцию так, что тесты должны быть именно', 690 => 'на Python. Давайте', 691 => 'рассмотрим природу тестируемых HDL-проектов.', 692 => 'Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в', 693 => 'плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом.', 694 => 'Это и есть одна из важных задач MyHDL.', 695 => 'Точно так же прошиваемые проекты с заявленными временными ограничениями – не', 696 => 'то, что нас интересует: статический анализ тайминга – намного более хороший', 697 => 'способ для таких проектов.', 698 => false, 699 => 'Итог: HDL-описания,', 700 => 'которые нас интересуют – это реальные модели аппаратуры, которые будут', 701 => 'синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы', 702 => 'в синтезируемом коде не важны(нет строго заданного в проекте тайминга),', 703 => 'ограничение не влияет на выполнение наших задач.', 704 => false, 705 => '==== 1.      Гонка ====', 706 => 'В обычном RTL', 707 => 'некоторые события иногда вызывают другие события на том же clk (на том же временном интервале).', 708 => 'Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика,', 709 => 'насколько я помню). This is done by the', 710 => 'concept of “delta” cycles. In a fully general, race free co-simulation, the', 711 => 'co-simulators would communicate at the level of delta cycles. However, in MyHDL', 712 => 'co-simulation, this is not entirely the case.', 713 => false, 714 => 'Дельта-циклы из MyHDL-симулятора в HDL-симулятор', 715 => 'запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются', 716 => 'в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе.', 717 => false, 718 => 'Что это значит? Давайте начнем с', 719 => 'хооших новостей. Как уже объяснялось в предыдущей главе, концепция MyHDL предусматривает, что clk', 720 => 'генерируется на стороне MyHDL. когда мы', 721 => 'используем clk от MyHDL и', 722 => 'соответствующий ему сигнал в HDL, сосимуляция', 723 => 'не может иметь гонок. То есть используя полность подход MyHDL, мы не можем столкнуться с гонкой.', 724 => false, 725 => 'Другая ситуация наступает, когда', 726 => 'мы хотим использовать сигнал, который управляется из HDL (и', 727 => 'соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно', 728 => 'изменять сигналы в MyHDL со сдвигом по времени, избегая', 729 => 'таким образом возможность произвольного взаиморасположения положительного', 730 => 'фронта clk и события, избегая таким образом гонки.', 731 => false, 732 => 'ЕЩЕ ТУТ БЫЛО ЧТО-ТО.', 733 => false, 734 => '== Трансляция в Verilog и VHDL ==', 735 => false, 736 => '=== Введение ===', 737 => 'Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL.', 738 => false, 739 => 'Эта глава описывает основы этой', 740 => 'трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. <nowiki>http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage</nowiki>', 741 => false, 742 => '=== Описание продукта ===', 743 => 'Чтобы быть конвертируемым, проект', 744 => 'должен удовлетворять некоторым ограничениям, которые мы будем называть', 745 => 'конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте', 746 => '«Конвертируемое подмножество».', 747 => false, 748 => 'Конвертируемый проект может быть', 749 => 'сконвертирован в эквивалентную модель на Verilog или VHDL с помощью', 750 => 'функций toVerilog(), toVHDL() библиотеки', 751 => 'MyHDL.', 752 => false, 753 => 'Когда проект предназначается для', 754 => 'имплементации, синтез производит другое средство, предназначенное для', 755 => 'синтезирования из Verilog и VHDL', 756 => 'проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести', 757 => 'проект, написанный на Python, до уровня прошивки платы.', 758 => false, 759 => 'Конверсия начинается не с', 760 => 'исходного кода, а с инстанцированного проекта, который воспринимается', 761 => 'интерпретатором Python. Конвертер использует', 762 => 'профилировщик Питона, чтобы понимать все операции интерпретатора Питона и чтобы', 763 => 'определить структуру проекта и пространство имен. Далее он выборочно', 764 => 'компилирует части исходного кода для дополнительного анализа и для конверсии.', 765 => false, 766 => '=== Особенности ===', 767 => false, 768 => '==== Конверсия после обработки ====', 769 => 'Под обработкой в данном случае понимается изначальная', 770 => 'обработка проекта для достижения представления о устройстве, в том числе, может', 771 => 'ли оно быть сконвертировано, синтезировано и имплементировано. В частности,', 772 => 'структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для', 773 => 'обработки используется интерпретатор Питона. Объект Simulation строится', 774 => 'на основе своих аргументов - обработанных экземпляров. Таким же образом', 775 => 'конверсия работает с уже собранным экземпляром всего проекта. Таким образом,', 776 => 'интерпретатор Питона используется максимально активно.', 777 => false, 778 => '==== Обработка структур любой сложности ====', 779 => 'Так как конверсия работает с уже обработанными экземплярами,', 780 => 'все условия(constraints)', 781 => 'моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами,', 782 => 'нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина', 783 => 'иерархии, естественно, тоже не ограничена. ', 784 => false, 785 => '==== Генераторы переводятся в Verilog и VHDL ====', 786 => 'Конвертор анализирует код каждого генератора и переводит его', 787 => 'в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки.', 788 => 'В VHDL они будут', 789 => 'переведены в process-блоки', 790 => 'одновременных присваиваний сигналов.', 791 => false, 792 => '==== Назначение портов определяется в зависимости от сигналов ====', 793 => 'В MyHDL', 794 => 'нет понятия направления порта, нет входных и выходных портов, определенных', 795 => 'строго и единожды. Конвертер проверяет, как используется сигнал: как входной', 796 => 'порт, выходной порт или как внутренний сигнал.', 797 => false, 798 => '==== Интерфейсы конвертируемы ====', 799 => 'Интерфейс – это объект с несколькими сигналами и их', 800 => 'свойствами. Конвертер поддерживает их с помощью имен и mangling.', 801 => false, 802 => '==== Вызов функций преобразуется в Verilog и VHDL-подпрограммы ====', 803 => 'Конвертер анализирует функцию и ее код. Каждая функция', 804 => 'переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы', 805 => 'поддерживать возможности функций в Питоне полностью, на каждый вызов функции в', 806 => 'Питоне генерируется отдельная подпрограмма', 807 => false, 808 => '==== if-then-else-структуры могут быть переведены в case-блоки ====', 809 => 'Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции,', 810 => 'где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими', 811 => 'свойствами в случае синтеза (вероятно, мультиплексор – авт.)', 812 => false, 813 => '==== Выбор способов перевода enum’ов – перечислимых типов ====', 814 => 'Функция enum()', 815 => 'в MyHDL возвращает', 816 => 'перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет', 817 => 'способ ее перевода в HDL:', 818 => 'бинарный, one hot,', 819 => 'или one cold.', 820 => false, 821 => '==== Память с произвольным доступом RAM ====', 822 => 'Большинство средств синтеза могут переводить Verilog и', 823 => 'VHDL, определяющий', 824 => 'блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию,', 825 => 'конвертер преобразует list из Signal’ов', 826 => 'в Verilog-памяти или VHDL-массивы.', 827 => false, 828 => '==== Память ROM ====', 829 => 'Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает', 830 => 'перевод в такие конструкции автоматически, основываясь на высокоуровневом', 831 => 'описании. ROM', 832 => 'описывается одной строчкой, посредством индексирования tuple’а.', 833 => false, 834 => '==== Знаковая арифметика ====', 835 => 'В MyHDL', 836 => 'работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми', 837 => 'ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и', 838 => 'беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными', 839 => 'числами, пользователь должен строго указать, что эта переменная – знаковая. Но', 840 => 'когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё', 841 => 'становится сложнее.', 842 => false, 843 => 'Когда в Verilog', 844 => 'в одном выражении есть знаковые и беззнаковые числа, все считаются', 845 => 'беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик', 846 => 'вынужден будет добавлять знаковые расширения и преобразования типов для решения', 847 => 'этой проблемы.', 848 => false, 849 => 'В VHDL', 850 => 'знаковые и беззнаковые числа в одном выражении просто не будут работать.', 851 => 'Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только', 852 => 'после этого включать в выражение.', 853 => false, 854 => 'В MyHDL', 855 => 'эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того,', 856 => 'конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и  VHDL. Он использует знаковые и беззнаковые типы в зависимости от', 857 => 'ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя', 858 => 'граница, верхняя граница=верхняя граница). ', 859 => false, 860 => '==== Пользовательский код ====', 861 => 'Если необходимо, пользователь может обходить конверсию и', 862 => 'писать свой собственный код, который вставится непосредственно в конченый', 863 => 'проект.', 864 => false, 865 => '== Конвертируемое множество ==', 866 => false, 867 => '=== Введение ===', 868 => 'Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не', 869 => 'сильно значительные, конвертируемое подмножество намного шире, чем', 870 => 'синтезируемое подмножество, которое является производственным стандартом.', 871 => 'Другими словами, код на MyHDL,', 872 => 'написанный в соответствии с правилами для синтезируемых устройств, будет', 873 => 'конвертируемым. Хотя возможно также написать конвертируемый код для', 874 => 'несинтезируемых моделей и тестбенчей. ', 875 => false, 876 => 'Конвертер пытается выдавать понятные сообщения об ошибках,', 877 => 'когда натыкается на конструкцию, которую сконвертировать невозможно.', 878 => false, 879 => '=== Кодстайл ===', 880 => 'Действительно важное ограничение на конвертируемый код – это', 881 => 'то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку', 882 => 'или по другому сигналу.', 883 => false, 884 => 'Для чистого моделирования неважно, какие генераторы Вы', 885 => 'используете. Хотя в конвертируемом коде они должны быть созданы с помощью', 886 => 'декораторов из MyHDL:   '''instance()''', '''always()''', '''always_seq()''', или '''always_comb()'''. ', 887 => false, 888 => '=== Поддерживаемые типы ===', 889 => 'Наиболее значимое ограничение', 890 => 'накладывается на типы. Только ограниченное количество типов может быть', 891 => 'скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены', 892 => 'длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL.', 893 => false, 894 => 'Объекты Intbv должны быть', 895 => 'сконструированы с той длиной, которую они определяют сами с помощью нижнего и', 896 => 'верхнего ограничения, которое задаем мы сами:', 897 => false, 898 => 'index '''=''' intbv(0, min'''='''MIN, max'''='''MAX)', 899 => false, 900 => 'Verilog-конвертер поддерживает intbv с отрицательными значениями. ', 901 => false, 902 => 'Еще можно создавать его с помощью', 903 => 'слайсинга вот так:', 904 => false, 905 => 'index '''=''' intbv(0)[N:]', 906 => false, 907 => 'Это выражение даст нам', 908 => 'число 0 с максимальным значением 2**N, то есть длиной N.', 909 => false, 910 => 'Конвертер вообще', 911 => 'поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) http://docs.myhdl.org/en/stable/manual/index.html' ]
Удалённые в правке строки ($1) (removed_lines)
[]
Новый текст страницы, очищенный от разметки ($1) (new_text)
' Содержание 1 Юнит-тестирование 1.1 Введение 1.2 Важность юнит-тестирования 1.3 Разработка юнит-тестов. 1.3.1 Определение требований 1.3.2 Изменение требований: 2 Одновременная с верилогом симуляция, Сосимуляция(Co-simulation) 2.1 Введение 2.2 На стороне HDL 2.3 На стороне MyHDL 2.4 Ограничения 2.4.1 Только пассивный HDL может быть сосимулирован. 2.4.2 1.&#160;&#160;&#160;&#160;&#160; Гонка 3 Трансляция в Verilog и VHDL 3.1 &#160;Введение 3.2 Описание продукта 3.3 Особенности 3.3.1 &#160;Конверсия после обработки 3.3.2 Обработка структур любой сложности 3.3.3 Генераторы переводятся в Verilog и VHDL 3.3.4 Назначение портов определяется в зависимости от сигналов 3.3.5 Интерфейсы конвертируемы 3.3.6 Вызов функций преобразуется в Verilog и VHDL-подпрограммы 3.3.7 if-then-else-структуры могут быть переведены в case-блоки 3.3.8 Выбор способов перевода enum’ов – перечислимых типов 3.3.9 Память с произвольным доступом RAM 3.3.10 Память ROM 3.3.11 Знаковая арифметика 3.3.12 Пользовательский код 4 Конвертируемое множество 4.1 Введение 4.2 Кодстайл 4.3 Поддерживаемые типы Юнит-тестирование[править | править вики-текст] Введение[править | править вики-текст] Многие аспекты разработки современных цифровых аппаратных средств могут быть рассмотрены как отдельное направление разработки ПО. Рассматривая ситуацию с такой точки зрения, это спорный вопрос – могут ли технологии, применимые в разработке ПО, применяться в разработке аппаратного обеспечения. Есть подход к программированию, который стал популярен относительно недавно. Он называется «Экстремальное программирование» (Extreme Programming, ХР). Это впечатляющий набор способов и руководств, который идет против традиционных подходов к разработке. В других случаях экстремальное программирование будто бы подчеркивает здравый смысл, который не всегда присутствует в стандартных методах. Например, экстремальное программирование делает упор на рабочую неделю, что позволяет иметь всегда свежую голову. Это необходимо для качественной разработки. Эта статья не является попыткой написать учебник по экстремальному программированию. Наоборот, в этой статье я подчеркну лишь одну черту экстремального программирования, которая необходима в аппаратной разработке: важность юнит-тестирования и его методики. Важность юнит-тестирования[править | править вики-текст] Юнит-тестирование – это один из краеугольных камней экстремального программирования. Другие идеи этой технологии, такие как коллективное владение кодом и непрерывное повышение качества возможны тогда и только тогда, когда реализовано юнит-тестирование. Более того, экстремальное программирование подчеркивает, что написание юнит-тестов должно быть автоматизировано, что должны быть протестированы все элементы всех классов и они должны работать идеально всё время. Я верю, что эта концепция применима как раз в аппаратной разработке. К томуже, юнит-тесты – это способ управлять временем симуляции. Например, верификация конечного автомата, который очень медленно работает на редких событиях, может быть затруднена или невозможна с учетом ограниченности времени, даже на самом быстром симуляторе. Понятно, что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны, если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно, в теории экстремального программирования подчеркивается необходимость стандартного юнит-тестирования, которое поддерживает все описанные задачи. В этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для аппаратных проектов. Разработка юнит-тестов.[править | править вики-текст] В этой главе мы будем неформально изучать применение юнит-тестирования к аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем тестировать устройство, переводящее двоичный код в код Грея. Это устройство было описано в разделе Bit indexing. Определение требований[править | править вики-текст] Мы начинаем с определения требований. Для кодировщика в код Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея. Давайте определим код как список кодовых слов, где кодовое слово – это строка из битов. Код длины n может определять 2**n слов. Широко известные характеристики – это то, для чего предназначен код Грея: Последовательные кодовые слова в коде Грея должны отличаться ровно в одном бите. Этого достаточно? Не совсем. Например, под наши нынешние требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это, очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода Грея превосходила длину битового слова. Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно одному слову в коде Грея такого же порядка. Когда написаны требования, мы можем продолжать. Сначала – тесты Отличная идея экстремального программирования состоит в том, что нужно писать юнит-тесты до написания кода. Таким образом, до того, как реализовывать что-то, сначала напишите тест, который это что-то должно проходить. Это противоречит стандартной практике и вообще логичному ходу событий. Многие разработчики предпочитают в первую очередь создать работающий продукт, а после этого уже ее тестировать. Но если подумать, что логично сделать верификацию сначала. Верификация – это просто условия. Таким образом, ваши мысли еще не заняты деталями реализации. Юнит-тесты – это работающая реализация описания и требований, поэтому реализация их в коде позволяет их более хорошо понять, поэтому это стоит сделать. После этого реализация будет проще. Возможно, самым важным является то, что тесты доступны уже во время разработки, и их может запускать любой из разработчиков проекта, чтобы проверять свои изменения. Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий юнит-тесты. С unittest тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются модулем как тесты из комплекта тестов. Мы зададим тесты для требований кода Грея, после чего напишем тесты для каждого из требований. Итогом создания комплекта тестов таков: from unittest import TestCase class TestGrayCodeProperties(TestCase): def testSingleBitChange(self): """ Check that only one bit changes in successive codewords """ .... def testUniqueCodeWords(self): """ Check that all codewords occur exactly once """ .... Каждый метод будет небольшим тестом, который проверяет заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую реализацию, включающую только интерфейс: def bin2gray(B, G, width): ### NOT IMPLEMENTED YET! ### yield None Для первого требования мы напишем тест, который подает на вход последовательно все цифры, а потом сравнивает между собой выходные данные кодировщика и проверяет, что действительно последовательные слова отличаются на один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного порядка MAX_WIDTH. def testSingleBitChange(self): """ Check that only one bit changes in successive codewords """ def test(B, G, width): B.next = intbv(0) yield delay(10) for i in range(1, 2**width): G_Z.next = G B.next = intbv(i) yield delay(10) diffcode = bin(G ^ G_Z) self.assertEqual(diffcode.count('1'), 1) for width in range(1, MAX_WIDTH): B = Signal(intbv(-1)) G = Signal(intbv(0)) G_Z = Signal(intbv(0)) dut = bin2gray(B, G, width) check = test(B, G, width) sim = Simulation(dut, check) sim.run(quiet=1) Обратите внимание на то, как проверка равенства осуществляется с помощью метода self.assertEqual , определенного в unittest.TestCase. Точно так же мы пишем тест для проверки второго требования. Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из кодировщика, сортируем их и проверяем совпадение с начальным набором. def testUniqueCodeWords(self): """ Check that all codewords occur exactly once """ def test(B, G, width): actual = [] for i in range(2**width): B.next = intbv(i) yield delay(10) actual.append(int(G)) actual.sort() expected = range(2**width) self.assertEqual(actual, expected) for width in range(1, MAX_WIDTH): B = Signal(intbv(-1)) G = Signal(intbv(0)) dut = bin2gray(B, G, width) check = test(B, G, width) sim = Simulation(dut, check) sim.run(quiet=1) Разработка от тестирования Когда написаны тесты, мы начинаем реализацию. Для демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и посмотрим, как тесты будут себя вести. unittest.main() Давайте запустим этот тест, используя неработающий, еще пустой объект кодировщика, показанный выше: % python test_gray.py -v Check that only one bit changes in successive codewords ... FAIL Check that all codewords occur exactly once ... FAIL &lt;trace backs not shown&gt; Как и ожидалось, он полностью проваливается. Теперь попытаемся протестировать неправильную реализацию, которая выдает последний бит числа (то есть удовлетворяет одному условию): def bin2gray(B, G, width): ### INCORRECT - DEMO PURPOSE ONLY! ### @always_comb def logic(): G.next = B[0] return logic Запуск тестов дает нам: % python test_gray.py -v Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... FAIL ====================================================================== FAIL: Check that all codewords occur exactly once ---------------------------------------------------------------------- Traceback (most recent call last): File "test_gray.py", line 109, in testUniqueCodeWords sim.run(quiet=1) ... File "test_gray.py", line 104, in test self.assertEqual(actual, expected) File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual raise self.failureException, \ AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3] ---------------------------------------------------------------------- Ran 2 tests in 0.785s Теперь проходятся тесты, проверяющие первое из требований, но не проходятся - проверяющие второе. Нужно смотреть сообщения, которые выдает тест, чтобы понять, что не так с нашим кодом. В конце концов, если мы тестируем правильную версию bin2gray: def bin2gray(B, G, width): """ Gray encoder. B -- input intbv signal, binary encoded G -- output intbv signal, gray encoded width -- bit width """ @always_comb def logic(): for i in range(width): G.next[i] = B[i+1] ^ B[i] return logic Вывод получается такой: % python test_gray.py -v Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... ok ---------------------------------------------------------------------- Ran 2 tests in 6.364s OK Изменение требований:[править | править вики-текст] В предыдущей главе мы сосредоточились на основных требованиях к коду Грея. Их возможно определить не глядя на код самого устройства. Легко увидеть, что есть не одно решение (в виде программного кода модуля кодировщика), проходящее эти требования. В хорошем экстремальном программировании мы проверяем только требования и ничего больше. Может случиться так, что нужно больше контроля. Например, требования могут заключаться и в том, чтобы он выдавал заданные коды, а не удовлетворял двум общим признакам. Например, покажем, как тестировать код Грея, который является частным случаем, который удовлетворяет этим условиям (что описаны в предыдущей главе). В этом случае этот тест будет действительно проще предыдущего. Мы обозначим код Грея длины n за Ln. Примеры: L1 = ['0', '1'] L2 = ['00', '01', '11', '10'] L3 = ['000', '001', '011', '010', '110', '111', '101', 100'] Можно определить эти коды рекурсивным алгоритмом, например вот так: 1.&#160;&#160;&#160; L1 = [‘0’, ‘1’] 2.&#160;&#160;&#160; Ln+1 может быть получен из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому кодовому слову Ln &#160;‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1. Python хорошо известен как язык с красивыми описаниями алгоритмов, и это хороший тому пример. Вот как выглядит этот алгоритм на Python: def nextLn(Ln): &#160;&#160;&#160; """ Return Gray code Ln+1, given Ln. """ Ln0 = ['0' + codeword for codeword in Ln] Ln1 = ['1' + codeword for codeword in Ln] Ln1.reverse() return Ln0 + Ln1 Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков, создаваемых с помощью цикла с простыми действиями. Сейчас требования состоят в том, что код точно соответствует Ln. Мы используем функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так: class TestOriginalGrayCode(TestCase): def testOriginalGrayCode(self): """ Check that the code is an original Gray code """ Rn = [] def stimulus(B, G, n): for i in range(2**n): B.next = intbv(i) yield delay(10) Rn.append(bin(G, width=n)) Ln = ['0', '1'] # n == 1 for n in range(2, MAX_WIDTH): Ln = nextLn(Ln) del Rn[:] B = Signal(intbv(-1)) G = Signal(intbv(0)) dut = bin2gray(B, G, n) stim = stimulus(B, G, n) sim = Simulation(dut, stim) sim.run(quiet=1) self.assertEqual(Ln, Rn) Если так сделать, то реализованное нами устройство выдает действительно настоящий код Грея. %python test_gray.py -v TestOriginalGrayCode Check that the code is an original Gray code ... ok ---------------------------------------------------------------------- Ran 1 tests in 3.091s OK Одновременная с верилогом симуляция, Сосимуляция(Co-simulation)[править | править вики-текст] Введение[править | править вики-текст] Одна из наиболее важных возможностей MyHDL заключается в том, что его можно использовать для верификации аппаратных устройств, написанных на Verilog и VHDL, писать с его помощью тесты и контролировать симуляцию. В наши дни известно, что язык, предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать современные технологии прораммирования, такие, как обьектно-ориентированность. Все потому, что верификация – это наиболее сложная и затратная по времени часть разработки аппаратного обеспечения. Следовательно, ценна любая новая технология. Более того, тесты не обязательно должны быть имплементируемыми (реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода, они не имеют ограничений в реализации. Технически верификация аппаратного обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В данный момент MyHDL включает PLI-модуль для двух симуляторов: Icarus и Cver. На стороне HDL[править | править вики-текст] Чтобы продемонстрировать сосимуляцию мы будем использовать снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать его, написав для этого на Verilog. Конечно же, мы захотим снова использовать те же тесты, что были для него уже написаны. Для начала давайте освежим в памяти, как выглядит кодировщик на MyHDL: def bin2gray(B, G, width): """ Gray encoder. B -- input intbv signal, binary encoded G -- output intbv signal, gray encoded width -- bit width """ @always_comb def logic(): for i in range(width): G.next[i] = B[i+1] ^ B[i] return logic Чтобы показать, что происходит при симуляции, нам не нужна реализация на Verilog, а нужен только ее интерфейс. Кодировщик будет иметь следующий интерфейс: module bin2gray(B, G); &#160;&#160; parameter width = 8; &#160;&#160; input [width-1:0]&#160; B; output [width-1:0] G; .... Чтобы написать тесты кто-то создает новый модуль, который представляет собой Design Under Test (DUT). Тестбенч определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том же HDL, но мы будем писать их не на HDL, а на MyHDL. чтобы соединить их с входами Verilog-модуля нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере этоделается так: module dut_bin2gray; &#160;&#160; reg [`width-1:0] B; &#160;&#160; wire [`width-1:0] G; initial begin $from_myhdl(B); $to_myhdl(G); end bin2gray dut (.B(B), .G(G)); defparam dut.width = `width; endmodule $from_myhdl&#160;говорит о том, что эти провода принимают сигналы от MyHDL, а&#160;$to_myhdl&#160;означает, что данные сигналы будут считаны тестовым окружением MyHDL. Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле myhdl.vpi&#160;, который скомпилирован из кода, написанного на С. На стороне MyHDL[править | править вики-текст] MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать, как запускать симуляцию HDL. следовательно, первый аргумент для его конструктора – это командная строка для исполнения симуляции. Способ генерации и запуска исполняемого файла симуляции зависит от симулятора. Например, в Icarus Verilog исполняемый файл для нашего примера может быть получен посредством запуска компилятора iverilog, как показано ниже: % iverilog -o bin2gray -Dwidth=4 bin2gray.v dut_bin2gray.v Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов. Симуляция его запускается с помощью команды vvp: % vvp -m ./myhdl.vpi bin2gray Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation) Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так: import os from myhdl import Cosimulation cmd = "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v" def bin2gray(B, G, width): os.system(cmd % width) return Cosimulation("vvp -m ./myhdl.vpi bin2gray", B=B, G=G) После аргумента-исполняемой команды Cosimulation принимает все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти имена указаны в $to_myhdl и $from_myhdl. Аргументы этих функций – сигналы из MyHDL. Когда мы изучили всё это, мы можем попробовать использовать существующий юнит-тест для верификации Verilog-описания. Обратите внимание, что мы используем одинаковые имена и параметры для функции bin2gray: все, что нам нужно, - это обеспечить другое определение существующему юнит-тесту. Взглянем на этот Verilog-проект: module bin2gray(B, G); parameter width = 8; input [width-1:0]&#160; B; output [width-1:0] G; reg [width-1:0] G; integer i; wire [width:0] extB; assign extB = {1'b0, B}; // zero-extend input always @(extB) begin for (i=0; i &lt; width; i=i+1) G[i] &lt;= extB[i+1] ^ extB[i]; end endmodule Если мы запустим тест, мы получим: % python test_bin2gray.py Check that only one bit changes in successive codewords ... ok Check that all codewords occur exactly once ... ok Check that the code is an original Gray code ... ok ---------------------------------------------------------------------- Ran 3 tests in 2.729s OK Ограничения[править | править вики-текст] В идеальной ситуации можно симулировать любое HDL-описание с помощью MyHDL, не прилагая дополнительных усилий. К тому же соединенные сигналы с обеих сторон работают как один, делая границу между тестовым окружением и модулем абсолютно прозрачной. По некоторым причинам невозможно добиться полной общности. Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения отдельного симулятора и различия между разными симуляторами могут быть ошеломляющими. К тому же полная универсальность может требовать непропорционального времени под разработку по сравнению с немного менее общим решением, которого может быть достаточно для данной задачи. Далее, я попробовал достичь решения, которое будет достаточно простым, чтобы можно было довольно уверенно ожидать, что любой симулятор, поддерживающий PLI, сможет его поддерживать и чтобы было довольно просто верифицировать его и поддерживать. В то же время, решение достаточно общее, чтобы заполнить всю область ожидаемого применения. Результатом компромисса было наложение на HDL нескольких ограничений. В этой главе будет рассказано о них. Только пассивный HDL может быть сосимулирован.[править | править вики-текст] Наиболее важное ограничение MyHDL на сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В частности, сигнал clk должен быть сгенерирован на стороне MyHDL. Сначала это кажется важным ограничением, но если рассмотреть реальные случаи, оно оказывается не очень важным. MyHDL поддерживает сосимуляцию так, что тесты должны быть именно на Python. Давайте рассмотрим природу тестируемых HDL-проектов. Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом. Это и есть одна из важных задач MyHDL. Точно так же прошиваемые проекты с заявленными временными ограничениями – не то, что нас интересует: статический анализ тайминга – намного более хороший способ для таких проектов. Итог: HDL-описания, которые нас интересуют – это реальные модели аппаратуры, которые будут синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы в синтезируемом коде не важны(нет строго заданного в проекте тайминга), ограничение не влияет на выполнение наших задач. 1.&#160;&#160;&#160;&#160;&#160; Гонка[править | править вики-текст] В обычном RTL некоторые события иногда вызывают другие события на том же clk (на том же временном интервале). Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика, насколько я помню). This is done by the concept of “delta” cycles. In a fully general, race free co-simulation, the co-simulators would communicate at the level of delta cycles. However, in MyHDL co-simulation, this is not entirely the case. Дельта-циклы из MyHDL-симулятора в HDL-симулятор запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе. Что это значит? Давайте начнем с хооших новостей. Как уже объяснялось в предыдущей главе, концепция MyHDL предусматривает, что clk генерируется на стороне MyHDL. когда мы используем clk от MyHDL и соответствующий ему сигнал в HDL, сосимуляция не может иметь гонок. То есть используя полность подход MyHDL, мы не можем столкнуться с гонкой. Другая ситуация наступает, когда мы хотим использовать сигнал, который управляется из HDL (и соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно изменять сигналы в MyHDL со сдвигом по времени, избегая таким образом возможность произвольного взаиморасположения положительного фронта clk и события, избегая таким образом гонки. ЕЩЕ ТУТ БЫЛО ЧТО-ТО. Трансляция в Verilog и VHDL[править | править вики-текст] &#160;Введение[править | править вики-текст] Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL. Эта глава описывает основы этой трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage Описание продукта[править | править вики-текст] Чтобы быть конвертируемым, проект должен удовлетворять некоторым ограничениям, которые мы будем называть конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте «Конвертируемое подмножество». Конвертируемый проект может быть сконвертирован в эквивалентную модель на Verilog или VHDL с помощью функций toVerilog(), toVHDL() библиотеки MyHDL. Когда проект предназначается для имплементации, синтез производит другое средство, предназначенное для синтезирования из Verilog и VHDL проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести проект, написанный на Python, до уровня прошивки платы. Конверсия начинается не с исходного кода, а с инстанцированного проекта, который воспринимается интерпретатором Python. Конвертер использует профилировщик Питона, чтобы понимать все операции интерпретатора Питона и чтобы определить структуру проекта и пространство имен. Далее он выборочно компилирует части исходного кода для дополнительного анализа и для конверсии. Особенности[править | править вики-текст] &#160;Конверсия после обработки[править | править вики-текст] Под обработкой в данном случае понимается изначальная обработка проекта для достижения представления о устройстве, в том числе, может ли оно быть сконвертировано, синтезировано и имплементировано. В частности, структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для обработки используется интерпретатор Питона. Объект Simulation строится на основе своих аргументов - обработанных экземпляров. Таким же образом конверсия работает с уже собранным экземпляром всего проекта. Таким образом, интерпретатор Питона используется максимально активно. Обработка структур любой сложности[править | править вики-текст] Так как конверсия работает с уже обработанными экземплярами, все условия(constraints) моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами, нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина иерархии, естественно, тоже не ограничена. Генераторы переводятся в Verilog и VHDL[править | править вики-текст] Конвертор анализирует код каждого генератора и переводит его в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки. В VHDL они будут переведены в process-блоки одновременных присваиваний сигналов. Назначение портов определяется в зависимости от сигналов[править | править вики-текст] В MyHDL нет понятия направления порта, нет входных и выходных портов, определенных строго и единожды. Конвертер проверяет, как используется сигнал: как входной порт, выходной порт или как внутренний сигнал. Интерфейсы конвертируемы[править | править вики-текст] Интерфейс – это объект с несколькими сигналами и их свойствами. Конвертер поддерживает их с помощью имен и mangling. Вызов функций преобразуется в Verilog и VHDL-подпрограммы[править | править вики-текст] Конвертер анализирует функцию и ее код. Каждая функция переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы поддерживать возможности функций в Питоне полностью, на каждый вызов функции в Питоне генерируется отдельная подпрограмма if-then-else-структуры могут быть переведены в case-блоки[править | править вики-текст] Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции, где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими свойствами в случае синтеза (вероятно, мультиплексор – авт.) Выбор способов перевода enum’ов – перечислимых типов[править | править вики-текст] Функция enum() в MyHDL возвращает перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет способ ее перевода в HDL: бинарный, one hot, или one cold. Память с произвольным доступом RAM[править | править вики-текст] Большинство средств синтеза могут переводить Verilog и VHDL, определяющий блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию, конвертер преобразует list из Signal’ов в Verilog-памяти или VHDL-массивы. Память ROM[править | править вики-текст] Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает перевод в такие конструкции автоматически, основываясь на высокоуровневом описании. ROM описывается одной строчкой, посредством индексирования tuple’а. Знаковая арифметика[править | править вики-текст] В MyHDL работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными числами, пользователь должен строго указать, что эта переменная – знаковая. Но когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё становится сложнее. Когда в Verilog в одном выражении есть знаковые и беззнаковые числа, все считаются беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик вынужден будет добавлять знаковые расширения и преобразования типов для решения этой проблемы. В VHDL знаковые и беззнаковые числа в одном выражении просто не будут работать. Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только после этого включать в выражение. В MyHDL эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того, конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и &#160;VHDL. Он использует знаковые и беззнаковые типы в зависимости от ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя граница, верхняя граница=верхняя граница). Пользовательский код[править | править вики-текст] Если необходимо, пользователь может обходить конверсию и писать свой собственный код, который вставится непосредственно в конченый проект. Конвертируемое множество[править | править вики-текст] Введение[править | править вики-текст] Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не сильно значительные, конвертируемое подмножество намного шире, чем синтезируемое подмножество, которое является производственным стандартом. Другими словами, код на MyHDL, написанный в соответствии с правилами для синтезируемых устройств, будет конвертируемым. Хотя возможно также написать конвертируемый код для несинтезируемых моделей и тестбенчей. Конвертер пытается выдавать понятные сообщения об ошибках, когда натыкается на конструкцию, которую сконвертировать невозможно. Кодстайл[править | править вики-текст] Действительно важное ограничение на конвертируемый код – это то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку или по другому сигналу. Для чистого моделирования неважно, какие генераторы Вы используете. Хотя в конвертируемом коде они должны быть созданы с помощью декораторов из MyHDL:&#160; &#160;instance(), always(), always_seq(), или always_comb(). Поддерживаемые типы[править | править вики-текст] Наиболее значимое ограничение накладывается на типы. Только ограниченное количество типов может быть скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL. Объекты Intbv должны быть сконструированы с той длиной, которую они определяют сами с помощью нижнего и верхнего ограничения, которое задаем мы сами: index = intbv(0, min=MIN, max=MAX) Verilog-конвертер поддерживает intbv с отрицательными значениями. Еще можно создавать его с помощью слайсинга вот так: index = intbv(0)[N:] Это выражение даст нам число 0 с максимальным значением 2**N, то есть длиной N. Конвертер вообще поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) http://docs.myhdl.org/en/stable/manual/index.html '
Разобранный HTML-код новой версии ($1) (new_html)
'<p></p> <div id="toc" class="toc"> <div id="toctitle"> <h2>Содержание</h2> </div> <ul> <li class="toclevel-1 tocsection-1"><a href="#.D0.AE.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B5"><span class="tocnumber">1</span> <span class="toctext">Юнит-тестирование</span></a> <ul> <li class="toclevel-2 tocsection-2"><a href="#.D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5"><span class="tocnumber">1.1</span> <span class="toctext">Введение</span></a></li> <li class="toclevel-2 tocsection-3"><a href="#.D0.92.D0.B0.D0.B6.D0.BD.D0.BE.D1.81.D1.82.D1.8C_.D1.8E.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F"><span class="tocnumber">1.2</span> <span class="toctext"><i>Важность юнит-тестирования</i></span></a></li> <li class="toclevel-2 tocsection-4"><a href="#.D0.A0.D0.B0.D0.B7.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B0_.D1.8E.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.BE.D0.B2."><span class="tocnumber">1.3</span> <span class="toctext"><i>Разработка юнит-тестов.</i></span></a> <ul> <li class="toclevel-3 tocsection-5"><a href="#.D0.9E.D0.BF.D1.80.D0.B5.D0.B4.D0.B5.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5_.D1.82.D1.80.D0.B5.D0.B1.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B9"><span class="tocnumber">1.3.1</span> <span class="toctext">Определение требований</span></a></li> <li class="toclevel-3 tocsection-6"><a href="#.D0.98.D0.B7.D0.BC.D0.B5.D0.BD.D0.B5.D0.BD.D0.B8.D0.B5_.D1.82.D1.80.D0.B5.D0.B1.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B9:"><span class="tocnumber">1.3.2</span> <span class="toctext">Изменение требований:</span></a></li> </ul> </li> </ul> </li> <li class="toclevel-1 tocsection-7"><a href="#.D0.9E.D0.B4.D0.BD.D0.BE.D0.B2.D1.80.D0.B5.D0.BC.D0.B5.D0.BD.D0.BD.D0.B0.D1.8F_.D1.81_.D0.B2.D0.B5.D1.80.D0.B8.D0.BB.D0.BE.D0.B3.D0.BE.D0.BC_.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D1.8F.D1.86.D0.B8.D1.8F.2C_.D0.A1.D0.BE.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D1.8F.D1.86.D0.B8.D1.8F.28Co-simulation.29"><span class="tocnumber">2</span> <span class="toctext">Одновременная с верилогом симуляция, Сосимуляция(Co-simulation)</span></a> <ul> <li class="toclevel-2 tocsection-8"><a href="#.D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5_2"><span class="tocnumber">2.1</span> <span class="toctext">Введение</span></a></li> <li class="toclevel-2 tocsection-9"><a href="#.D0.9D.D0.B0_.D1.81.D1.82.D0.BE.D1.80.D0.BE.D0.BD.D0.B5_HDL"><span class="tocnumber">2.2</span> <span class="toctext">На стороне HDL</span></a></li> <li class="toclevel-2 tocsection-10"><a href="#.D0.9D.D0.B0_.D1.81.D1.82.D0.BE.D1.80.D0.BE.D0.BD.D0.B5_MyHDL"><span class="tocnumber">2.3</span> <span class="toctext">На стороне MyHDL</span></a></li> <li class="toclevel-2 tocsection-11"><a href="#.D0.9E.D0.B3.D1.80.D0.B0.D0.BD.D0.B8.D1.87.D0.B5.D0.BD.D0.B8.D1.8F"><span class="tocnumber">2.4</span> <span class="toctext">Ограничения</span></a> <ul> <li class="toclevel-3 tocsection-12"><a href="#.D0.A2.D0.BE.D0.BB.D1.8C.D0.BA.D0.BE_.D0.BF.D0.B0.D1.81.D1.81.D0.B8.D0.B2.D0.BD.D1.8B.D0.B9_HDL_.D0.BC.D0.BE.D0.B6.D0.B5.D1.82_.D0.B1.D1.8B.D1.82.D1.8C_.D1.81.D0.BE.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD."><span class="tocnumber">2.4.1</span> <span class="toctext">Только пассивный HDL может быть сосимулирован.</span></a></li> <li class="toclevel-3 tocsection-13"><a href="#1..C2.A0.C2.A0.C2.A0.C2.A0.C2.A0_.D0.93.D0.BE.D0.BD.D0.BA.D0.B0"><span class="tocnumber">2.4.2</span> <span class="toctext">1.&#160;&#160;&#160;&#160;&#160; Гонка</span></a></li> </ul> </li> </ul> </li> <li class="toclevel-1 tocsection-14"><a href="#.D0.A2.D1.80.D0.B0.D0.BD.D1.81.D0.BB.D1.8F.D1.86.D0.B8.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL"><span class="tocnumber">3</span> <span class="toctext">Трансляция в Verilog и VHDL</span></a> <ul> <li class="toclevel-2 tocsection-15"><a href="#.C2.A0.D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5"><span class="tocnumber">3.1</span> <span class="toctext">&#160;Введение</span></a></li> <li class="toclevel-2 tocsection-16"><a href="#.D0.9E.D0.BF.D0.B8.D1.81.D0.B0.D0.BD.D0.B8.D0.B5_.D0.BF.D1.80.D0.BE.D0.B4.D1.83.D0.BA.D1.82.D0.B0"><span class="tocnumber">3.2</span> <span class="toctext">Описание продукта</span></a></li> <li class="toclevel-2 tocsection-17"><a href="#.D0.9E.D1.81.D0.BE.D0.B1.D0.B5.D0.BD.D0.BD.D0.BE.D1.81.D1.82.D0.B8"><span class="tocnumber">3.3</span> <span class="toctext">Особенности</span></a> <ul> <li class="toclevel-3 tocsection-18"><a href="#.C2.A0.D0.9A.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.81.D0.B8.D1.8F_.D0.BF.D0.BE.D1.81.D0.BB.D0.B5_.D0.BE.D0.B1.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B8"><span class="tocnumber">3.3.1</span> <span class="toctext">&#160;Конверсия после обработки</span></a></li> <li class="toclevel-3 tocsection-19"><a href="#.D0.9E.D0.B1.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B0_.D1.81.D1.82.D1.80.D1.83.D0.BA.D1.82.D1.83.D1.80_.D0.BB.D1.8E.D0.B1.D0.BE.D0.B9_.D1.81.D0.BB.D0.BE.D0.B6.D0.BD.D0.BE.D1.81.D1.82.D0.B8"><span class="tocnumber">3.3.2</span> <span class="toctext">Обработка структур любой сложности</span></a></li> <li class="toclevel-3 tocsection-20"><a href="#.D0.93.D0.B5.D0.BD.D0.B5.D1.80.D0.B0.D1.82.D0.BE.D1.80.D1.8B_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.BE.D0.B4.D1.8F.D1.82.D1.81.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL"><span class="tocnumber">3.3.3</span> <span class="toctext">Генераторы переводятся в Verilog и VHDL</span></a></li> <li class="toclevel-3 tocsection-21"><a href="#.D0.9D.D0.B0.D0.B7.D0.BD.D0.B0.D1.87.D0.B5.D0.BD.D0.B8.D0.B5_.D0.BF.D0.BE.D1.80.D1.82.D0.BE.D0.B2_.D0.BE.D0.BF.D1.80.D0.B5.D0.B4.D0.B5.D0.BB.D1.8F.D0.B5.D1.82.D1.81.D1.8F_.D0.B2_.D0.B7.D0.B0.D0.B2.D0.B8.D1.81.D0.B8.D0.BC.D0.BE.D1.81.D1.82.D0.B8_.D0.BE.D1.82_.D1.81.D0.B8.D0.B3.D0.BD.D0.B0.D0.BB.D0.BE.D0.B2"><span class="tocnumber">3.3.4</span> <span class="toctext">Назначение портов определяется в зависимости от сигналов</span></a></li> <li class="toclevel-3 tocsection-22"><a href="#.D0.98.D0.BD.D1.82.D0.B5.D1.80.D1.84.D0.B5.D0.B9.D1.81.D1.8B_.D0.BA.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.82.D0.B8.D1.80.D1.83.D0.B5.D0.BC.D1.8B"><span class="tocnumber">3.3.5</span> <span class="toctext">Интерфейсы конвертируемы</span></a></li> <li class="toclevel-3 tocsection-23"><a href="#.D0.92.D1.8B.D0.B7.D0.BE.D0.B2_.D1.84.D1.83.D0.BD.D0.BA.D1.86.D0.B8.D0.B9_.D0.BF.D1.80.D0.B5.D0.BE.D0.B1.D1.80.D0.B0.D0.B7.D1.83.D0.B5.D1.82.D1.81.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL-.D0.BF.D0.BE.D0.B4.D0.BF.D1.80.D0.BE.D0.B3.D1.80.D0.B0.D0.BC.D0.BC.D1.8B"><span class="tocnumber">3.3.6</span> <span class="toctext">Вызов функций преобразуется в Verilog и VHDL-подпрограммы</span></a></li> <li class="toclevel-3 tocsection-24"><a href="#if-then-else-.D1.81.D1.82.D1.80.D1.83.D0.BA.D1.82.D1.83.D1.80.D1.8B_.D0.BC.D0.BE.D0.B3.D1.83.D1.82_.D0.B1.D1.8B.D1.82.D1.8C_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D1.8B_.D0.B2_case-.D0.B1.D0.BB.D0.BE.D0.BA.D0.B8"><span class="tocnumber">3.3.7</span> <span class="toctext">if-then-else-структуры могут быть переведены в case-блоки</span></a></li> <li class="toclevel-3 tocsection-25"><a href="#.D0.92.D1.8B.D0.B1.D0.BE.D1.80_.D1.81.D0.BF.D0.BE.D1.81.D0.BE.D0.B1.D0.BE.D0.B2_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.BE.D0.B4.D0.B0_enum.E2.80.99.D0.BE.D0.B2_.E2.80.93_.D0.BF.D0.B5.D1.80.D0.B5.D1.87.D0.B8.D1.81.D0.BB.D0.B8.D0.BC.D1.8B.D1.85_.D1.82.D0.B8.D0.BF.D0.BE.D0.B2"><span class="tocnumber">3.3.8</span> <span class="toctext">Выбор способов перевода enum’ов – перечислимых типов</span></a></li> <li class="toclevel-3 tocsection-26"><a href="#.D0.9F.D0.B0.D0.BC.D1.8F.D1.82.D1.8C_.D1.81_.D0.BF.D1.80.D0.BE.D0.B8.D0.B7.D0.B2.D0.BE.D0.BB.D1.8C.D0.BD.D1.8B.D0.BC_.D0.B4.D0.BE.D1.81.D1.82.D1.83.D0.BF.D0.BE.D0.BC_RAM"><span class="tocnumber">3.3.9</span> <span class="toctext">Память с произвольным доступом RAM</span></a></li> <li class="toclevel-3 tocsection-27"><a href="#.D0.9F.D0.B0.D0.BC.D1.8F.D1.82.D1.8C_ROM"><span class="tocnumber">3.3.10</span> <span class="toctext">Память ROM</span></a></li> <li class="toclevel-3 tocsection-28"><a href="#.D0.97.D0.BD.D0.B0.D0.BA.D0.BE.D0.B2.D0.B0.D1.8F_.D0.B0.D1.80.D0.B8.D1.84.D0.BC.D0.B5.D1.82.D0.B8.D0.BA.D0.B0"><span class="tocnumber">3.3.11</span> <span class="toctext">Знаковая арифметика</span></a></li> <li class="toclevel-3 tocsection-29"><a href="#.D0.9F.D0.BE.D0.BB.D1.8C.D0.B7.D0.BE.D0.B2.D0.B0.D1.82.D0.B5.D0.BB.D1.8C.D1.81.D0.BA.D0.B8.D0.B9_.D0.BA.D0.BE.D0.B4"><span class="tocnumber">3.3.12</span> <span class="toctext">Пользовательский код</span></a></li> </ul> </li> </ul> </li> <li class="toclevel-1 tocsection-30"><a href="#.D0.9A.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.82.D0.B8.D1.80.D1.83.D0.B5.D0.BC.D0.BE.D0.B5_.D0.BC.D0.BD.D0.BE.D0.B6.D0.B5.D1.81.D1.82.D0.B2.D0.BE"><span class="tocnumber">4</span> <span class="toctext">Конвертируемое множество</span></a> <ul> <li class="toclevel-2 tocsection-31"><a href="#.D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5_3"><span class="tocnumber">4.1</span> <span class="toctext">Введение</span></a></li> <li class="toclevel-2 tocsection-32"><a href="#.D0.9A.D0.BE.D0.B4.D1.81.D1.82.D0.B0.D0.B9.D0.BB"><span class="tocnumber">4.2</span> <span class="toctext">Кодстайл</span></a></li> <li class="toclevel-2 tocsection-33"><a href="#.D0.9F.D0.BE.D0.B4.D0.B4.D0.B5.D1.80.D0.B6.D0.B8.D0.B2.D0.B0.D0.B5.D0.BC.D1.8B.D0.B5_.D1.82.D0.B8.D0.BF.D1.8B"><span class="tocnumber">4.3</span> <span class="toctext">Поддерживаемые типы</span></a></li> </ul> </li> </ul> </div> <p></p> <h2><span class="mw-headline" id=".D0.AE.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B5">Юнит-тестирование</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=1" title="Редактировать раздел «Юнит-тестирование»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=1" title="Редактировать раздел «Юнит-тестирование»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h2> <h3><span class="mw-headline" id=".D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5">Введение</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=2" title="Редактировать раздел «Введение»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=2" title="Редактировать раздел «Введение»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Многие аспекты разработки современных цифровых аппаратных средств могут быть рассмотрены как отдельное направление разработки ПО. Рассматривая ситуацию с такой точки зрения, это спорный вопрос – могут ли технологии, применимые в разработке ПО, применяться в разработке аппаратного обеспечения.</p> <p>Есть подход к программированию, который стал популярен относительно недавно. Он называется «Экстремальное программирование» (<i>Extreme Programming,</i> ХР). Это впечатляющий набор способов и руководств, который идет против традиционных подходов к разработке. В других случаях экстремальное программирование будто бы подчеркивает здравый смысл, который не всегда присутствует в стандартных методах. Например, экстремальное программирование делает упор на рабочую неделю, что позволяет иметь всегда свежую голову. Это необходимо для качественной разработки.</p> <p><i>Эта</i> статья не является попыткой написать учебник по экстремальному программированию. Наоборот, в этой статье я подчеркну лишь одну черту экстремального программирования, которая необходима в аппаратной разработке: важность юнит-тестирования и его методики.</p> <h3><span class="mw-headline" id=".D0.92.D0.B0.D0.B6.D0.BD.D0.BE.D1.81.D1.82.D1.8C_.D1.8E.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F"><i>Важность юнит-тестирования</i></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=3" title="Редактировать раздел «Важность юнит-тестирования»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=3" title="Редактировать раздел «Важность юнит-тестирования»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p><i>Юнит-тестирование</i> – это один из краеугольных камней экстремального программирования. Другие идеи этой технологии, такие как коллективное владение кодом и непрерывное повышение качества возможны тогда и только тогда, когда реализовано юнит-тестирование. Более того, экстремальное программирование подчеркивает, что написание юнит-тестов должно быть автоматизировано, что должны быть протестированы все элементы всех классов и они должны работать идеально всё время.</p> <p><i>Я</i> верю, что эта концепция применима как раз в аппаратной разработке. К томуже, юнит-тесты – это способ управлять временем симуляции. Например, верификация конечного автомата, который очень медленно работает на редких событиях, может быть затруднена или невозможна с учетом ограниченности времени, даже на самом быстром симуляторе.</p> <p><i>Понятно,</i> что юнит-тесты имеют неограниченные преимущества. Однако, с другой стороны, если мы должны все тестировать, нам нужно писать очень много юнит-тестов. Тогда будет легко, удобно и приятно создавать, управлять и запускать их. Следовательно, в теории экстремального программирования подчеркивается необходимость стандартного юнит-тестирования, которое поддерживает все описанные задачи. В этой главе мы будем исследовать использование модуля unittest из стандартной библиотеки Python для создание юнит-тестов для аппаратных проектов.</p> <h3><span class="mw-headline" id=".D0.A0.D0.B0.D0.B7.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B0_.D1.8E.D0.BD.D0.B8.D1.82-.D1.82.D0.B5.D1.81.D1.82.D0.BE.D0.B2."><i>Разработка юнит-тестов.</i></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=4" title="Редактировать раздел «Разработка юнит-тестов.»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=4" title="Редактировать раздел «Разработка юнит-тестов.»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p><i>В</i> этой главе мы будем неформально изучать применение юнит-тестирования к аппаратному проектированию. Мы будетм делать так на маленьком примере: мы будем тестировать устройство, переводящее двоичный код в код Грея. Это устройство было описано в разделе Bit indexing.</p> <h4><span class="mw-headline" id=".D0.9E.D0.BF.D1.80.D0.B5.D0.B4.D0.B5.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5_.D1.82.D1.80.D0.B5.D0.B1.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B9">Определение требований</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=5" title="Редактировать раздел «Определение требований»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=5" title="Редактировать раздел «Определение требований»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Мы начинаем с определения требований. Для кодировщика в код Грея мы хотим, чтобы выходные данные удовлетворяли характеристикам кода Грея. Давайте определим код как список кодовых слов, где кодовое слово – это строка из битов. Код длины n может определять 2**n слов.</p> <p>Широко известные характеристики – это то, для чего предназначен код Грея:</p> <p>Последовательные кодовые слова в коде Грея должны отличаться ровно в одном бите.</p> <p>Этого достаточно? Не совсем. Например, под наши нынешние требования подходит устройство, возвращающее последний бит (lsb) для любого двоичного слова. Это, очевидно, не то, что мы хотим увидеть. Также нам не нужно, чтобы длина кода Грея превосходила длину битового слова.</p> <p>Таким образом: каждое двоичное слово порядка n должно соотвествовать ровно одному слову в коде Грея такого же порядка.</p> <p>Когда написаны требования, мы можем продолжать.</p> <p>Сначала – тесты</p> <p>Отличная идея экстремального программирования состоит в том, что нужно писать юнит-тесты до написания кода. Таким образом, до того, как реализовывать что-то, сначала напишите тест, который это что-то должно проходить. Это противоречит стандартной практике и вообще логичному ходу событий. Многие разработчики предпочитают в первую очередь создать работающий продукт, а после этого уже ее тестировать.</p> <p>Но если подумать, что логично сделать верификацию сначала. Верификация – это просто условия. Таким образом, ваши мысли еще не заняты деталями реализации. Юнит-тесты – это работающая реализация описания и требований, поэтому реализация их в коде позволяет их более хорошо понять, поэтому это стоит сделать. После этого реализация будет проще. Возможно, самым важным является то, что тесты доступны уже во время разработки, и их может запускать любой из разработчиков проекта, чтобы проверять свои изменения.</p> <p>Python имеет хороший модуль для юнит-тестирования unittest, управляющий и запускающий юнит-тесты. С unittest тестовое окружение пишется с помощью создания класса, который наследуется от unittest.TestCase. Отдельные тесты создаются как методы этого класса: имя каждого метода должно начинаться с test. Все такие методы рассматриваются модулем как тесты из комплекта тестов.</p> <p>Мы зададим тесты для требований кода Грея, после чего напишем тесты для каждого из требований. Итогом создания комплекта тестов таков:</p> <p><b>from</b> unittest <b>import</b> TestCase</p> <p><b>class</b> <b>TestGrayCodeProperties</b>(TestCase):</p> <p><b>def</b> <b>testSingleBitChange</b>(self):</p> <p>""" Check that only one bit changes in successive codewords """</p> <p><b>....</b></p> <p><b>def</b> <b>testUniqueCodeWords</b>(self):</p> <p>""" Check that all codewords occur exactly once """</p> <p><b>....</b></p> <p>Каждый метод будет небольшим тестом, который проверяет заданное условие. Для написания тестов нам не нужна реализация самого проекта, самого кодировщика, но нам нужен интерфейс этого устройства. Можно определить пустую реализацию, включающую только интерфейс:</p> <pre> <b>def</b> <b>bin2gray</b>(B, G, width): </pre> <pre> <i>### NOT IMPLEMENTED YET! ###</i> </pre> <pre> <b>yield</b> None </pre> <p>Для первого требования мы напишем тест, который подает на вход последовательно все цифры, а потом сравнивает между собой выходные данные кодировщика и проверяет, что действительно последовательные слова отличаются на один бит, не больше и не меньше. Мы тестируем все коды Грея вплоть до заданного порядка MAX_WIDTH.</p> <pre> <b>def</b> <b>testSingleBitChange</b>(self): </pre> <pre> """ Check that only one bit changes in successive codewords """ </pre> <pre> <b>def</b> <b>test</b>(B, G, width): </pre> <pre> B<b>.</b>next <b>=</b> intbv(0) </pre> <pre> <b>yield</b> delay(10) </pre> <pre> <b>for</b> i <b>in</b> range(1, 2<b>**</b>width): </pre> <pre> G_Z<b>.</b>next <b>=</b> G </pre> <pre> B<b>.</b>next <b>=</b> intbv(i) </pre> <pre> <b>yield</b> delay(10) </pre> <pre> diffcode <b>=</b> bin(G <b>^</b> G_Z) </pre> <pre> self<b>.</b>assertEqual(diffcode<b>.</b>count('1'), 1) </pre> <pre> <b>for</b> width <b>in</b> range(1, MAX_WIDTH): </pre> <pre> B <b>=</b> Signal(intbv(<b>-</b>1)) </pre> <pre> G <b>=</b> Signal(intbv(0)) </pre> <pre> G_Z <b>=</b> Signal(intbv(0)) </pre> <pre> dut <b>=</b> bin2gray(B, G, width) </pre> <pre> check <b>=</b> test(B, G, width) </pre> <pre> sim <b>=</b> Simulation(dut, check) </pre> <pre> sim.run(quiet=1) </pre> <p>Обратите внимание на то, как проверка равенства осуществляется с помощью метода self.assertEqual , определенного в unittest.TestCase.</p> <p>Точно так же мы пишем тест для проверки второго требования. Мы снова включаем симуляцию для всех чисел. Берем список чисел, вышедших из кодировщика, сортируем их и проверяем совпадение с начальным набором.</p> <pre> <b>def</b> <b>testUniqueCodeWords</b>(self): </pre> <pre> """ Check that all codewords occur exactly once """ </pre> <pre> <b>def</b> <b>test</b>(B, G, width): </pre> <pre> actual <b>=</b> [] </pre> <pre> <b>for</b> i <b>in</b> range(2<b>**</b>width): </pre> <pre> B<b>.</b>next <b>=</b> intbv(i) </pre> <pre> <b>yield</b> delay(10) </pre> <pre> actual<b>.</b>append(int(G)) </pre> <pre> actual<b>.</b>sort() </pre> <pre> expected <b>=</b> range(2<b>**</b>width) </pre> <pre> self<b>.</b>assertEqual(actual, expected) </pre> <pre> <b>for</b> width <b>in</b> range(1, MAX_WIDTH): </pre> <pre> B <b>=</b> Signal(intbv(<b>-</b>1)) </pre> <pre> G <b>=</b> Signal(intbv(0)) </pre> <pre> dut <b>=</b> bin2gray(B, G, width) </pre> <pre> check <b>=</b> test(B, G, width) </pre> <pre> sim <b>=</b> Simulation(dut, check) </pre> <pre> sim<b>.</b>run(quiet<b>=</b>1) </pre> <p>Разработка от тестирования</p> <p>Когда написаны тесты, мы начинаем реализацию. Для демонстрации работы тестов сделаем в реализации какие-нибудь ошибки и посмотрим, как тесты будут себя вести.</p> <pre> unittest<b>.</b>main() </pre> <p>Давайте запустим этот тест, используя неработающий, еще пустой объект кодировщика, показанный выше:</p> <pre> % python test_gray.py -v </pre> <pre> Check that only one bit changes in successive codewords ... FAIL </pre> <pre> Check that all codewords occur exactly once ... FAIL </pre> <pre> &lt;trace backs not shown&gt; </pre> <p>Как и ожидалось, он полностью проваливается. Теперь попытаемся протестировать неправильную реализацию, которая выдает последний бит числа (то есть удовлетворяет одному условию):</p> <pre> <b>def</b> <b>bin2gray</b>(B, G, width): </pre> <pre> <i>### INCORRECT - DEMO PURPOSE ONLY! ###</i> </pre> <pre> @always_comb </pre> <pre> <b>def</b> <b>logic</b>(): </pre> <pre> G<b>.</b>next <b>=</b> B[0] </pre> <pre> <b>return</b> logic </pre> <p>Запуск тестов дает нам:</p> <pre> % python test_gray.py -v </pre> <pre> Check that only one bit changes in successive codewords ... ok </pre> <pre> Check that all codewords occur exactly once ... FAIL </pre> <pre> ====================================================================== </pre> <pre> FAIL: Check that all codewords occur exactly once </pre> <pre> ---------------------------------------------------------------------- </pre> <pre> Traceback (most recent call last): </pre> <pre> File "test_gray.py", line 109, in testUniqueCodeWords </pre> <pre> sim.run(quiet=1) </pre> <pre> ... </pre> <pre> File "test_gray.py", line 104, in test </pre> <pre> self.assertEqual(actual, expected) </pre> <pre> File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual </pre> <pre> raise self.failureException, \ </pre> <pre> AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3] </pre> <pre> ---------------------------------------------------------------------- </pre> <pre> Ran 2 tests in 0.785s </pre> <p>Теперь проходятся тесты, проверяющие первое из требований, но не проходятся - проверяющие второе. Нужно смотреть сообщения, которые выдает тест, чтобы понять, что не так с нашим кодом.</p> <p>В конце концов, если мы тестируем правильную версию bin2gray:</p> <pre> <b>def</b> <b>bin2gray</b>(B, G, width): </pre> <pre> """ Gray encoder. </pre> <pre> B -- input intbv signal, binary encoded </pre> <pre> G -- output intbv signal, gray encoded </pre> <pre> width -- bit width </pre> <pre> """ </pre> <pre> @always_comb </pre> <pre> <b>def</b> <b>logic</b>(): </pre> <pre> <b>for</b> i <b>in</b> range(width): </pre> <pre> G<b>.</b>next[i] <b>=</b> B[i<b>+</b>1] <b>^</b> B[i] </pre> <pre> <b>return</b> logic </pre> <p>Вывод получается такой:</p> <pre> % python test_gray.py -v </pre> <pre> Check that only one bit changes in successive codewords ... ok </pre> <pre> Check that all codewords occur exactly once ... ok </pre> <pre> ---------------------------------------------------------------------- </pre> <pre> Ran 2 tests in 6.364s </pre> <pre> OK </pre> <h4><span class="mw-headline" id=".D0.98.D0.B7.D0.BC.D0.B5.D0.BD.D0.B5.D0.BD.D0.B8.D0.B5_.D1.82.D1.80.D0.B5.D0.B1.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B9:">Изменение требований:</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=6" title="Редактировать раздел «Изменение требований:»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=6" title="Редактировать раздел «Изменение требований:»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>В предыдущей главе мы сосредоточились на основных требованиях к коду Грея. Их возможно определить не глядя на код самого устройства. Легко увидеть, что есть не одно решение (в виде программного кода модуля кодировщика), проходящее эти требования. В хорошем экстремальном программировании мы проверяем только требования и ничего больше.</p> <p>Может случиться так, что нужно больше контроля. Например, требования могут заключаться и в том, чтобы он выдавал заданные коды, а не удовлетворял двум общим признакам. Например, покажем, как тестировать код Грея, который является частным случаем, который удовлетворяет этим условиям (что описаны в предыдущей главе). В этом случае этот тест будет действительно проще предыдущего.</p> <p>Мы обозначим код Грея длины n за Ln. Примеры:</p> <pre> L1 = ['0', '1'] </pre> <pre> L2 = ['00', '01', '11', '10'] </pre> <pre> L3 = ['000', '001', '011', '010', '110', '111', '101', 100'] </pre> <p>Можно определить эти коды рекурсивным алгоритмом, например вот так:</p> <p>1.&#160;&#160;&#160; L1 = [‘0’, ‘1’]</p> <p>2.&#160;&#160;&#160; Ln+1 может быть получен из Ln так, как описано здесь. Создаем новый код Ln0, в котором добавляем ‘0’ в начало каждого элемента Ln. Создаем еще один новый код Ln1, добавляя к каждому кодовому слову Ln &#160;‘1’, и меняем их порядок. Ln+1 – это конкатенация Ln0 и Ln1.</p> <p>Python хорошо известен как язык с красивыми описаниями алгоритмов, и это хороший тому пример. Вот как выглядит этот алгоритм на Python:</p> <p><b>def</b> <b>nextLn</b>(Ln):</p> <p>&#160;&#160;&#160; """ Return Gray code Ln+1, given Ln. """</p> <p>Ln0 <b>=</b> ['0' <b>+</b> codeword <b>for</b> codeword <b>in</b> Ln]</p> <p>Ln1 <b>=</b> ['1' <b>+</b> codeword <b>for</b> codeword <b>in</b> Ln]</p> <p>Ln1<b>.</b>reverse()</p> <p><b>return</b> Ln0 <b>+</b> Ln1</p> <p>Код [‘0’ + codeword for codeword in … ] называется list comprehension. Это сокращенный способ создания списков, создаваемых с помощью цикла с простыми действиями.</p> <p>Сейчас требования состоят в том, что код точно соответствует Ln. Мы используем функцию nextLn для расчета ожидаемого результата. Новый testcase выглядит так:</p> <p><b>class</b> <b>TestOriginalGrayCode</b>(TestCase):</p> <p><b>def</b> <b>testOriginalGrayCode</b>(self):</p> <p>""" Check that the code is an original Gray code """</p> <p>Rn <b>=</b> []</p> <p><b>def</b> <b>stimulus</b>(B, G, n):</p> <p><b>for</b> i <b>in</b> range(2<b>**</b>n):</p> <p>B<b>.</b>next <b>=</b> intbv(i)</p> <p><b>yield</b> delay(10)</p> <p>Rn<b>.</b>append(bin(G, width<b>=</b>n))</p> <p>Ln <b>=</b> ['0', '1'] <i># n == 1</i></p> <p><b>for</b> n <b>in</b> range(2, MAX_WIDTH):</p> <p>Ln <b>=</b> nextLn(Ln)</p> <p><b>del</b> Rn[:]</p> <p>B <b>=</b> Signal(intbv(<b>-</b>1))</p> <p>G <b>=</b> Signal(intbv(0))</p> <p>dut <b>=</b> bin2gray(B, G, n)</p> <p>stim <b>=</b> stimulus(B, G, n)</p> <p>sim <b>=</b> Simulation(dut, stim)</p> <p>sim<b>.</b>run(quiet<b>=</b>1)</p> <p>self<b>.</b>assertEqual(Ln, Rn)</p> <p>Если так сделать, то реализованное нами устройство выдает действительно настоящий код Грея.</p> <p>%python test_gray.py -v TestOriginalGrayCode</p> <p>Check that the code is an original Gray code ... ok</p> <p>----------------------------------------------------------------------</p> <p>Ran 1 tests in 3.091s</p> <p>OK</p> <h2><span class="mw-headline" id=".D0.9E.D0.B4.D0.BD.D0.BE.D0.B2.D1.80.D0.B5.D0.BC.D0.B5.D0.BD.D0.BD.D0.B0.D1.8F_.D1.81_.D0.B2.D0.B5.D1.80.D0.B8.D0.BB.D0.BE.D0.B3.D0.BE.D0.BC_.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D1.8F.D1.86.D0.B8.D1.8F.2C_.D0.A1.D0.BE.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D1.8F.D1.86.D0.B8.D1.8F.28Co-simulation.29">Одновременная с верилогом симуляция, Сосимуляция(Co-simulation)</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=7" title="Редактировать раздел «Одновременная с верилогом симуляция, Сосимуляция(Co-simulation)»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=7" title="Редактировать раздел «Одновременная с верилогом симуляция, Сосимуляция(Co-simulation)»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h2> <h3><span class="mw-headline" id=".D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5_2">Введение</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=8" title="Редактировать раздел «Введение»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=8" title="Редактировать раздел «Введение»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Одна из наиболее важных возможностей MyHDL заключается в том, что его можно использовать для верификации аппаратных устройств, написанных на Verilog и VHDL, писать с его помощью тесты и контролировать симуляцию.</p> <p>В наши дни известно, что язык, предназначенный для верификации аппаратных модулей (HVL, hardvare Verification Language), должен поддерживать современные технологии прораммирования, такие, как обьектно-ориентированность. Все потому, что верификация – это наиболее сложная и затратная по времени часть разработки аппаратного обеспечения. Следовательно, ценна любая новая технология. Более того, тесты не обязательно должны быть имплементируемыми (реализуемыми в аппаратном виде). Поэтому, в отличие от синтезируемого кода, они не имеют ограничений в реализации.</p> <p>Технически верификация аппаратного обеспечения с помощью другого языка называется co-simulation. MyHDL создан для сосимуляции с любым симулятором, поддерживающим PLI(Procedural Language Interface). На стороне MyHDL неважно, какой симулятор используется. С другой стороны, для каждого HDL-симулятора можно написать PLI-модуль на языке С. В данный момент MyHDL включает PLI-модуль для двух симуляторов: Icarus и Cver.</p> <h3><span class="mw-headline" id=".D0.9D.D0.B0_.D1.81.D1.82.D0.BE.D1.80.D0.BE.D0.BD.D0.B5_HDL">На стороне HDL</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=9" title="Редактировать раздел «На стороне HDL»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=9" title="Редактировать раздел «На стороне HDL»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Чтобы продемонстрировать сосимуляцию мы будем использовать снова кодировщик в код Грея из предыдущих глав. Пусть мы хотим синтезировать его, написав для этого на Verilog. Конечно же, мы захотим снова использовать те же тесты, что были для него уже написаны.</p> <p>Для начала давайте освежим в памяти, как выглядит кодировщик на MyHDL:</p> <p><b>def</b> <b>bin2gray</b>(B, G, width):</p> <p>""" Gray encoder.</p> <p>B -- input intbv signal, binary encoded</p> <p>G -- output intbv signal, gray encoded</p> <p>width -- bit width</p> <p>"""</p> <p>@always_comb</p> <p><b>def</b> <b>logic</b>():</p> <p><b>for</b> i <b>in</b> range(width):</p> <p>G<b>.</b>next[i] <b>=</b> B[i<b>+</b>1] <b>^</b> B[i]</p> <p><b>return</b> logic</p> <p>Чтобы показать, что происходит при симуляции, нам не нужна реализация на Verilog, а нужен только ее интерфейс. Кодировщик будет иметь следующий интерфейс:</p> <p>module bin2gray(B, G);</p> <p>&#160;&#160; parameter width = 8;</p> <p>&#160;&#160; input [width-1:0]&#160; B;</p> <p>output [width-1:0] G;</p> <p>....</p> <p>Чтобы написать тесты кто-то создает новый модуль, который представляет собой Design Under Test (DUT). Тестбенч определяет net*ы и regи (или signalы в VHDL), которые соединяют входы и выходы DUT с генераторами тестовых сигналов и приемниками отклика. В случае тестов полностью на HDL генераторы и чекеры пишутся на том же HDL, но мы будем писать их не на HDL, а на MyHDL. чтобы соединить их с входами Verilog-модуля нужно определить, какие из них будут соединены с DUT, то есть будут во время симуляции управляться (входы) и считываться(выходы) с помощью MyHDLsimulator. В нашем примере этоделается так:</p> <p>module dut_bin2gray;</p> <p>&#160;&#160; reg [`width-1:0] B;</p> <p>&#160;&#160; wire [`width-1:0] G;</p> <p>initial begin</p> <p>$from_myhdl(B);</p> <p>$to_myhdl(G);</p> <p>end</p> <p>bin2gray dut (.B(B), .G(G));</p> <p>defparam dut.width = `width;</p> <p>endmodule</p> <p>$from_myhdl&#160;говорит о том, что эти провода принимают сигналы от MyHDL, а&#160;$to_myhdl&#160;означает, что данные сигналы будут считаны тестовым окружением MyHDL. Эти директивы принимают любое количество аргументов. Они определены в модуле PLI , написанном на C, и сделаны доступны для каждого поддерживаемого симулятора. В Icarus Verilog эти директивы определены в модуле myhdl.vpi&#160;, который скомпилирован из кода, написанного на С.</p> <h3><span class="mw-headline" id=".D0.9D.D0.B0_.D1.81.D1.82.D0.BE.D1.80.D0.BE.D0.BD.D0.B5_MyHDL">На стороне MyHDL</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=10" title="Редактировать раздел «На стороне MyHDL»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=10" title="Редактировать раздел «На стороне MyHDL»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>MyHDL поддерживает сосимуляцию с помощью объекта Cosimulation. Этот объект должен знать, как запускать симуляцию HDL. следовательно, первый аргумент для его конструктора – это командная строка для исполнения симуляции.</p> <p>Способ генерации и запуска исполняемого файла симуляции зависит от симулятора. Например, в Icarus Verilog исполняемый файл для нашего примера может быть получен посредством запуска компилятора iverilog, как показано ниже:</p> <p>% iverilog -o bin2gray -Dwidth=4 bin2gray.v dut_bin2gray.v</p> <p>Эта команда сгенерирует исполняемый файл bin2gray для параметра width=4 с помощью компиляции соответствующих Verilog-файлов.</p> <p>Симуляция его запускается с помощью команды vvp:</p> <p>% vvp -m ./myhdl.vpi bin2gray</p> <p>Таким образом запускается симуляция bin2gray и определяется, что будет использоваться myhdl.vpi PLI модуль, находящийся в текущей директории. (Это просто пример использования командной строки; На самом деле симуляция myhdl.vpi модуля важна только для объеста Cosimulation)</p> <p>Мы можем использовать Cosimulation для того, чтобы сообщить версию HDL симулятору MyHDL. Вместо функции генератора мы пишем функцию, которая возвращает объект Cosimulation. На нашем примере для симулятора Icarus Verilog это делается так:</p> <p><b>import</b> os</p> <p><b>from</b> myhdl <b>import</b> Cosimulation</p> <p>cmd <b>=</b> "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v"</p> <p><b>def</b> <b>bin2gray</b>(B, G, width):</p> <p>os<b>.</b>system(cmd <b>%</b> width)</p> <p><b>return</b> Cosimulation("vvp -m ./myhdl.vpi bin2gray", B<b>=</b>B, G<b>=</b>G)</p> <p>После аргумента-исполняемой команды Cosimulation принимает все пришедшие аргументы. Эти аргументы создают связь между сигналами MyHDL и reg и net в HDL. Связаны они с помощью имен. Эти имена указаны в $to_myhdl и $from_myhdl. Аргументы этих функций – сигналы из MyHDL.</p> <p>Когда мы изучили всё это, мы можем попробовать использовать существующий юнит-тест для верификации Verilog-описания. Обратите внимание, что мы используем одинаковые имена и параметры для функции bin2gray: все, что нам нужно, - это обеспечить другое определение существующему юнит-тесту.</p> <p>Взглянем на этот Verilog-проект:</p> <p>module bin2gray(B, G);</p> <p>parameter width = 8;</p> <p>input [width-1:0]&#160; B;</p> <p>output [width-1:0] G;</p> <p>reg [width-1:0] G;</p> <p>integer i;</p> <p>wire [width:0] extB;</p> <p>assign extB = {1'b0, B}; // zero-extend input</p> <p>always @(extB) begin</p> <p>for (i=0; i &lt; width; i=i+1)</p> <p>G[i] &lt;= extB[i+1] ^ extB[i];</p> <p>end</p> <p>endmodule</p> <p>Если мы запустим тест, мы получим:</p> <p>% python test_bin2gray.py</p> <p>Check that only one bit changes in successive codewords ... ok</p> <p>Check that all codewords occur exactly once ... ok</p> <p>Check that the code is an original Gray code ... ok</p> <p>----------------------------------------------------------------------</p> <p>Ran 3 tests in 2.729s</p> <p>OK</p> <h3><span class="mw-headline" id=".D0.9E.D0.B3.D1.80.D0.B0.D0.BD.D0.B8.D1.87.D0.B5.D0.BD.D0.B8.D1.8F">Ограничения</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=11" title="Редактировать раздел «Ограничения»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=11" title="Редактировать раздел «Ограничения»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>В идеальной ситуации можно симулировать любое HDL-описание с помощью MyHDL, не прилагая дополнительных усилий. К тому же соединенные сигналы с обеих сторон работают как один, делая границу между тестовым окружением и модулем абсолютно прозрачной.</p> <p>По некоторым причинам невозможно добиться полной общности. Как человек, который разрабатывал приложения на Verilog-PLI, могу поручиться – ограничения отдельного симулятора и различия между разными симуляторами могут быть ошеломляющими. К тому же полная универсальность может требовать непропорционального времени под разработку по сравнению с немного менее общим решением, которого может быть достаточно для данной задачи.</p> <p>Далее, я попробовал достичь решения, которое будет достаточно простым, чтобы можно было довольно уверенно ожидать, что любой симулятор, поддерживающий PLI, сможет его поддерживать и чтобы было довольно просто верифицировать его и поддерживать. В то же время, решение достаточно общее, чтобы заполнить всю область ожидаемого применения.</p> <p>Результатом компромисса было наложение на HDL нескольких ограничений. В этой главе будет рассказано о них.</p> <h4><span class="mw-headline" id=".D0.A2.D0.BE.D0.BB.D1.8C.D0.BA.D0.BE_.D0.BF.D0.B0.D1.81.D1.81.D0.B8.D0.B2.D0.BD.D1.8B.D0.B9_HDL_.D0.BC.D0.BE.D0.B6.D0.B5.D1.82_.D0.B1.D1.8B.D1.82.D1.8C_.D1.81.D0.BE.D1.81.D0.B8.D0.BC.D1.83.D0.BB.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.">Только пассивный HDL может быть сосимулирован.</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=12" title="Редактировать раздел «Только пассивный HDL может быть сосимулирован.»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=12" title="Редактировать раздел «Только пассивный HDL может быть сосимулирован.»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Наиболее важное ограничение MyHDL на сосимуляцию – это то, что может быть сосимулирован только «пассивный» HDL. Это значит, что HDL не должен содержать строк с временными задержками. Другими словами, MyHDL требует, чтобы только он управлял временем в симуляции. В частности, сигнал clk должен быть сгенерирован на стороне MyHDL.</p> <p>Сначала это кажется важным ограничением, но если рассмотреть реальные случаи, оно оказывается не очень важным.</p> <p>MyHDL поддерживает сосимуляцию так, что тесты должны быть именно на Python. Давайте рассмотрим природу тестируемых HDL-проектов. Для высокоуровневых, поведенческих моделей, который не должны будут прошиты в плату, я бы рекомендовал писать тесты непосредственно на MyHDL, и это не будет большим сюрпризом. Это и есть одна из важных задач MyHDL. Точно так же прошиваемые проекты с заявленными временными ограничениями – не то, что нас интересует: статический анализ тайминга – намного более хороший способ для таких проектов.</p> <p>Итог: HDL-описания, которые нас интересуют – это реальные модели аппаратуры, которые будут синтезироваться и имплементироваться для прошивки в плату. Если временные интервалы в синтезируемом коде не важны(нет строго заданного в проекте тайминга), ограничение не влияет на выполнение наших задач.</p> <h4><span class="mw-headline" id="1..C2.A0.C2.A0.C2.A0.C2.A0.C2.A0_.D0.93.D0.BE.D0.BD.D0.BA.D0.B0">1.&#160;&#160;&#160;&#160;&#160; Гонка</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=13" title="Редактировать раздел «1.      Гонка»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=13" title="Редактировать раздел «1.      Гонка»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>В обычном RTL некоторые события иногда вызывают другие события на том же clk (на том же временном интервале). Например, на положительном фронте clk некоторые сигналы сразу изменяются (комбинационная логика, насколько я помню). This is done by the concept of “delta” cycles. In a fully general, race free co-simulation, the co-simulators would communicate at the level of delta cycles. However, in MyHDL co-simulation, this is not entirely the case.</p> <p>Дельта-циклы из MyHDL-симулятора в HDL-симулятор запрещены. Хотя, с другой стороны, нет. Изменения сигналов просто возвращаются в MyHDL после всех дельта-циклов, которые прошли в HDL-сосимуляторе.</p> <p>Что это значит? Давайте начнем с хооших новостей. Как уже объяснялось в предыдущей главе, концепция MyHDL предусматривает, что clk генерируется на стороне MyHDL. когда мы используем clk от MyHDL и соответствующий ему сигнал в HDL, сосимуляция не может иметь гонок. То есть используя полность подход MyHDL, мы не можем столкнуться с гонкой.</p> <p>Другая ситуация наступает, когда мы хотим использовать сигнал, который управляется из HDL (и соответсвтующий ему MyHDL-сигнал) как clk. Соединение, которое использует как clk этот сигнал, может быть подвержено гонкам. Но можно изменять сигналы в MyHDL со сдвигом по времени, избегая таким образом возможность произвольного взаиморасположения положительного фронта clk и события, избегая таким образом гонки.</p> <p>ЕЩЕ ТУТ БЫЛО ЧТО-ТО.</p> <h2><span class="mw-headline" id=".D0.A2.D1.80.D0.B0.D0.BD.D1.81.D0.BB.D1.8F.D1.86.D0.B8.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL">Трансляция в Verilog и VHDL</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=14" title="Редактировать раздел «Трансляция в Verilog и VHDL»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=14" title="Редактировать раздел «Трансляция в Verilog и VHDL»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h2> <h3><span class="mw-headline" id=".C2.A0.D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5">&#160;Введение</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=15" title="Редактировать раздел « Введение»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=15" title="Редактировать раздел « Введение»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Не считая некоторых недостатков, MyHDL поддерживает автоматическую трансляцию кода MyHDL на Verilog и VHDL.</p> <p>Эта глава описывает основы этой трансляции. Конкретные примеры можно посмотреть в разделе Conversion examples. http://docs.myhdl.org/en/stable/manual/conversion_examples.html#conv-usage</p> <h3><span class="mw-headline" id=".D0.9E.D0.BF.D0.B8.D1.81.D0.B0.D0.BD.D0.B8.D0.B5_.D0.BF.D1.80.D0.BE.D0.B4.D1.83.D0.BA.D1.82.D0.B0">Описание продукта</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=16" title="Редактировать раздел «Описание продукта»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=16" title="Редактировать раздел «Описание продукта»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Чтобы быть конвертируемым, проект должен удовлетворять некоторым ограничениям, которые мы будем называть конвертируемым подмножеством. Эти ограничения подробно описаны в подпункте «Конвертируемое подмножество».</p> <p>Конвертируемый проект может быть сконвертирован в эквивалентную модель на Verilog или VHDL с помощью функций toVerilog(), toVHDL() библиотеки MyHDL.</p> <p>Когда проект предназначается для имплементации, синтез производит другое средство, предназначенное для синтезирования из Verilog и VHDL проекта, имплементируемого в FPGA или ASIC. То есть с наличием такого инструмента мы можем довести проект, написанный на Python, до уровня прошивки платы.</p> <p>Конверсия начинается не с исходного кода, а с инстанцированного проекта, который воспринимается интерпретатором Python. Конвертер использует профилировщик Питона, чтобы понимать все операции интерпретатора Питона и чтобы определить структуру проекта и пространство имен. Далее он выборочно компилирует части исходного кода для дополнительного анализа и для конверсии.</p> <h3><span class="mw-headline" id=".D0.9E.D1.81.D0.BE.D0.B1.D0.B5.D0.BD.D0.BD.D0.BE.D1.81.D1.82.D0.B8">Особенности</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=17" title="Редактировать раздел «Особенности»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=17" title="Редактировать раздел «Особенности»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <h4><span class="mw-headline" id=".C2.A0.D0.9A.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.81.D0.B8.D1.8F_.D0.BF.D0.BE.D1.81.D0.BB.D0.B5_.D0.BE.D0.B1.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B8">&#160;Конверсия после обработки</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=18" title="Редактировать раздел « Конверсия после обработки»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=18" title="Редактировать раздел « Конверсия после обработки»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Под обработкой в данном случае понимается изначальная обработка проекта для достижения представления о устройстве, в том числе, может ли оно быть сконвертировано, синтезировано и имплементировано. В частности, структурные параметры и конструкции обрабатываются на этом шаге. В MyHDL для обработки используется интерпретатор Питона. Объект Simulation строится на основе своих аргументов - обработанных экземпляров. Таким же образом конверсия работает с уже собранным экземпляром всего проекта. Таким образом, интерпретатор Питона используется максимально активно.</p> <h4><span class="mw-headline" id=".D0.9E.D0.B1.D1.80.D0.B0.D0.B1.D0.BE.D1.82.D0.BA.D0.B0_.D1.81.D1.82.D1.80.D1.83.D0.BA.D1.82.D1.83.D1.80_.D0.BB.D1.8E.D0.B1.D0.BE.D0.B9_.D1.81.D0.BB.D0.BE.D0.B6.D0.BD.D0.BE.D1.81.D1.82.D0.B8">Обработка структур любой сложности</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=19" title="Редактировать раздел «Обработка структур любой сложности»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=19" title="Редактировать раздел «Обработка структур любой сложности»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Так как конверсия работает с уже обработанными экземплярами, все условия(constraints) моделирования применяются только к никуда не включенным (leaf) элементам проекта, таким, как [co-operating generators]. Другими словами, нет ограничений на сложность проекта: мощности Питона хватит на все. Глубина иерархии, естественно, тоже не ограничена.</p> <h4><span class="mw-headline" id=".D0.93.D0.B5.D0.BD.D0.B5.D1.80.D0.B0.D1.82.D0.BE.D1.80.D1.8B_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.BE.D0.B4.D1.8F.D1.82.D1.81.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL">Генераторы переводятся в Verilog и VHDL</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=20" title="Редактировать раздел «Генераторы переводятся в Verilog и VHDL»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=20" title="Редактировать раздел «Генераторы переводятся в Verilog и VHDL»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Конвертор анализирует код каждого генератора и переводит его в эквивалентную конструкцию на заданном HDL. Для Verilog он переводит их в always-блоки, continuous assignments и initial-блоки. В VHDL они будут переведены в process-блоки одновременных присваиваний сигналов.</p> <h4><span class="mw-headline" id=".D0.9D.D0.B0.D0.B7.D0.BD.D0.B0.D1.87.D0.B5.D0.BD.D0.B8.D0.B5_.D0.BF.D0.BE.D1.80.D1.82.D0.BE.D0.B2_.D0.BE.D0.BF.D1.80.D0.B5.D0.B4.D0.B5.D0.BB.D1.8F.D0.B5.D1.82.D1.81.D1.8F_.D0.B2_.D0.B7.D0.B0.D0.B2.D0.B8.D1.81.D0.B8.D0.BC.D0.BE.D1.81.D1.82.D0.B8_.D0.BE.D1.82_.D1.81.D0.B8.D0.B3.D0.BD.D0.B0.D0.BB.D0.BE.D0.B2">Назначение портов определяется в зависимости от сигналов</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=21" title="Редактировать раздел «Назначение портов определяется в зависимости от сигналов»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=21" title="Редактировать раздел «Назначение портов определяется в зависимости от сигналов»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>В MyHDL нет понятия направления порта, нет входных и выходных портов, определенных строго и единожды. Конвертер проверяет, как используется сигнал: как входной порт, выходной порт или как внутренний сигнал.</p> <h4><span class="mw-headline" id=".D0.98.D0.BD.D1.82.D0.B5.D1.80.D1.84.D0.B5.D0.B9.D1.81.D1.8B_.D0.BA.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.82.D0.B8.D1.80.D1.83.D0.B5.D0.BC.D1.8B">Интерфейсы конвертируемы</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=22" title="Редактировать раздел «Интерфейсы конвертируемы»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=22" title="Редактировать раздел «Интерфейсы конвертируемы»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Интерфейс – это объект с несколькими сигналами и их свойствами. Конвертер поддерживает их с помощью имен и mangling.</p> <h4><span class="mw-headline" id=".D0.92.D1.8B.D0.B7.D0.BE.D0.B2_.D1.84.D1.83.D0.BD.D0.BA.D1.86.D0.B8.D0.B9_.D0.BF.D1.80.D0.B5.D0.BE.D0.B1.D1.80.D0.B0.D0.B7.D1.83.D0.B5.D1.82.D1.81.D1.8F_.D0.B2_Verilog_.D0.B8_VHDL-.D0.BF.D0.BE.D0.B4.D0.BF.D1.80.D0.BE.D0.B3.D1.80.D0.B0.D0.BC.D0.BC.D1.8B">Вызов функций преобразуется в Verilog и VHDL-подпрограммы</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=23" title="Редактировать раздел «Вызов функций преобразуется в Verilog и VHDL-подпрограммы»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=23" title="Редактировать раздел «Вызов функций преобразуется в Verilog и VHDL-подпрограммы»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Конвертер анализирует функцию и ее код. Каждая функция переводится в соответствующую подпрограмму на выбранном HDL. Функцию или task в Verilog или function или procedure в VHDL. Чтобы поддерживать возможности функций в Питоне полностью, на каждый вызов функции в Питоне генерируется отдельная подпрограмма</p> <h4><span class="mw-headline" id="if-then-else-.D1.81.D1.82.D1.80.D1.83.D0.BA.D1.82.D1.83.D1.80.D1.8B_.D0.BC.D0.BE.D0.B3.D1.83.D1.82_.D0.B1.D1.8B.D1.82.D1.8C_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D1.8B_.D0.B2_case-.D0.B1.D0.BB.D0.BE.D0.BA.D0.B8">if-then-else-структуры могут быть переведены в case-блоки</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=24" title="Редактировать раздел «if-then-else-структуры могут быть переведены в case-блоки»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=24" title="Редактировать раздел «if-then-else-структуры могут быть переведены в case-блоки»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Питон не поддерживает case-блоки. Но конвертер умеет распознавать условные конструкции, где значение переменной по очереди сравнивается с элементами enum’а и переписывает ее как case-блок с соответствующими свойствами в случае синтеза (вероятно, мультиплексор – авт.)</p> <h4><span class="mw-headline" id=".D0.92.D1.8B.D0.B1.D0.BE.D1.80_.D1.81.D0.BF.D0.BE.D1.81.D0.BE.D0.B1.D0.BE.D0.B2_.D0.BF.D0.B5.D1.80.D0.B5.D0.B2.D0.BE.D0.B4.D0.B0_enum.E2.80.99.D0.BE.D0.B2_.E2.80.93_.D0.BF.D0.B5.D1.80.D0.B5.D1.87.D0.B8.D1.81.D0.BB.D0.B8.D0.BC.D1.8B.D1.85_.D1.82.D0.B8.D0.BF.D0.BE.D0.B2">Выбор способов перевода enum’ов – перечислимых типов</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=25" title="Редактировать раздел «Выбор способов перевода enum’ов – перечислимых типов»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=25" title="Редактировать раздел «Выбор способов перевода enum’ов – перечислимых типов»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Функция enum() в MyHDL возвращает перечислимый тип. Эта функция принимает дополнительный параметр encoding=, который определяет способ ее перевода в HDL: бинарный, one hot, или one cold.</p> <h4><span class="mw-headline" id=".D0.9F.D0.B0.D0.BC.D1.8F.D1.82.D1.8C_.D1.81_.D0.BF.D1.80.D0.BE.D0.B8.D0.B7.D0.B2.D0.BE.D0.BB.D1.8C.D0.BD.D1.8B.D0.BC_.D0.B4.D0.BE.D1.81.D1.82.D1.83.D0.BF.D0.BE.D0.BC_RAM">Память с произвольным доступом RAM</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=26" title="Редактировать раздел «Память с произвольным доступом RAM»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=26" title="Редактировать раздел «Память с произвольным доступом RAM»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Большинство средств синтеза могут переводить Verilog и VHDL, определяющий блоки памяти, прямо в блоки памяти. чтобы поддерживать эту интересную функцию, конвертер преобразует list из Signal’ов в Verilog-памяти или VHDL-массивы.</p> <h4><span class="mw-headline" id=".D0.9F.D0.B0.D0.BC.D1.8F.D1.82.D1.8C_ROM">Память ROM</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=27" title="Редактировать раздел «Память ROM»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=27" title="Редактировать раздел «Память ROM»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Некоторые средства синтеза могут определять ROM из case-конструкций. .Конвертер делает перевод в такие конструкции автоматически, основываясь на высокоуровневом описании. ROM описывается одной строчкой, посредством индексирования tuple’а.</p> <h4><span class="mw-headline" id=".D0.97.D0.BD.D0.B0.D0.BA.D0.BE.D0.B2.D0.B0.D1.8F_.D0.B0.D1.80.D0.B8.D1.84.D0.BC.D0.B5.D1.82.D0.B8.D0.BA.D0.B0">Знаковая арифметика</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=28" title="Редактировать раздел «Знаковая арифметика»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=28" title="Редактировать раздел «Знаковая арифметика»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>В MyHDL работа с отрицательными числами тривиальна: кто-то просто использует объекты intbv с необходимыми ограничениями на их значения. В отличие от этого метода, Verilog и VHDL обрабатывают знаковую и беззнаковую арифметику абсолютно по-разному. чтбы работать с отрицательными числами, пользователь должен строго указать, что эта переменная – знаковая. Но когда в одном выражении присутствуют и знаковые, и беззнаковые числа, всё становится сложнее.</p> <p>Когда в Verilog в одном выражении есть знаковые и беззнаковые числа, все считаются беззнаковыми. Очевидно, это приводит к непредсказуемым результатам. Разработчик вынужден будет добавлять знаковые расширения и преобразования типов для решения этой проблемы.</p> <p>В VHDL знаковые и беззнаковые числа в одном выражении просто не будут работать. Разработчик будет вынужден вручную преобразовывать их к знаковым типам и только после этого включать в выражение.</p> <p>В MyHDL эти проблемы не существуют, потому что intbv работают сразу как integer’ы. Более того, конвертер автоматизирует громоздкие task’и, которые необходимы в Verilog и &#160;VHDL. Он использует знаковые и беззнаковые типы в зависимости от ограничений по значениям, которые мы задаем при задании intbv(значение, нижняя граница=нижняя граница, верхняя граница=верхняя граница).</p> <h4><span class="mw-headline" id=".D0.9F.D0.BE.D0.BB.D1.8C.D0.B7.D0.BE.D0.B2.D0.B0.D1.82.D0.B5.D0.BB.D1.8C.D1.81.D0.BA.D0.B8.D0.B9_.D0.BA.D0.BE.D0.B4">Пользовательский код</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=29" title="Редактировать раздел «Пользовательский код»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=29" title="Редактировать раздел «Пользовательский код»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h4> <p>Если необходимо, пользователь может обходить конверсию и писать свой собственный код, который вставится непосредственно в конченый проект.</p> <h2><span class="mw-headline" id=".D0.9A.D0.BE.D0.BD.D0.B2.D0.B5.D1.80.D1.82.D0.B8.D1.80.D1.83.D0.B5.D0.BC.D0.BE.D0.B5_.D0.BC.D0.BD.D0.BE.D0.B6.D0.B5.D1.81.D1.82.D0.B2.D0.BE">Конвертируемое множество</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=30" title="Редактировать раздел «Конвертируемое множество»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=30" title="Редактировать раздел «Конвертируемое множество»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h2> <h3><span class="mw-headline" id=".D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5_3">Введение</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=31" title="Редактировать раздел «Введение»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=31" title="Редактировать раздел «Введение»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Неудивительно, что не любой код на MyHDL может быть сконвертирован в HDL. Хотя ограничения не сильно значительные, конвертируемое подмножество намного шире, чем синтезируемое подмножество, которое является производственным стандартом. Другими словами, код на MyHDL, написанный в соответствии с правилами для синтезируемых устройств, будет конвертируемым. Хотя возможно также написать конвертируемый код для несинтезируемых моделей и тестбенчей.</p> <p>Конвертер пытается выдавать понятные сообщения об ошибках, когда натыкается на конструкцию, которую сконвертировать невозможно.</p> <h3><span class="mw-headline" id=".D0.9A.D0.BE.D0.B4.D1.81.D1.82.D0.B0.D0.B9.D0.BB">Кодстайл</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=32" title="Редактировать раздел «Кодстайл»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=32" title="Редактировать раздел «Кодстайл»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Действительно важное ограничение на конвертируемый код – это то, что он должен быть написан в MyHDL-стиле: генераторы, общение через сигналы, работа по клоку или по другому сигналу.</p> <p>Для чистого моделирования неважно, какие генераторы Вы используете. Хотя в конвертируемом коде они должны быть созданы с помощью декораторов из MyHDL:&#160; &#160;<b>instance()</b>, <b>always()</b>, <b>always_seq()</b>, или <b>always_comb()</b>.</p> <h3><span class="mw-headline" id=".D0.9F.D0.BE.D0.B4.D0.B4.D0.B5.D1.80.D0.B6.D0.B8.D0.B2.D0.B0.D0.B5.D0.BC.D1.8B.D0.B5_.D1.82.D0.B8.D0.BF.D1.8B">Поддерживаемые типы</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=MyHDL&amp;veaction=edit&amp;vesection=33" title="Редактировать раздел «Поддерживаемые типы»" class="mw-editsection-visualeditor">править</a><span class="mw-editsection-divider"> | </span><a href="/w/index.php?title=MyHDL&amp;action=edit&amp;section=33" title="Редактировать раздел «Поддерживаемые типы»">править вики-текст</a><span class="mw-editsection-bracket">]</span></span></h3> <p>Наиболее значимое ограничение накладывается на типы. Только ограниченное количество типов может быть скомпилировано. Питоновские int и long конвертируются в integer’ы Verilog и VHDL. Все другие поддерживаемые типы должны быть снабжены длиной в битах. Поддерживаемые типы из Питона – bool, intbv из MyHDL, и enum из MuHDL.</p> <p>Объекты Intbv должны быть сконструированы с той длиной, которую они определяют сами с помощью нижнего и верхнего ограничения, которое задаем мы сами:</p> <p>index <b>=</b> intbv(0, min<b>=</b>MIN, max<b>=</b>MAX)</p> <p>Verilog-конвертер поддерживает intbv с отрицательными значениями.</p> <p>Еще можно создавать его с помощью слайсинга вот так:</p> <p>index <b>=</b> intbv(0)[N:]</p> <p>Это выражение даст нам число 0 с максимальным значением 2**N, то есть длиной N.</p> <p>Конвертер вообще поддерживает еще некоторые типы, основанные на списках и кортежах(tuples). (Источник - документация к MyHDL) <a rel="nofollow" class="external free" href="http://docs.myhdl.org/en/stable/manual/index.html">http://docs.myhdl.org/en/stable/manual/index.html</a></p> <!-- NewPP limit report Parsed by mw1129 Cached time: 20151116113019 Cache expiry: 2592000 Dynamic content: false CPU time usage: 0.055 seconds Real time usage: 0.054 seconds Preprocessor visited node count: 179/1000000 Preprocessor generated node count: 0/1500000 Post‐expand include size: 0/2097152 bytes Template argument size: 0/2097152 bytes Highest expansion depth: 2/40 Expensive parser function count: 0/500 Number of Wikibase entities loaded: 0--> <!-- Transclusion expansion time report (%,ms,calls,template) 100.00% 0.000 1 - -total --> '
Была ли правка сделана через выходной узел сети Tor (tor_exit_node)
0
Unix-время изменения ($1) (timestamp)
1447673419