Создаём простейший проект для ПЛИС Lattice в среде Litex

В предыдущей паре статей я рассуждал про ПЛИС Lattice. Давайте сегодня поговорим об эффективной разработке под них. Один наш крупный руководитель уверяет, что из разговоров с иностранными Заказчиками, он выяснил, что российских разработчиков считают пишущими классные вещи, но делающими это крайне медленно. Ну, разумеется, по сравнению с разработчиками из других стран. И разумеется, «классные» идёт на первом месте.

Один из методов обхода проблемы скорости разработки – использование готовой инфраструктуры для проектов. Я делал цикл статей про комплекс Redd, где продвигал использование готовой шинной инфраструктуры в среде разработки Quartus. Сначала это была честная процессорная система, потом – процессор мы изъяли, а шины – оставили.

Создаём простейший проект для ПЛИС Lattice в среде Litex
Перечень статей этого цикла – под катом.

1.     Разработка простейшей «прошивки» для ПЛИС, установленной в Redd, и отладка на примере теста памяти
2.     Разработка простейшей «прошивки» для ПЛИС, установленной в Redd. Часть 2. Программный код
3.     Разработка собственного ядра для встраивания в процессорную систему на базе ПЛИС
4.     Разработка программ для центрального процессора Redd на примере доступа к ПЛИС
5.     Первые опыты использования потокового протокола на примере связи ЦП и процессора в ПЛИС комплекса Redd
6.     Веселая Квартусель, или как процессор докатился до такой жизни
7.     Методы оптимизации кода для Redd. Часть 1: влияние кэша
8.     Методы оптимизации кода для Redd. Часть 2: некэшируемая память и параллельная работа шин
9.     Экстенсивная оптимизация кода: замена генератора тактовой частоты для повышения быстродействия системы
10.   Доступ к шинам комплекса Redd, реализованным на контроллерах FTDI
11.   Работа с нестандартными шинами комплекса Redd
12.   Практика в работе с нестандартными шинами комплекса Redd
13.   Проброс USB-портов из Windows 10 для удалённой работы
14.   Использование процессорной системы Nios II без процессорного ядра Nios II
15.   Практическая работа с ПЛИС в комплекте Redd. Осваиваем DMA для шины Avalon-ST и коммутацию между шинами Avalon-MM

Затем я делал пару циклов, где активно использовал данный подход. Мне приходилось дописывать только проблемно-ориентированные модули, остальное — среда разработки создавала за меня.

Цикл про логический анализатор – под катом
 А под этим катом – цикл про шинный анализатор USB

Есть ли что-то подобное для Lattice в частности и сцепки Yosys/NextPNR в целом? Вы не поверите! Решение не просто есть, но оно ещё и настолько кроссплатформенное, что подойдёт и для Yosys/NextPNR, и для Quartus, и для Vivado! И называется оно Litex. Итак, давайте попробуем поэкспериментировать с ним для подхода, который я уже давно продвигаю: «делаем штатную основу, а на неё нанизываем свои целевые блоки». Причём кто знает, что такое Litex  – не пугайтесь! Свои блоки мы будем делать на Верилоге!

Введение

Первое, что надо сказать про Litex – там всё делается на Питоне. А я с этим языком до сих пор не был знаком. Но в целом, для начала работы оказалось достаточно основ.

Дальше, если вы попробуете почитать руководства по этой системе, то найдёте утверждения, что не надо ничего делать на Верилоге, всё можно описать на Питоне. Даже сами модули. Да, можно. Но если сравнить объём кода, его читаемость, а главное – количество вещей, которые надо держать в голове, на мой взгляд, этот подход сильно проигрывает прямой разработке на Верилоге. Поэтому не волнуйтесь, я не буду учить этому.

Чего Litex умеет делать классно – это создавать базовую шинно-ориентированную систему. Для него уже разработаны штатные контроллеры: SDRAM, UART, I2C, SPI, многие другие. Он может в качестве базовой шины использовать Wishbone, так что на эту систему вполне можно навесить многие ядра с сайта OpenCores.org (просто я участвовал в проекте, где это делалось). Так что мы будем пользоваться именно этим полезным свойством.

Дальше, в базе этой среды уже имеется масса готовых плат на базе Altera (Intel), Xilinx и Lattice. Поэтому наши разработки будут кроссплатформенными,.ведь мы, в принципе, сможем переключать верхний уровень с одной платы на другую, не особо вглядываясь, ПЛИС какой фирмы там стоит. Верхний уровень создан за нас точно под ту ПЛИС, какую мы указали. С учётом её специфики! Лишь бы она была в базе Litex, а база там шикарная.

А в пределах платы,  уже описаны ножки по группам.  Скажем, есть группа SDRAM. Есть группы «разъём J1» и так далее. При компиляции проекта достаточно указать версию платы, чтобы автоматически подключилась нужная нам цоколёвка. Так что мы описываем свой проект в абстракции конечного пользователя. Конечный пользователь же видит на схеме разъём… Ну пусть J15. Вот к его ножкам он и подключается! А как там на плате проброшено всё – его не должно интересовать.

В общем, мы пользуемся всем полезным и не пользуемся тем, что кажется нам бесполезным. Да, нам придётся чуть-чуть написать на Питоне. Но при работе с Квартусом мы тоже создавали странные файлы, просто нам в этом помогала GUI среда. А так – тоже на странном языке связки писались.

По уму, надо было бы построить большой цикл лекций, плавно вводя в курс. Интересное началось бы лекции с третьей-четвёртой… Но после великого провала с циклом статей про Teensy я больше не планирую далее, чем на одну статью вперёд. Обидно тратить уйму сил на то, что на самом деле, не пользуется спросом. Поэтому будем начинать сразу с интересного, нарушая некоторые академические принципы. Зато не надо держать в столе кучу статей, на которые потрачены силы, уже зная при этом, что они мало кому будут нужны, как это было в случае с Тинсей. Лучше потратить силы на другое. Поэтому я просто кратко расскажу про виды подключения к шине, и мы сразу ринемся в бой. Систему мы сделаем совершенно спартанскую, но потом узнаем, как можно добавить к ней автодокументирование… Или не узнаем… Но потом.

Шины и регистры команд-состояния

При разработках под Альтеру я всегда подключался к шине Avalon-MM, либо гонял потоки по шине Avalon-ST. Здесь мы тоже можем подключать наши устройства к шине Wishbone. Но вдобавок к этому нам предоставляется возможность создания собственных регистров команд и состояния (они же Command Status Registers, они же CSR).  

В Альтеровских проектах я должен был сам заводить эти регистры, сам декодировать адрес регистра команд, адрес регистра состояния на шине, сам сопоставлять сущности Верилоговского кода с этими регистрами. Тут же за меня всё может сделать автоматика.

То есть я беру самый что ни на есть простейший Verilog модуль. Скажем, такой:

module gpu(
input        clk, 
output       hsync, 
output       vsync, 
output       color, 
input [15:0] x0, 
input [15:0] x1, 
input [15:0] y0, 
input [15:0] y1);

Линия clk должна заходить откуда-то из тактового генератора

Линии color, hsync, vsync явно должны выходить на ножки ПЛИС, так как они уйдут в видеоразъём (в нашем случае, в VGA).

А линии x0, x1, y0 и y1 – это управляющие входы, задающие координаты рисуемого на экране прямоугольника. Они должны устанавливаться по шине.

И вот, чтобы не переделывать такой модуль под работу с шиной Wishbone (а завтра нам может вообще понадобится AXI), мы вполне можем подключить x0, x1, y0 и y1 к линиям CSR. Так как это намного проще, чем самим ещё реализовывать работу с шиной, то с этого и начнём. Превратим многострадальную плату Colorlight 5A‑75B в VGA адаптер, воспользовавшись Verilog кодом, найденным в Интернете. Этот код как раз формирует VGA сигнал. Жаль только, что такая видеокарта майнить не сможет. Правда, сначала нам надо установить тот самый Litex, при помощи которого мы будем всё делать.

Устанавливаем Litex

Установить Litex не просто, а очень просто. Это одинаково легко делается и в Windows, и в Linux. Сначала ставим Питон. Без него никак. Далее, идём на страницу проекта: enjoy-digital/litex: Build your hardware, easily!, там есть инструкция. Достаточно скачать и запустить один единственный Питоновский скрипт. Имейте только в виду, что он насосёт кучу каталогов на один уровень иерархии выше себя.  Вот этот файл на моей машине:

Как видим, он живёт на уровне D:TATTICELitexlitex

А теперь смотрите, чего он натворил на уровне D:LatticeLitex! До установки там был только один каталог litex:

Имейте в виду! Иначе придётся потом чистить каталог… Я чистил…

Ну всё. Чтобы статья не разрослась слишком сильно, заканчиваем теорию, приступаем к практике. Её будет много.

Создаём проект

Все руководства, которые мне удавалось найти, рассчитаны на специалистов как по Питону, так и по Litex. Там всё показано в общих чертах. Мол, это правим так, это так, это так… Ага! А я сижу, пялюсь, как баран на новые ворота, и не понимаю, что же авторы имели в виду! Поэтому давайте во всём разберёмся во всех подробностях.

Начнём с источника вдохновения. Самый лучший пример я нашёл вот тут: https://gist.github.com/hinzundcode/129d0f81fe24257240d55401f04cdb5a

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

Итак. Начинаем делать свой проект. Для этого запускаем Visual Studio (разумеется, в ней должна быть включена поддержка Питона) и создаём новый проект.  Ищем шаблон Python:

И в получившемся списке выбираем Питон:

Имя проекта я выбрал mypytest, путь расположения тоже выбрал попроще. Остальное – оставил по умолчанию.

Всё, создаём проект. Уффф. И что туда писать? Идём в каталог, куда установился Litex и там открываем litex-boards. В нём — litex_boards. И там самые интересные каталоги – platforms и targets. В обоих каталогах есть файлы, имя которых напоминает нам имя нашей платы:

В платформах описано всё про нашу плату. Мало того, там описано не просто про нашу плату, а про разные её версии. Я уже отмечал, что цоколёвка ПЛИС в разных версиях вдрызг разная. Благодаря этому описанию, мы можем через опции для Питоновского скрипта выбрать плату и версию, а система автоматически подтянет номера ножек. Этим мы скоро воспользуемся. Но сначала мы пойдём в каталог targets. Именно там описывается целевая система. Давайте заглянем в файл Litexlitex-boardslitex_boardstargetscolorlight_5a_75x.py. Он состоит из двух больших частей. Первая часть описывает классы. А именно – класс тактовых доменов _CRG и класс базовой платформы BaseSoC. Мы вполне можем воспользоваться этими базовыми классами, хотя можем и написать что-то своё по образу и подобию. Своё писать мы будем, когда станем крутыми. Сейчас для нас и так слишком много новой информации. Так что будем пользоваться готовым.

Дальше идёт описание функции main(). Вот это дело нам нужно скопировать в свой файл, который мы создали в Visual Studio.

Первые модификации проекта

Итак. Мы скопировали всё, начиная со строки

def main():

и до конца файла colorlight_5a_75x.py к себе в проект. Можно запускать на пробу? Не совсем.

Первое, что следует сделать – разобраться, какой Питон у нас используется. У меня на машине стоял Питон 3.7, который установился вместе с Visual Studio, а также Питон 3.10, в котором была установлена среда Litex. Само собой из-под Студии всё пыталось запуститься под версией 3.7, а там ничего такого не было установлено. И ничего не работало. Поэтому выбираем пункт меню Средства->Python->Окружения Python:

И в появившемся окне выбираем ту версию, под которую был установлен Litex. А потом на моём скриншоте написано: «Это окружение по умолчанию для новых проектов». А если выбрана иная версия, на этом месте будет предложено сделать эту версию средством по умолчанию. Я им воспользовался.

Теперь мы должны задать несколько опций запуска проекта. Ведь при запуске программы на Питоне скрипту передаются ключи с опциями. Надо задать их и здесь. Для этого выбираем пункт меню Отладка->Свойства отладки для проекта:

И там я перешёл на вкладку «Откладка» и вписал ключи, добавляющие в систему мост Ethernet-Wishbone и задающие ip-адрес моей платы на шине Ethernet.

 —with-etherbone —eth-ip=192.168.2.128

Уффф. Подготовка окружения завершена. Специалисты по Питону нервно стучат в монитор и кричат, что запускаться ещё рано, но я хочу не донести до читателя «Запомните и повторяйте». Я хочу, чтобы читатель научился разбираться в диалоге с системой, чего стоит добавить. Поэтому запускаем код! И сразу же получаем исключение

Помните, как в языке Си надо мучиться с директивами #include? Вот сейчас мы тоже немного помучаемся. Перво-наперво, добавляем в самое начало файла строку:

from migen import *

Её придётся запомнить. Она – основа всего! Если что,  Migen – это девичья фамилия Litexа. 

А вот дальше – можно уже что-то выводить опытным путём. Помните, я показывал два крайне важных файла с платформой и целевой системой? Их надо включить. И вот, что удивительно. Давайте рассмотрим путь к первому файлу:

Путь, относительно корня для Litex нам и надо вбить. Причём в Visual Studio к нашим услугам будет IntelliSense. Начинаем писать:

Видите? Нам уже начинают подсказывать. Выберем litex_boards, нажмём точку – нам подскажут дальше

Вот так легко и непринуждённо мы импортируем всё необходимое из тех самых полезных двух файлов. И первые три строки нашего файла будут:

from migen import *
from litex_boards.targets.colorlight_5a_75x import *
from litex_boards.platforms.colorlight_5a_75b import *

Прекрасно! Пробуем запуститься… Получаем вот такое безобразие

Какой такой тулчейн? Какой такой компилятор? Дело в том, что по умолчанию, нам в систему пытаются добавить процессорное ядро. А сегодня оно нам не нужно. Нам нужна только шина с устройствами на ней. Мастером будет мост Ethernet to Wishbone, который мы недавно добавили в опциях. Давайте откажемся от процессора.

Отказываемся от процессорного ядра

Если коротко, то нам надо вот сюда:

    soc = BaseSoC(board=args.board, revision=args.revision,
        sys_clk_freq     = int(float(args.sys_clk_freq)),
        with_ethernet    = args.with_ethernet,
        with_etherbone   = args.with_etherbone,
        eth_ip           = args.eth_ip,
        eth_phy          = args.eth_phy,
        use_internal_osc = args.use_internal_osc,
        sdram_rate       = args.sdram_rate,
        **soc_core_argdict(args)    
        )

 Добавить в конец пару опций. Чтобы получилось:

То же самое текстом.
soc = BaseSoC(board=args.board, revision=args.revision,
        sys_clk_freq     = int(float(args.sys_clk_freq)),
        with_ethernet    = args.with_ethernet,
        with_etherbone   = args.with_etherbone,
        eth_ip           = args.eth_ip,
        eth_phy          = args.eth_phy,
        use_internal_osc = args.use_internal_osc,
        sdram_rate       = args.sdram_rate,
        cpu_type         = None,
        cpu              = None,
        **soc_core_argdict(args)
    )

Но мы договорились, что я учу понимать, как делать всё в диалоге с системой. Поэтому начинаю обосновывать. Вот в этой статье Экспериментируем с ПЛИС семейства ECP5 фирмы Lattice / Хабр (habr.com) я уже приводил ссылку на проект enjoy-digital/colorlite: Take control of your Colorlight FPGA board with LiteX/LiteEth 🙂 (github.com). В нём авторы добиваются работы без процессорного ядра. Как? Смотрим класс целевой системы, который используем мы:

class BaseSoC(SoCCore):

А вот так описано в классе, на который я только что сослался:

class ColorLite(SoCMini):

Автор наследуется не от SoCCore, а от SoCMini. Переписываем всё? Сначала давайте осмотримся. Где эти вещи описаны? Осматриваемся в списке включений… Не видим там никаких SoCMini, но видим:

from litex.soc.integration.soc_core import *

Мы уже грамотные и умеем декодировать эти строки. Вернее, выдёргивать из них путь к файлу. Идём в файл litexsocintegrationsoc_core.py и выясняем, что SoCMini описан вот так:

class SoCMini(SoCCore):
     def __init__(self, *args, **kwargs):
        if "cpu_type" not in kwargs.keys():
            kwargs["cpu_type"] = "None"
        if "integrated_sram_size" not in kwargs.keys():
            kwargs["integrated_sram_size"] = 0
        if "with_uart" not in kwargs.keys():
            kwargs["with_uart"] = False
        if "with_timer" not in kwargs.keys():
            kwargs["with_timer"] = False

        SoCCore.__init__(self, *args, **kwargs)

Он вполне себе наследуется от SoCCore, просто перекрывает несколько аргументов. Честно говоря, дальше я действовал по наитию. Я нашёл вот такие строки:

def soc_core_args(parser):
…
    parser.add_argument("--cpu-type",          default=None,                     help="Select CPU: {}, (default=vexriscv).".format(", ".join(iter(cpu.CPUS.keys()))))
    parser.add_argument("--cpu-variant",       default=None,                     help="CPU variant (default=standard).")

Выяснил методом трассировки, что переменные cpu и cpu_type по факту не равны None, а должны быть… И поэтому дописал в аргументы

        cpu_type         = None,
        cpu              = None,

Помогло. А работает – не тронь. Вот так я вывел те строки, которые нужны. Вы, при необходимости, сможете пройти тем же путём, чтобы не запоминать все константы.

Ура! Система создана!

Но проку от неё пока мало. Давайте добавим в систему свой модуль не Верилоге. Как я уже отмечал, основные файлы и идеи мы будем брать отсюда:

fomu litex vga (github.com)

Добавляем свой модуль на Верилоге

Копируем файлы gpu.v и hvsync_generator.v туда же, где живёт наш создаваемый питоновский скрипт. Нам предстоит подключиться вот к такому Верилоговскому модулю:

module gpu(clk, hsync, vsync, color, x0, x1, y0, y1);
  input clk;
  output hsync, vsync;
  output color;
  wire pclk;
  wire display_on;
  wire signed [15:0] hpos;
  wire signed [15:0] vpos;
  
  input signed [15:0] x0;
  input signed [15:0] x1;
  input signed [15:0] y0;
  input signed [15:0] y1;

Как видим, нам предстоит:

  • Подключиться к тактовой линии из тактового домена,

  • Пробросить линии hsync, vsync и color на ножки микросхемы,

  • Подавать на линии x0, x1, y0 и y1 сигналы из Control/Status регистров (то есть из модуля CSR).

Статус, правда, мы сегодня считывать не научимся, но всему своё время. Статья и так огромная, нам бы хоть что-то освоить. Также по причине огромности статьи, мы сейчас сделаем всё крайне просто. Красоту будем учиться наводить позже, если тема вообще будет иметь спрос. Если не будет, то описывать всё равно не будет смысла.

Итак. Открываем пример workshop_vga.py и переносим оттуда класс к нам, выше функции main:

from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
class GPU(Module, AutoCSR):
    def __init__(self, pins, clk):
        self.x0 = CSRStorage(16, reset=100)
        self.x1 = CSRStorage(16, reset=150)
        self.y0 = CSRStorage(16, reset=100)
        self.y1 = CSRStorage(16, reset=200)
        self.comb += [
            pins[1].eq(0),
        ]
        self.specials += Instance(
            'gpu',
            i_clk=clk,
            i_x0=self.x0.storage,
            i_x1=self.x1.storage,
            i_y0=self.y0.storage,
            i_y1=self.y1.storage,
            o_hsync=pins[2],
            o_vsync=pins[3],
            o_color=pins[0]
        )

Я специально добавил строку импорта непосредственно перед классом. Как все уже догадались, я сначала добавил класс, потом попробовал запустить скрипт, а потом – понял, чего не хватает.

Класс GPU наследуется не только от Module, но ещё и от AutoCSR. Этот класс позволяет нам генерировать регистры команд и статуса, проецируя их на адресное пространство шины (в нашем случае, шины Wishbone). Мы добавили четыре 16-разрядных регистра. Также мы берём содержимое некоего… Ох и не силён я в Питоновской терминологии… Списка, да? В этом списке мы передаём сведения об используемых ножках микросхемы.

Пробуем запуститься, убеждаемся, что всё собралось без ошибок.

Добавляем сведения о ножках микросхемы

А сейчас мы сделаем шаг, идею которого я подсмотрел в проекте, который почему-то не содержит внутри себя никаких ссылок, и не ищется Гуглем. Поэтому отмечаю, что я его не сам написал, но где нашёл – не помню. Там есть вот такое полезное описание ножек gpio со ссылкой на разъёмы. Как я уже говорил, ножки ПЛИС с контактам разъёмов могут идти разные. Задавая в опциях версию платы, мы всегда будем уверены, что система подберёт нам правильные ножки ПЛИС! При всех недостатках Litex, этого у неё не отнять! Эта система о нас забоится! Помещаем текст в начало нашего файла (но уже после строк импорта). Я приведу только ключевой фрагмент. Остальное можно будет посмотреть в полном проекте (а если убрать многоточие, то для наших сегодняшних задач хватит и этого):

_gpios = [
    # Attn. Jx/pin descriptions are 1-based, but zero based defs. used!

    # J1
	("gpio", 0, Pins("j1:0"), IOStandard("LVCMOS33")), # Input now
    ("gpio", 1, Pins("j1:1"), IOStandard("LVCMOS33")), # Input now
    ("gpio", 2, Pins("j1:2"), IOStandard("LVCMOS33")), # Input now   
    # GND
	("gpio", 3, Pins("j1:4"), IOStandard("LVCMOS33")), # Input now
	("gpio", 4, Pins("j1:5"), IOStandard("LVCMOS33")), # Input now
    ("gpio", 5, Pins("j1:6"), IOStandard("LVCMOS33")), # Input now
    ("gpio", 6, Pins("j1:7"), IOStandard("LVCMOS33")),
    ("gpio", 7, Pins("j1:8"), IOStandard("LVCMOS33")),
    ("gpio", 8, Pins("j1:9"), IOStandard("LVCMOS33")),
    ("gpio", 9, Pins("j1:10"), IOStandard("LVCMOS33")),
    ("gpio", 10, Pins("j1:11"), IOStandard("LVCMOS33")),
    ("gpio", 11, Pins("j1:12"), IOStandard("LVCMOS33")),
    ("gpio", 12, Pins("j1:13"), IOStandard("LVCMOS33")),
    ("gpio", 13, Pins("j1:14"), IOStandard("LVCMOS33")),
    # GND

    # J2
	("gpio", 14, Pins("j2:0"), IOStandard("LVCMOS33")), # Input now   
    ("gpio", 15, Pins("j2:1"), IOStandard("LVCMOS33")), # Input now  
…
    ("gpio", 111, Pins("j8:14"), IOStandard("LVCMOS33")),
    # GND
]

Формируем нужный блок в системе

Итак. Верилоговские файлы на включение скопированы, класс-обёртка сделан, ножки gpio описаны. Инфраструктура готова! Начинаем внедрение! Внедряться будем между объявлением нашей системы SoC и её построением:

Добавляем список используемых ножек:

soc.platform.add_extension(_gpios) # General LED outputs        
touch_pins = [
      soc.platform.request("gpio", 0),
      soc.platform.request("gpio", 1),
      soc.platform.request("gpio", 2),
      soc.platform.request("gpio", 3),
      soc.platform.request("gpio", 4)
]

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

clk = soc.crg.cd_sys.clk

И всё! И вставляем наш модуль GPU, а также внедряем его регистры в блок CSR:

    soc.submodules.gpu = GPU(touch_pins, clk)
    soc.add_csr("gpu")

 Ну, ещё сообщаем, что нам надо взять Верилоговские файлы:

    soc.platform.add_source("hvsync_generator.v")
    soc.platform.add_source("gpu.v")

 Итого, включая реперные строки до и после, получаем:

soc = BaseSoC(board=args.board, revision=args.revision,
        sys_clk_freq     = int(float(args.sys_clk_freq)),
        with_ethernet    = args.with_ethernet,
        with_etherbone   = args.with_etherbone,
        eth_ip           = args.eth_ip,
        eth_phy          = args.eth_phy,
        use_internal_osc = args.use_internal_osc,
        sdram_rate       = args.sdram_rate,
        cpu_type         = None,
        cpu              = None,
        **soc_core_argdict(args)
    )

    soc.platform.add_extension(_gpios) # General LED outputs        
    touch_pins = [
           soc.platform.request("gpio", 0),
           soc.platform.request("gpio", 1),
           soc.platform.request("gpio", 2),
           soc.platform.request("gpio", 3),
           soc.platform.request("gpio", 4)
       ]
    clk = soc.crg.cd_sys.clk

    soc.submodules.gpu = GPU(touch_pins, clk)
    soc.add_csr("gpu")
    
    soc.platform.add_source("hvsync_generator.v")
    soc.platform.add_source("gpu.v")

    builder = Builder(soc, **builder_argdict(args))
    builder.build(**trellis_argdict(args), run=args.build)

Что получаем

Запускаем скрипт, он отрабатывает, в каталоге /build/colorlight_5a_75b/gateware появился уже знакомый нам по прошлым статьям набор файлов:

А что делать с этими файлами, мы рассмотрим в следующий раз, потому что сегодня все уже устали. Если тема будет интересна, разумеется.

Заключение

Мы выяснили, что система Litex позволяет не только писать полные проекты на языке Python, но ещё и делать базовую шинно-ориентированную систему, подключая к ней внешние модули на языках Verilog и VHDL. Причём модули могут подключаться как шиной (Wishbone либо AXI), так и на уровне линий. Для этого система использует регистры CSR (Command Status Registers).

Мы создали систему, в которой имеется шина Wishbone, готовый модуль Ethernet to Wishbone, память SDRAM и модуль GPU, описанный на языке Verilog. Модуль GPU подключён через механизм CSR. Часть линий GPU проброшена на выводы ПЛИС, а от них – на разъём J1. Если выбирать разные версии платы, ножки ПЛИС будут конфигурироваться согласно заданной в системе цоколёвке.

В следующей статье запланированы практические опыты с полученной системой.

Пример, разработанный в статье, можно скачать тут.

 

Источник

Читайте также