Главная » 2016 » Декабрь » 10 » Функциональные языки в разработке аппаратуры!
19:26
Функциональные языки в разработке аппаратуры!

Простой способ моделировать сигналы — представить их списками значений в каждый момент времени. Если один сигнал равен другому со смещением на один квант во времени, мы просто добавляем в начала списка 0

delay s = 0:s

>>или так:

Можно создать свой тип для сигналов — это эффективнее, безопаснее и правильнее, но для простоты мы пока ограничимся использованием простых списков.

data Signal v = S v (Signal v) delay v s = S v s

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

RS-триггер представляет из себя два NOR-узла, соединенные взаимно-рекурсивно. У этой системы есть два стабильных состояния, в которых на выходе одного NOR единица, а другого — ноль. Подавая единицу на второй вход одного из NOR-узлов можно переключать состояния.
Вообще говоря RS-триггер — асинхронная схема. Но для простоты примера мы будем моделировать ее как синхронную, что не совсем верно (даже выбрав короткий размер «такта» сложно смоделировать переходные процессы, лучше воспользоваться другим представлением сигнала).

nor '_' '_' = '~'

nor _ _ = '_'

rs r s = (q, nq) where

q = '_' : zipWith nor r nq

nq = '_' : zipWith nor q s

main = let

r = "~_______"

s = "___~~___"

(q,nq) = rs r s

in do

print r

print s

print q

print nq

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

macD r x y = acc

where

prods = zipWith (*) x y

sums = zipWith (+) acc prods

acc = 0 : zipWith (\r v -> if r == 1 then 0 else v) r sums

 

macS r x y = scanl macA 0 $ zip3 r x y

  where

                macA acc (r,x,y) = if r == 1 then 0 else acc+x*y

Здесь описаны две эквивалентные модели операции MAC (умножение со сложением) с аккумулятором. macD — с использованием рекурсивного сигнала с задержкой, macS — с использованием явно описанного состояния.

Если подмножество Haskell так хорошо моделирует синхронную аппаратуру, то почему бы из него не синтезировать HDL?
Есть несколько проектов расширения компилятора, которое позволяет это делать: коммерческий Bluespec, свободные Lava и CλaSH.

Clash

В качестве примера я хочу рассмотреть Clash, так как он умеет компилировать и в VHDL, и в SystemVerilog, и в старый добрый Verilog (который меня привлекает тем, что используется не только в микроэлектронике :-)).
Процесс инсталляции достаточно подробно описан на сайте. К нему стоит отнестись внимательно — во первых заявлена совместимость с ghc-7.x (то есть с 8.x может не работать), во вторых не надо пробовать запускать «cabal install clash» — это устаревший пакет, надо устанавливать clash-ghc («cabal install clash-ghc --enable-documentation»).
Исполняемый файл clash (или clash.exe, в зависимости от OS) будет установлен в директорию "~/.cabal/bin", лучше добавить ее в $PATH.
Основной узел, с которого clash начинает компиляцию, называется topEntity, который представляет из себя функцию из входящего сигнала в исходящий (естественно, сигналы могут быть составные).
Например, рассмотрим однобитный сумматор:

      topEntity :: Signal (Bool, Bool) -> Signal (Bool, Bool)

  topEntity s = fmap (\(s1,s2) -> (s1 .&. s2, s1 `xor` s2)) s

 

fmap превращает функцию от пары логических величин в функцию от сигнала.
Откомпилировать файл в verilog можно командой «clash --verilog ADD1.hs»

Для работы с состоянием можно использовать автоматы Мура и Мили. Рассмотрим делитель частоты, сначала с помощью автомата Мура.

data DIV3S = S0 | S1 | S2

div3st S0 _ = S1

div3st S1 _ = S2

div3st S2 _ = S0

 

div3out S2 = True

div3out _ = False

topEntity :: Signal Bool -> Signal Bool

topEntity = moore div3st div3out S0

data — это конструкция Haskell описывающая тип данных.
В этой программе мы описываем тип DIV3S представляющего состояние нашего автомата. Возможные значения этого типа перечислены через '|' — S0, S1 и S3.
div3st — функция состояния (символом "_" принято называть неиспользуемый параметр, в данном случае значение входного сигнала).
div3out — функция из состояние в величину выходного сигнала.
 

Библиотечная функция moore создает узел по двум этим функциям и начальному состоянию.

То же самое с автоматом Мили:

      data DIV3S = S0 | S1 | S2

  div3 S0 _ = (S1, False)

div3 S1 _ = (S2, False)

div3 S2 _ = (S0, True)

topEntity :: Signal Bool -> Signal Bool

topEntity = mealy div3 S0

В Clash вместо списков используются вектора фиксированного размера и большинство библиотечных функций переопределено на работу с ними. Добраться до стандартных списковых функций можно добавив в файл (или выполнив в REPL) строчку

import qualified Data.List as L

 

После этого можно использовать функции, явно указав префикс «L.». Например

*DIV3Mealy L> L.scanl (+) 0 [1,2,3,4]

[0,1,3,6,10]

С векторами работают большинство привычных списковых функций.

    *DIV3Mealy L> scanl (+) 0 (1 :> 2 :> 3 :> 4 :> Nil)

     <0,1,3,6,10>

     *DIV3Mealy L> scanl (+) 0 $(v [1,2,3,4])

<0,1,3,6,10>

Но там много тонкостей, за подробностями стоит обратиться к документации.
Руководство с примерами можно посмотреть здесь.
На сайте есть примеры проектов на Clash, в частности реализация процессора 6502.

Перспективы

Haskell очень мощный язык, и его возможно использовать для разработки DSL, например для разработки программного интерфейса устройства (с генерацией, кроме HDL, еще и через Ivory драйверов и эмуляторов для систем виртуализации), или описания архитектуры и микроархитектуры (с генерацией LLVM backend, оптимизирующий для данной микроархитектуры).
 

 

 

 
 
Просмотров: 476 | Добавил: Вова2119 | Рейтинг: 0.0/0
Всего комментариев: 0
avatar
Яндекс.Метрика
24 log 24 LOG statistick
счетчик посещений
Если вы видите это,
то ваш браузер устарел
и не поддерживает технологий
CSS 3.0