Синхронизация с правками публикуемого издания (#101)

* СП. Обновление предисловия

* СП. Обновление введения

* СП. Обновление лаб

* СП. Обновление доп материалов

* СП. Введение

* СП. Введение

* СП. ЛР№4, 15

* СП. Базовые конструкции Verilog

* Update Implementation steps.md

* СП. ЛР 4,5,7,8,14

* СП. ЛР№8

* Синхронизация правок

* СП. Финал

* Исправление ссылки на рисунок

* Обновление схемы

* Синхронизация правок

* Добавление белого фона .drawio-изображениям

* ЛР2. Исправление нумерации рисунка
This commit is contained in:
Andrei Solodovnikov
2025-02-12 17:53:52 +03:00
committed by GitHub
parent d251574bbc
commit 9739429d6e
168 changed files with 79781 additions and 961 deletions

View File

@@ -20,7 +20,7 @@
## Теория
Помимо процессора и памяти, третьим ключевым элементом вычислительной системы является система ввода/вывода, обеспечивающая обмен информации между ядром вычислительной машины и периферийными устройствами [1, стр.364].
Помимо процессора и памяти, третьим ключевым элементом архитектуры вычислительной машины является **система ввода/вывода**, обеспечивающая обмен информацией между ядром вычислительной машины и разнообразными периферийными устройствами [1, стр.351].
Любое периферийное устройство со стороны вычислительной машины видится как набор ячеек памяти (регистров). С помощью чтения и записи этих регистров происходит обмен информации с периферийным устройством, и управление им. Например, датчик температуры может быть реализован самыми разными способами, но для процессора он в любом случае ячейка памяти, из которой он считывает число температуру.
@@ -47,7 +47,8 @@
_Рисунок 1. Итоговая структура процессорной системы._
Обратите внимание на то, что на вход `mem_ready_i` модуля `lsu` подаётся единица. Вообще говоря, каждый модуль-контроллер периферийного устройства должен содержать выходной сигнал `ready_o`, который должен мультиплексироваться с остальными подобно тому, как мультиплексируются сигналы read_data_o. На вход `lsu` должен подаваться выход мультиплексора. Однако, поскольку все модули достаточно просты, чтобы, как и у памяти данных, выходной сигнал `ready_o` был всегда равен единице (а также для упрощения _рис. 1_), эти сигналы были убраны из микроархитектуры. В случае, если вы решите добавить в процессорную систему периферийное устройство, сигнал `ready_o` которого не будет равен константной единице, логику управления входом `mem_ready_i` модуля `lsu` будет необходимо обновить описанным выше способом.
> [!IMPORTANT]
> Обратите внимание на то, что на вход `mem_ready_i` модуля `lsu` подаётся единица. Вообще говоря, каждый модуль-контроллер периферийного устройства должен содержать выходной сигнал `ready_o`, который должен мультиплексироваться с остальными подобно тому, как мультиплексируются сигналы read_data_o. На вход `lsu` должен подаваться выход мультиплексора. Однако, поскольку все модули достаточно просты, чтобы, как и у памяти данных, выходной сигнал `ready_o` был всегда равен единице (а также для упрощения _рис. 1_), эти сигналы были убраны из микроархитектуры. В случае, если вы решите добавить в процессорную систему периферийное устройство, сигнал `ready_o` которого не будет равен константной единице, логику управления входом `mem_ready_i` модуля `lsu` будет необходимо обновить описанным выше способом.
### Активация выбранного устройства
@@ -57,7 +58,7 @@ _Рисунок 1. Итоговая структура процессорной
Реализация унитарного кодирования предельно проста:
- Нулевой сигнал этой шины будет равен единице только если `data_addr_o[31:24] = 8'd0`.
- Нулевой бит этой шины будет равен единице только если `data_addr_o[31:24] = 8'd0`.
- Первый бит этой шины будет равен единице только если `data_addr_o[31:24] = 8'd1`.
- ...
- Двести пятьдесят пятый бит шины будет равен единице только если `data_addr_o[31:24] = 8'd255`.
@@ -100,9 +101,10 @@ module processor_system(
endmodule
```
Эти порты нужно подключить к одноименным портам ваших контроллеров периферии (**речь идёт только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**). Иными словами, в описании модуля должны быть все указанные входы и выходы. Но использовать вам нужно только порты, связанные с теми периферийными устройствами, реализацию которых вам необходимо подключить к процессорной системе в рамках индивидуального задания.
Эти порты нужно подключить к одноимённым портам ваших контроллеров периферии (**речь идёт только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**). Иными словами, в описании модуля должны быть все указанные входы и выходы. Но использовать вам нужно только порты, связанные с теми периферийными устройствами, реализацию которых вам необходимо подключить к процессорной системе в рамках индивидуального задания.
Обратите внимание на то, что изменился сигнал сброса (`resetn_i`). Буква `n` на конце означает, что сброс работает по уровню `0` (в таком случае говорят, что **активный уровень** данного сигнала `0`: когда сигнал равен нулю — это сброс, когда единице — не сброс).
> [!IMPORTANT]
> Обратите внимание на то, что изменился сигнал сброса (`resetn_i`). Буква `n` на конце означает, что сброс работает по уровню `0` (в таком случае говорят, что **активный уровень** данного сигнала `0`: когда сигнал равен нулю — это сброс, когда единице — не сброс).
Помимо прочего, необходимо подключить к вашему модулю `блок делителя частоты`. Поскольку в данном курсе лабораторных работ вы выполняли реализацию однотактного процессора, инструкция должна пройти через все ваши блоки за один такт. Из-за этого критический путь схемы не позволит использовать тактовый сигнал частотой в `100 МГц`, от которого работает отладочный стенд. Поэтому, необходимо создать отдельный сигнал с пониженной тактовой частотой, от которого будет работать ваша схема.
@@ -134,7 +136,7 @@ _Таблица 1. Карта памяти периферийных устрой
- **(R)** означает что разрешён доступ только на чтение (операция записи по этому адресу должна игнорироваться вашим контроллером).
- **"Выставленное на переключателях значение"** означает ровно то, что и означает. Если процессор выполняет операцию чтения по адресу `0x01000000` (`0x01` [старшая часть адреса переключателей] + `0x000000` [младшая часть адреса для получения выставленного на переключателях значения]), то контроллер должен выставить на выходной сигнал `RD` значение на переключателях (о том, как получить это значение будет рассказано чуть позже).
Рассмотрим ещё один пример. При обращении по адресу `0x02000024` (`0x02` [старшая часть адреса контроллера светодиодов] + `0x000024` [младшая часть адреса для доступа на запись к регистру сброса] ) должна произойти запись в регистр сброса, который должен сбросить значения в регистре управления зажигаемых светодиодов и регистре управления режимом "моргания" светодиодов (подробнее о том как должны работать эти регистры будет ниже).
Рассмотрим ещё один пример. При обращении по адресу `0x02000024` (`0x02` [старшая часть адреса контроллера светодиодов] + `0x000024` [младшая часть адреса для доступа на запись к регистру сброса] ) должна произойти запись в регистр сброса, который должен сбросить значения в регистре управления зажигаемых светодиодов и регистре управления режимом "моргания" светодиодов (подробнее о том как должны работать эти регистры будет далее).
Таким образом, каждый контроллер периферийного устройства должен выполнять две вещи:
@@ -152,7 +154,7 @@ _Таблица 1. Карта памяти периферийных устрой
3. Добавьте в проект пакет [`peripheral_pkg`](peripheral_pkg.sv). Данный пакет содержит старшие части адресов периферии в виде параметров, а также вспомогательные вызовы, используемые верификационным окружением.
4. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен в [примере описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md).
1. Готовые модули периферии, управление которыми должны осуществлять модули-контроллеры хранятся в папке `peripheral modules`.
5. Обновите модуль `processor_system` в соответствии с разделом ["Дополнительные правки модуля processor_system"](#дополнительные-правки-модуля-processor_system).
5. Обновите модуль `processor_system` в соответствии с параграфом ["Дополнительные правки модуля processor_system"](#дополнительные-правки-модуля-processor_system).
1. Подключите в проект файл `sys_clk_rst_gen.sv`.
2. Добавьте в модуль `processor_system` входы и выходы периферии, а также замените вход `rst_i` входом `resetn_i`. **Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать**.
3. Создайте в начале описания модуля `processor_system` экземпляр модуля `sys_clk_rst_gen`, скопировав фрагмент кода, приведённый в _листинге 1_.
@@ -162,7 +164,7 @@ _Таблица 1. Карта памяти периферийных устрой
2. Во время интеграции, вам необходимо использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров.
7. Проверьте работу процессорной системы с помощью моделирования.
1. Для моделирования используйте тестбенч `lab_13_tb_system`.
2. Для каждой пары контроллеров в папке `firmware/mem_files` представлены файлы, инициализирующие память инструкций. Содержимым одного из файлов, соответствующих паре периферийных устройств вашего варианта необходимо заменить содержимое файла `program.mem` в `Design Sources` проекта. Обратите внимание, что для пары "PS2-VGA" также необходим файл, инициализирующий память данных (в модуле `data_mem` необходимо добавить вызов инициализирующей функции `$readmemh` в блоке `initial`).
2. Для каждой пары контроллеров в папке `firmware/mem_files` представлены файлы, инициализирующие память инструкций. Содержимым одного из файлов, соответствующих паре периферийных устройств вашего варианта необходимо заменить содержимое файла `program.mem` в `Design Sources` проекта. Обратите внимание, что для пары "PS2-VGA" также необходим файл, инициализирующий память данных — [lab_13_ps2_ascii_data.mem](./firmware/mem_files/lab_13_ps2ascii_data.mem) (в модуле `data_mem` необходимо добавить вызов инициализирующей функции `$readmemh` в блоке `initial`).
3. Для проверки тестбенч имитирует генерацию данных периферийных устройств ввода. Перед проверкой желательно найти в тестбенче `initial`-блок своего устройства ввода (`sw_block`, `ps2_block`, `uart_block`) — по этому блоку будет понятно, какие данные будет передавать устройство ввода. Именно эти данные в итоге должны оказаться на шине `mem_rd_i`.
4. Для того, чтобы понять, что устройство работает должным образом, в первую очередь необходимо убедиться, что контроллер устройства ввода успешно осуществил прием данных (сгенерированные тестбенчем данные оказались в соответствующем регистре контроллера периферийного устройства) и выполнил запрос на прерывание.
5. После чего, необходимо убедиться, что процессор среагировал на данное прерывание, и в процессе его обработки в контроллер устройства вывода были поданы выходные данные.
@@ -176,7 +178,7 @@ _Таблица 1. Карта памяти периферийных устрой
10. Проверьте работу вашей процессорной системы на отладочном стенде с ПЛИС.
1. Обратите внимание, что в данной лабораторной уже не будет модуля верхнего уровня `nexys_...`, так как ваш модуль процессорной системы уже полностью самостоятелен и взаимодействует непосредственно с ножками ПЛИС через модули, управляемые контроллерами периферии.
2. Для проверки периферии переключателей и светодиодов будет достаточно одного лишь отладочного стенда. Для проверки всей остальной периферии может могут потребоваться: компьютер (для uart_rx / uart_tx), клавиатура (для контроллера клавиатуры) и VGA-монитор для VGA-контроллера.
1. Чтобы проверить работоспособность контроллеров UART, необходимо запустить на компьютере программу Putty, в настройках программы указать настройки, которыми будет сконфигурирован программой ваш контроллер (либо указать значения, которыми сбрасываются регистры, если программа ничего не настраивает) и COM-порт, через который компьютер будет общаться с контроллером. Определить нужный COM-порт на операционной системе Windows можно через "Диспетчер устройств", который можно открыть через меню пуск.
1. Чтобы проверить работоспособность контроллеров UART, необходимо запустить на компьютере программу Putty, во вкладке `Connections->Serial` указать настройки, которыми будет сконфигурирован программой ваш контроллер (либо указать значения регистров после сброса) и COM-порт, через который компьютер будет общаться с контроллером. Определить нужный COM-порт на операционной системе Windows можно через "Диспетчер устройств", который можно открыть через меню кнопки "Пуск".
В данном окне необходимо найти вкладку "Порты (COM и LPT)", раскрыть её, а затем подключить отладочный стенд через USB-порт (если тот ещё не был подключён). В списке появится новое устройство, а в скобках будет указан нужный COM-порт.
2. Несмотря на то, что описанный контроллер клавиатуры позволяет управлять клавиатурой с интерфейсом PS/2, некоторые платы (например, Nexys A7) позволяют подключать вместо них клавиатуры с USB-интерфейсом. Дело в том, что PS/2 уже давно устарел и найти клавиатуры с таким интерфейсом — задача непростая. Однако протокол передачи по этому интерфейсу очень удобен для образовательных целей, поэтому некоторые производители просто ставят на платы переходник с USB на PS/2, позволяя объединить простоту разработки с удобством использования.
@@ -195,7 +197,8 @@ _Таблица 1. Карта памяти периферийных устрой
3. На входе `write_enable_i` выставлено значение `0`.
4. На входе `addr_i` выставлено значение `0xАДРЕС`
Обратите внимание на то, что **запрос на чтение** должен обрабатываться **синхронно** (выходные данные должны выдаваться по положительному фронту `clk_i`) так же, как был реализован порт на чтение памяти данных в [ЛР№6](../06.%20Main%20memory/).
> [!IMPORTANT]
> Обратите внимание на то, что **запрос на чтение** должен обрабатываться **синхронно** (выходные данные должны выдаваться по положительному фронту `clk_i`) так же, как был реализован порт на чтение памяти данных в [ЛР№6](../06.%20Main%20memory/).
При описании поддерживаемых режимов доступа по данному адресу используются следующее обозначения:
@@ -294,7 +297,7 @@ endmodule
Регистр `led_mode` отвечает за режим вывода данных на светодиоды. Когда этот регистр равен единице, светодиоды должны "моргать" выводимым значением. Под морганием подразумевается вывод значения из регистра `led_val` на выход `led_o` на одну секунду (загорится часть светодиодов, соответствующие которым биты шины `led_o` равны единице), после чего на одну секунду выход `led_o` необходимо подать нули. Запись и чтение регистра `led_mode` осуществляется по адресу `0x04`.
Отсчёт времени можно реализовать простейшим счётчиком, каждый такт увеличивающимся на 1 и сбрасывающимся по достижении определенного значения, чтобы продолжить считать с нуля. Зная тактовую частоту, нетрудно определить до скольки должен считать счётчик. При тактовой частоте в 10 МГц происходит 10 миллионов тактов в секунду. Это означает, что при такой тактовой частоте через секунду счётчик будет равен `10⁷-1` (счёт идёт с нуля). Тем не менее удобней будет считать не до `10⁷-1` (что было бы достаточно очевидным и тоже правильным решением), а до `2*10⁷-1`. В этом случае старший бит счётчика каждую секунду будет инвертировать своё значение, что может быть использовано при реализации логики "моргания".
Отсчёт времени можно реализовать простейшим счётчиком, каждый такт увеличивающимся на 1 и сбрасывающимся по достижении определенного значения, чтобы продолжить считать с нуля. Зная тактовую частоту, нетрудно определить до скольки должен считать счётчик. При тактовой частоте в 10 МГц происходит 10 миллионов тактов в секунду. Это означает, что при такой тактовой частоте через секунду счётчик будет равен `10⁷-1` (счёт идёт с нуля). Тем не менее удобней будет считать не до `10⁷-1` (что было бы достаточно очевидным и тоже правильным решением), а до `2*10⁷-1`. В этом случае результат сравнения значения счётчика с 10⁷ будет меняться каждую секунду, что может быть использовано при реализации логики "моргания".
Важно отметить, что счётчик должен работать только при `led_mode == 1`, в противном случае счётчик должен быть равен нулю.
@@ -598,7 +601,7 @@ endmodule
Выходной сигнал `busy_o` на каждом такте `clk_i` должен записываться в регистр `busy`, доступ на чтение к которому осуществляется по адресу `0x08`.
Значения входных сигналов `baudrate_i`, `parity_en_i`, `stopbit_i` берутся из соответствующих регистров, доступ на запись к которым осуществляется по адресам `0x0C`, `0x10`, `0x14` соответственно, но только в моменты, когда выходной сигнал `busy_o` равен нулю. Иными словами, изменение настроек передачи возможно только в моменты, когда передача не происходит. Доступ на чтение этих регистров может осуществляться в любой момент времени.
Значения входных сигналов `baudrate_i`, `parity_en_i`, `stopbit_i` берутся напрямую из соответствующих регистров. Доступ на запись к этим регистрам осуществляется по адресам `0x0C`, `0x10`, `0x14` соответственно, но только в моменты, когда выходной сигнал `busy_o` равен нулю. Иными словами, изменение настроек передачи возможно только в моменты, когда передача не происходит. Доступ на чтение этих регистров может осуществляться в любой момент времени.
В регистр `data` модуля `uart_rx_sb_ctrl` записывается значение одноименного выхода модуля `uart_rx` в моменты положительного фронта `clk_i`, когда сигнал `rx_valid_o` равен единице. Доступ на чтение этого регистра осуществляется по адресу `0x00`.
@@ -610,7 +613,7 @@ endmodule
На вход `tx_valid_i` модуля `uart_tx` подаётся единица в момент выполнения **запроса на запись** по адресу `0x00` (при сигнале `busy` равном нулю). В остальное время на вход этого сигнала подаётся `0`.
В случае **запроса на запись** значения `1` по адресу `0x24` (адресу сброса), все регистры модуля-контроллера должны сброситься. При этом регистр `baudrate` должен принять значение `9600`, регистр, `stopbit` должен принять значение `1`. Остальные регистры должны принять значение `0`.
В случае **запроса на запись** значения `1` по адресу `0x24` (адресу сброса), все регистры модуля-контроллера должны сброситься. При этом регистр `baudrate` должен принять значение `9600`, регистр `stopbit` должен принять значение `1`. Остальные регистры должны принять значение `0`.
Адресное пространство контроллера `uart_rx_sb_ctrl` представлено в аблице 6_.
@@ -655,7 +658,7 @@ _Рисунок 2. Пример игры с использованием сим
_Таблица 8. Адресное пространство контроллера VGA._
Для того, чтобы вывести символ на экран, необходимо использовать адрес этого символа на сетке `80x30` (диапазон адресов `char_map`). К примеру, мы хотим вывести символ в верхнем левом углу (т.е. нулевой символ нулевой строки). Это нулевой символ в диапазоне адресов `char_map`. Поскольку данный диапазон начинается с адреса `0x0000_0000`, запись по этому адресу приведёт к отображению символа, соответствующего [ASCII-коду](https://www.asciitable.com/), пришедшему на `write_data_i`.
Для того, чтобы вывести символ на экран, необходимо использовать адрес этого символа на сетке `80x30` (диапазон адресов `char_map`). К примеру, мы хотим вывести символ в верхнем левом углу (т.е. нулевой символ нулевой строки). Эта позиция расположена по адресу "0" в диапазоне адресов `char_map`. Поскольку данный диапазон начинается с адреса `0x0000_0000`, запись по этому адресу приведёт к отображению символа, соответствующего [ASCII-коду](https://www.asciitable.com/), пришедшему на `write_data_i`.
Если мы хотим вывести нулевой (левый) символ в первой строке (счёт ведётся с нуля), то необходимо произвести запись по адресу `1*80+0=80=0x0000_0050`.
@@ -683,7 +686,7 @@ _Рисунок 3. Цветовая палитра vga-модуля._
К примеру, мы хотим установить черный фоновый цвет и белый цвет в качестве цвета символа для верхней левой позиции. В этом случае, мы должны записать значение `f0` (f(15) — код белого цвета, 0 — код черного цвета) по адресу `0x0000_1000` (нулевой адрес в диапазоне `color_map`).
Для отрисовки символов, мы условно поделили экран на сетку `80х30`, и для каждой позиции в этой сетке определили фоновый и активный цвет. Чтобы модуль мог отрисовать символ на очередной позиции (которая занимает `16х8` пикселей), ему необходимо знать в какой цвет необходимо окрасить каждый пиксель для каждого ascii-кода. Для этого используется память шрифтов.
Для отрисовки символов, мы условно поделили экран на сетку `80х30`, и для каждой позиции в этой сетке определили фоновый и активный цвет. Чтобы модуль мог отрисовать символ на очередной позиции (которая занимает `16х8` пикселей), ему необходимо знать какой пиксель в этой позиции для заданного ASCII-кода является пикселем фона, а какой — пикселем символа. Для этого используется память шрифтов.
Допустим, нам необходимо отрисовать символ `F` (ascii-код `0x46`).
@@ -691,9 +694,9 @@ _Рисунок 3. Цветовая палитра vga-модуля._
_Рисунок 4. Отрисовка символа `F` в разрешении 16х8 пикселей._
Данный символ состоит из 16 строчек по 8 пикселей. Каждый пиксель кодируется одним битом (горит/не горит, цвет символа/фоновый цвет). Каждая строчка кодируется одним байтом (8 бит на 8 пикселей). Таким образом, каждый сканкод требует 16 байт памяти.
Данный символ состоит из 16 строчек по 8 пикселей. Каждый пиксель кодируется одним битом (горит/не горит, цвет символа/фоновый цвет). Каждая строчка кодируется одним байтом (8 бит на 8 пикселей). Таким образом, каждый символ в шрифте требует 16 байт памяти.
Данный модуль поддерживает 256 сканкодов. Следовательно, для хранения шрифта под каждый из 256 сканкодов требуется 16 * 256 = 4KiB памяти.
Данный модуль поддерживает 256 символов. Следовательно, для хранения шрифта под каждый из 256 символов требуется 16 * 256 = 4KiB памяти.
Для хранения шрифтов в модуле отведён диапазон адресов `0x00002000-0x00002FFF`. В отличие от предыдущих диапазонов адресов, где каждый адрес был закреплён за соответствующей позицией символа в сетке `80x30`, адреса данного диапазона распределены следующим образом:
@@ -787,6 +790,8 @@ module vga_sb_ctrl (
);
```
При интеграции модуля в процессорную систему, ко входу `clk100m_i` необходимо подключить провод `clk_i` (а ко входу `clk_i` — провод `sysclk`).
Реализация данного модуля исключительно простая. В первую очередь необходимо подключить одноименные сигналы напрямую:
- `clk_i`,
@@ -835,6 +840,6 @@ module vga_sb_ctrl (
## Список использованной литературы
1. С.А. Орлов, Б.Я. Цилькер / Организация ЭВМ и систем: Учебник для вузов. 2-е изд. / СПб.: Питер, 2011.
1. Орлов С.А. Организация ЭВМ и систем: Учебник для вузов. 4-е изд. дополненное и переработанное / С.А. Орлов. - Санкт-Петербург : Питер, 2021. - 688 с.
2. [Rebelstar](https://en.wikipedia.org/wiki/Rebelstar)
3. [Easycode](https://fontstruct.com/fontstructions/show/346317/easycode)