Игра жизни Конвея на ПЛИС

ИгражизниКонвеянаПЛИС
ПРОЕКТЫ , FPGA

Когда я изучаю новый язык программирования, мне нравится решать четко сформулированную, но нетривиальную проблему. Игра жизни Конвея (GoL) подходит под это определение. У него достаточно глубины, чтобы обнаружить различные компромиссы. Естественно, когда я взял Chisel язык описания оборудования (HDL ), Я хотел построить Game of Life на FPGA. Получилось намного интереснее, чем программно. В этом посте я буду следить за моим прогрессом от написания кода Chisel и Verilog до запуска GoL на Digilent Arty A7 и просмотр изображений в реальном времени на экране VGA.

Chisel в относительно новом HDL, созданном сообществом Berkeley и RISC V. Он использует язык программирования Scala в качестве основы и определяет HDL как предметно-ориентированный язык поверх него. По сути, Chisel – это просто набор библиотек Scala. Это позволяет использовать всю мощь языка программирования общего назначения для создания аппаратных абстракций более высокого порядка. Этот подход кажется почти противоположным тому, как развивались традиционные HDL, такие как Verilog. Первоначально Verilog фокусировался на описании оборудования – очень близком к тому, что можно было бы выразить с помощью обычной схемы, – а позже добавил элементы программирования общего назначения для создания более сложных компонентов.

Архитектура

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

Клетка

 класс Клетка  расширяется   Модуль   {  val   io   знак равно   IO   ( новый Пучок  {  val  включить  знак равно  Вход  (  Bool   ())   val   соседи   знак равно  Vec   (  8  ,  Вход (  Bool   ()))   val  государственный знак равно  Выход  (  Bool   ())   val   writeEnable   знак равно Вход  (  Bool   ())   val   writeState  знак равно  Вход (  Bool   ())  })   val  государственный знак равно   RegInit   ( ложный .   B  )  когда  (  io  .  включить )   { когда (  io  .   writeEnable  )   { государственный :  знак равно   io  .   writeState  }.  иначе  {  val  считать  знак равно  io  .   соседи  .   foldRight   ( ( 0.   U   (  3.   W  )) (( Икс:   Bool  ,   y  :   UInt  )   =>  Икс .   ВСПОМОГАТЕЛЬНОЕ   +   y  )  когда (считать  <  2.   U  )   { государственный :   знак равно ложный.   B   }.   в противном случае, когда   (считать знак равно ==   2.   U  )   { государственный :   знак равно государственный }.   иначе когда   ( считать  ===   3.   U  )   { государственный  :   знак равно правда .   B  }.  иначе  { государственный :  знак равно  ложный .   B  }  }  }   io  . государственный  :   знак равно государственный }   

Входы модуля ячейки включено, writeEnabled , writeState и восьми соседних состояний. Когда

включен равно 1, ячейка либо вычисляет свое следующее состояние в каждом тактовом цикле, либо, когда writeEnabled равно 1, устанавливает уровень writeState как новое состояние. Когда

включен = 0, ячейка сохраняет свое состояние неизменным. Единственным выходом модуля является текущее состояние.

За связкой ввода / вывода следует регистр состояния с часами и сброс, неявно подключаемый Chisel. Далее следует комбинационная схема, которая вычисляет правила GoL, имеющие соседей и собственные биты текущего состояния в качестве входных и новое состояние в качестве выходных.

Самая интересная строка - это строка, которая генерирует серию сумматоров для вычисления количества соседей. Складка Scala