ЛР12. Обновление методички

This commit is contained in:
Andrei Solodovnikov
2023-11-27 00:22:01 +03:00
parent ce62a8a6cf
commit 9bbbe0c820
7 changed files with 187 additions and 9 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -44,6 +44,7 @@
4. Семисегментные индикаторы 4. Семисегментные индикаторы
5. UART-приемник 5. UART-приемник
6. UART-передатчик 6. UART-передатчик
7. Видеоадаптер
В таком случае, если мы захотим обратиться в четвертый регистр семисегментных индикаторов, мы должны будем использовать адрес `0x03000004`. Старшие 8 бит (`0x03`) определяют выбранное периферийное устройство, оставшиеся 24 бита определяют конкретный адрес в адресном пространстве этого устройства. В таком случае, если мы захотим обратиться в четвертый регистр семисегментных индикаторов, мы должны будем использовать адрес `0x03000004`. Старшие 8 бит (`0x03`) определяют выбранное периферийное устройство, оставшиеся 24 бита определяют конкретный адрес в адресном пространстве этого устройства.
@@ -81,19 +82,30 @@ module riscv_unit(
// Входы и выходы периферии // Входы и выходы периферии
input logic [15:0] sw_i, // Переключатели input logic [15:0] sw_i, // Переключатели
output logic [15:0] led_o, // Светодиоды output logic [15:0] led_o, // Светодиоды
input logic kclk_i, // Тактирующий сигнал клавиатуры input logic kclk_i, // Тактирующий сигнал клавиатуры
input logic kdata_i, // Сигнал данных клавиатуры input logic kdata_i, // Сигнал данных клавиатуры
output logic [ 6:0] hex_led_o, // Вывод семисегментных индикаторов output logic [ 6:0] hex_led_o, // Вывод семисегментных индикаторов
output logic [ 7:0] hex_sel_o, // Селектор семисегментных индикаторов output logic [ 7:0] hex_sel_o, // Селектор семисегментных индикаторов
input logic rx_i, // Линия приема по UART input logic rx_i, // Линия приема по UART
output logic tx_o // Линия передачи по UART output logic tx_o, // Линия передачи по UART
output logic [3:0] vga_r_o, // красный канал vga
output logic [3:0] vga_g_o, // зеленый канал vga
output logic [3:0] vga_b_o, // синий канал vga
output logic vga_hs_o, // линия горизонтальной синхронизации vga
output logic vga_vs_o // линия вертикальной синхронизации vga
); );
//... //...
endmodule endmodule
``` ```
Эти порты нужно подключить к одноименным портам ваших контроллеров периферии (**речь идет только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**). Эти порты нужно подключить к одноименным портам ваших контроллеров периферии (**речь идет только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**). Иными словами, в описании модуля должны быть все указанные входы и выходы. Но использовать вам нужно только порты, связанные с теми периферийными устройствами, реализацию которых вам необходимо подключить к процессорной системе в рамках индивидуального задания.
Обратите внимание на то, что изменился сигнал сброса (`resetn_i`). Буква `n` на конце означает, что сброс работает по уровню `0` (когда сигнал равен нулю — это сброс, когда единице — не сброс). Обратите внимание на то, что изменился сигнал сброса (`resetn_i`). Буква `n` на конце означает, что сброс работает по уровню `0` (когда сигнал равен нулю — это сброс, когда единице — не сброс).
@@ -119,6 +131,8 @@ sys_clk_rst_gen divider(.ex_clk_i(clk_i),.ex_areset_n_i(resetn_i),.div_i(10),.sy
![Карта памяти](../../.pic/Labs/lab_12_periph/fig_02.png) ![Карта памяти](../../.pic/Labs/lab_12_periph/fig_02.png)
_Рисунок 2. Карта памяти периферийных устройств_
Работа с картой осуществляется следующим образом. Под названием каждого периферийного устройства указана старшая часть адреса (чему должны быть равны старшие 8 бит адреса, чтобы было сформировано обращение к данному периферийному устройству). Например, для переключателей это значение равно `0x01`, для светодиодов `0x02` и т.п. Работа с картой осуществляется следующим образом. Под названием каждого периферийного устройства указана старшая часть адреса (чему должны быть равны старшие 8 бит адреса, чтобы было сформировано обращение к данному периферийному устройству). Например, для переключателей это значение равно `0x01`, для светодиодов `0x02` и т.п.
В самом левом столбце указаны используемые/неиспользуемые адреса в адресном пространстве данного периферийного устройства. Например для переключателей есть только один используемый адрес: `0x000000`. Его функциональное назначение и разрешения на доступ указаны в столбце соответствующего периферийного устройства. Возвращаясь к адресу `0x000000` для переключателей мы видим следующее: В самом левом столбце указаны используемые/неиспользуемые адреса в адресном пространстве данного периферийного устройства. Например для переключателей есть только один используемый адрес: `0x000000`. Его функциональное назначение и разрешения на доступ указаны в столбце соответствующего периферийного устройства. Возвращаясь к адресу `0x000000` для переключателей мы видим следующее:
@@ -134,6 +148,8 @@ sys_clk_rst_gen divider(.ex_clk_i(clk_i),.ex_areset_n_i(resetn_i),.div_i(10),.sy
Подробное описание периферийных устройств их управления и назначение управляющих регистров будет дано после порядка выполнения задания. Подробное описание периферийных устройств их управления и назначение управляющих регистров будет дано после порядка выполнения задания.
Обратите внимание на то, что у всех модулей периферийных устройств есть выходной сигнал `ready_o`, который должен быть всегда равен единице.
--- ---
## Порядок выполнения задания ## Порядок выполнения задания
@@ -143,13 +159,12 @@ sys_clk_rst_gen divider(.ex_clk_i(clk_i),.ex_areset_n_i(resetn_i),.div_i(10),.sy
3. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен [здесь](../../Basic%20Verilog%20structures/Controllers.md). 3. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен [здесь](../../Basic%20Verilog%20structures/Controllers.md).
4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit). 4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit).
1. Подключите в проект файл `sys_clk_rst_gen.sv`. 1. Подключите в проект файл `sys_clk_rst_gen.sv`.
2. Добавьте в модуль `riscv_unit` входы и выходы периферии. Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать. 2. Добавьте в модуль `riscv_unit` входы и выходы периферии. **Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать**.
3. Создайте в начале описания модуля `riscv_unit` экземпляр модуля `sys_clk_rst_gen`, скопировав приведенный фрагмент кода. 3. Создайте в начале описания модуля `riscv_unit` экземпляр модуля `sys_clk_rst_gen`, скопировав приведенный фрагмент кода.
4. Замените подключение тактового сигнала исходных подмодулей `riscv_unit` на появившийся сигнал `sysclk`. Убедитесь, что на модули имеющие сигнал сброса приходит сигнал `rst`. 4. Замените подключение тактового сигнала исходных подмодулей `riscv_unit` на появившийся сигнал `sysclk`. Убедитесь, что на модули имеющие сигнал сброса приходит сигнал `rst`.
5. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти ([_рис. 2_](../../.pic/Labs/lab_12_periph/fig_02.png)). Это означает, что если вы реализуете контроллер светодиодов, на его вход `req_i` должна подаваться единица в случае если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`. 5. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти ([_рис. 2_](../../.pic/Labs/lab_12_periph/fig_02.png)). Это означает, что если вы реализуете контроллер светодиодов, на его вход `req_i` должна подаваться единица в случае если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`.
1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо. 1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо.
2. При этом во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров. 2. При этом во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров.
3. Даже если вы не используете какие-то входные/выходные сигналы в модуле `riscv_unit` (например по варианту вам не достался контроллер клавиатуры и поэтому вы не используете сигналы `kclk_i` и `kdata_i`), вы все равно должны их описать во входах и выходах модуля `riscv_unit`.
6. Проверьте работу процессорной системы с помощью моделирования. 6. Проверьте работу процессорной системы с помощью моделирования.
1. Для каждой пары контроллеров периферии предложено две программы: с обновлением данных по опросу и по прерываниям. Запустите моделирование сначала для одной программы, затем для другой (для этого необходимо обновить файл, инициализирующий память инструкций). После проверки работоспособности процессора, сравните поведение сигналов LSU для этих программ. 1. Для каждой пары контроллеров периферии предложено две программы: с обновлением данных по опросу и по прерываниям. Запустите моделирование сначала для одной программы, затем для другой (для этого необходимо обновить файл, инициализирующий память инструкций). После проверки работоспособности процессора, сравните поведение сигналов LSU для этих программ.
7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе. 7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
@@ -342,6 +357,8 @@ endmodule
В случае, если произошел запрос на чтение по адресу `0x00`, необходимо выставить на выход `read_data_o` значение регистра `scan_code` (дополнив старшие биты нулями), при этом значение регистра `scan_code_is_unread` необходимо обнулить. В случае, если одновременно с запросом на чтение пришел сигнал `keycode_valid_o`, регистр `scan_code_is_unread` обнулять не нужно (в этот момент в регистр `scan_code` уже записывается новое, еще непрочитанное значение). В случае, если произошел запрос на чтение по адресу `0x00`, необходимо выставить на выход `read_data_o` значение регистра `scan_code` (дополнив старшие биты нулями), при этом значение регистра `scan_code_is_unread` необходимо обнулить. В случае, если одновременно с запросом на чтение пришел сигнал `keycode_valid_o`, регистр `scan_code_is_unread` обнулять не нужно (в этот момент в регистр `scan_code` уже записывается новое, еще непрочитанное значение).
Обнуление регистра `scan_code_is_unread` должно происходить и в случае получения сигнала `interrupt_return_i` (однако опять же, если в этот момент приходит сигнал `keycode_valid_o`, обнулять регистр не нужно).
В случае запроса на чтение по адресу `0x04` необходимо вернуть значение регистра `scan_code_is_unread`. В случае запроса на чтение по адресу `0x04` необходимо вернуть значение регистра `scan_code_is_unread`.
В случае запроса на запись по адресу `0x24` со значением `1`, необходимо осуществить сброс регистров `scan_code` и `scan_code_is_unread` в `0`. В случае запроса на запись по адресу `0x24` со значением `1`, необходимо осуществить сброс регистров `scan_code` и `scan_code_is_unread` в `0`.
@@ -505,6 +522,7 @@ module uart_rx_sb_ctrl(
*/ */
output logic interrupt_request_o, output logic interrupt_request_o,
input logic interrupt_return_i,
/* /*
Часть интерфейса модуля, отвечающая за подключение передающему, Часть интерфейса модуля, отвечающая за подключение передающему,
@@ -535,6 +553,7 @@ module uart_tx_sb_ctrl(
input logic [31:0] write_data_i, input logic [31:0] write_data_i,
input logic write_enable_i, input logic write_enable_i,
output logic [31:0] read_data_o, output logic [31:0] read_data_o,
output logic ready_o,
/* /*
Часть интерфейса модуля, отвечающая за подключение передающему, Часть интерфейса модуля, отвечающая за подключение передающему,
@@ -551,21 +570,21 @@ module uart_tx_sb_ctrl(
endmodule endmodule
``` ```
У обоих предоставленных модулей схожий прототип, различия заключаются лишь в направлениях некоторых сигналов: У обоих предоставленных модулей схожий прототип, различия заключаются лишь в направлениях сигналов `data` и `valid`.
Управление сигналами этого модуля достаточно просто. Взаимодействие модулей `uart_rx` и `uart_tx` с соответствующими модулями-контроллерами осуществляется следующим образом.
Сигналы `clk_i` и `rx_i`/`tx_i` подключаются напрямую к соответствующим сигналам модуля-контроллера. Сигналы `clk_i` и `rx_i`/`tx_i` подключаются напрямую к соответствующим сигналам модулей-контроллеров.
Сигнал `rst_i` модулей `uart_rx` / `uart_tx` должен быть равен единице при запросе на запись единицы по адресу `0x24`, а так же в случае когда сигнал `rst_i` модуля-контроллера равен единице. Сигнал `rst_i` модулей `uart_rx` / `uart_tx` должен быть равен единице при запросе на запись единицы по адресу `0x24`, а так же в случае когда сигнал `rst_i` модуля-контроллера равен единице.
Выходной сигнал `busy_o` на каждом такте `clk_i` должен записываться в регистр `busy`, доступ на чтение к которому осуществляется по адресу `0x08`. Выходной сигнал `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`. В регистр `data` модуля `uart_rx_sb_ctrl` записывается значение одноименного выхода модуля `uart_rx` в моменты положительного фронта `clk_i`, когда сигнал `rx_valid_o` равен единице. Доступ на чтение этого регистра осуществляется по адресу `0x00`.
В регистр `valid` модуля `uart_rx_sb_ctrl` записывается единица по положительному фронту clk_i, когда выход `rx_valid_o` равен единице. Данный регистр сбрасывается в ноль при выполнении запроса на чтение по адресу `0x00`. Сам регистр доступен для чтения по адресу `0x04`. Регистр `valid` подключается к выходу `interrupt_request_o`. Что позволяет узнать о пришедших данных и посредством прерывания. В регистр `valid` модуля `uart_rx_sb_ctrl` записывается единица по положительному фронту clk_i, когда выход `rx_valid_o` равен единице. Данный регистр сбрасывается в ноль при выполнении запроса на чтение по адресу `0x00`, а так же при получении сигнала `interrupt_return_i`. Сам регистр доступен для чтения по адресу `0x04`. Регистр `valid` подключается к выходу `interrupt_request_o`. Что позволяет узнать о пришедших данных и посредством прерывания.
На вход `tx_data_i` модуля `uart_tx` подаются данные из регистра `data` модуля `uart_tx_sb_ctrl`. Доступ на запись в этот регистр происходит по адресу `0x00` в моменты положительного фронта `clk_i`, когда сигнал `busy_o` равен нулю. Доступ на чтение этого регистра может осуществляться в любой момент времени. На вход `tx_data_i` модуля `uart_tx` подаются данные из регистра `data` модуля `uart_tx_sb_ctrl`. Доступ на запись в этот регистр происходит по адресу `0x00` в моменты положительного фронта `clk_i`, когда сигнал `busy_o` равен нулю. Доступ на чтение этого регистра может осуществляться в любой момент времени.
@@ -595,3 +614,162 @@ endmodule
|0x10 | RW | [0:1] | Чтение/запись регистра `parity`, отвечающего за включение отключение проверки данных через бит четности | |0x10 | RW | [0:1] | Чтение/запись регистра `parity`, отвечающего за включение отключение проверки данных через бит четности |
|0x14 | RW | [0:1] | Чтение/запись регистра `stopbit`, отвечающего за длину стопового бита | |0x14 | RW | [0:1] | Чтение/запись регистра `stopbit`, отвечающего за длину стопового бита |
|0x24 | W | 1 | Запись сигнала сброса | |0x24 | W | 1 | Запись сигнала сброса |
### Видеоадаптер
Видеоадаптер позволяет выводить информацию на экран через интерфейс **VGA**. Предоставляемый в данной лабораторной работе vga-модуль способен выводить `80х30` символов (разрешение символа `8x16`). Таким образом, итоговое разрешение экрана, поддерживаемого vga-модулем будет `80*8 x 30*16 = 640x480`. VGA-модуль поддерживает управление цветовой схемой для каждого поля символа в сетке `80х30`. Это значит что каждый символ (и фон символа) может быть отрисован отдельным цветом из диапазона 16-ти цветов.
![../../.pic/Labs/lab_12_periph/fig_03.png](../../.pic/Labs/lab_12_periph/fig_03.png)
_Рисунок 3. Пример вывода на экран символьной информации_
Для управления выводимым на экран содержимым, адресное пространство модуля разделено на следующие диапазоны:
![../../.pic/Labs/lab_12_periph/fig_04.png](../../.pic/Labs/lab_12_periph/fig_04.png)
_Рисунок 4. Карта памяти vga-модуля_
Для того, чтобы вывести символ на экран, необходимо использовать адрес этого символа на сетке `80x30` (диапазон адресов `char_map`). К примеру, мы хотим вывести символ в верхнем левом углу. Это нулевой символ в диапазоне адресов `char_map`. Поскольку данный диапазон начинается с адреса `0x0000_0000`, запись по этому адресу приведет к отображению символа, соответствующего [ASCII-коду](https://www.asciitable.com/), пришедшему на `write_data_i`.
Если мы хотим вывести нулевой (левый) символ в первой строке (счет ведется с нуля), то необходимо произвести запись по адресу `1*80+0=80=0x0000_0050`.
Вывод символа в правом нижнем углу осуществляется записью по адресу `0x0000_095F` (80*30-1)
Установка цветовой схемы осуществляется по тем же самым адресам, к которым прибавлено значение `0x0000_1000`:
* верхний левый символ — `0x0000_1000`
* нулевой символ первой строки — `0x0000_1050`
* нижний правый символ — `0x0000_195F`
Цветовая схема каждой позиции состоит из двух цветов: цвета фона и цвета символа. Оба эти цвета выбираются из палитры на 16 цветов, сгруппированной по 8 парам. В каждой паре цвет на своей полной яркости, и половинной яркости. Один из цветов — черный, что на полной, что на половинной яркости он одинаковый. Ниже приведены коды цветов их rgb-значения:
![../../.pic/Labs/lab_12_periph/fig_05.png](../../.pic/Labs/lab_12_periph/fig_05.png)
_Рисунок 5. Цветовая палитра vga-модуля_
Таким образом, для установки цветовой схемы, необходимо выбрать два цвета из палитры, склеить их (в старших разрядах идет цвет символа, в младших — цвет фона) и записать получившееся 8-битное значение по адресу выбранной позиции в диапазоне адресов цветовой схемы (color_map).
К примеру, мы хотим установить черный фоновый цвет и белый цвет в качестве цвета символа для верхней левой позиции. В этом случае, мы должны записать значение `f0` (f(15) — код белого цвета, 0 — код черного цвета) по адресу `0x0000_1000` (нулевой адрес в диапазоне `color_map`).
Для отрисовки символов, мы условно поделили экран на сетку `80х30`, и для каждой позиции в этой сетке определили фоновый и активный цвет. Чтобы модуль мог отрисовать символ на очередной позиции (которая занимает `16х8` пикселей), ему необходимо знать в какой цвет необходимо окрасить каждый пиксель для каждого ascii-кода. Для этого используется память шрифтов.
Допустим, нам необходимо отрисовать символ `F` (ascii-код `0x46`).
![../../.pic/Labs/lab_12_periph/fig_06.png](../../.pic/Labs/lab_12_periph/fig_06.png)
_Рисунок 6. Отрисовка символа `F` в разрешении 16х8 пикселей._
Данный символ состоит из 16 строчек по 8 пикселей. Каждый пиксель кодируется одним битом (горит/не горит, цвет символа/фоновый цвет). Каждая строчка кодируется одним байтом (8 бит на 8 пикселей). Таким образом, каждый сканкод требует 16 байт памяти.
Данный модуль поддерживает 256 сканкодов. Следовательно, для хранения шрифта под каждый из 256 сканкодов требуется 16 * 256 = 4KiB памяти.
Для хранения шрифтов в модуле отведен диапазон адресов `0x00010000-0x00010FFF`. В отличие от предыдущих диапазонов адресов, где каждый адрес был закреплен за соответствующей позицией символа в сетке `80x30`, адреса данного диапазона распределены следующим образом:
* 0-ой байт — нулевая (верхняя) строчка символа с кодом 0;
* 1-ый байт — первая строчка символа с кодом 0;
* ...
* 15-ый байт — пятнадцатая (нижняя) строчка символа с кодом 0;
* 16-ый байт — нулевая (верхняя) строчка символа с кодом 1;
* ...
* 4095-ый байт — пятнадцатая (нижняя) строчка символа с кодом 255.
Прототип vga-модуля следующий:
```SystemVerilog
module vgachargen (
input logic clk_i, // системный синхроимпульс
input logic clk100m_i, // клок с частотой 100МГц
input logic rst_i, // сигнал сброса
/*
Интерфейс записи выводимого символа
*/
input logic [ 9:0] char_map_addr_i, // адрес позиции выводимого символа
input logic char_map_we_i, // сигнал разрешения записи кода
input logic [ 3:0] char_map_be_i, // сигнал выбора байтов для записи
input logic [31:0] char_map_wdata_i, // ascii-код выводимого символа
output logic [31:0] char_map_rdata_o, // сигнал чтения кода символа
/*
Интерфейс установки цветовой схемы
*/
input logic [ 9:0] col_map_addr_i, // адрес позиции устанавливаемой схемы
input logic col_map_we_i, // сигнал разрешения записи схемы
input logic [ 3:0] col_map_be_i, // сигнал выбора байтов для записи
input logic [31:0] col_map_wdata_i, // код устанавливаемой цветовой схемы
output logic [31:0] col_map_rdata_o, // сигнал чтения кода схемы
/*
Интерфейс установки шрифта
*/
input logic [ 9:0] char_tiff_addr_i, // адрес позиции устанавливаемого шрифта
input logic char_tiff_we_i, // сигнал разрешения записи шрифта
input logic [ 3:0] char_tiff_be_i, // сигнал выбора байтов для записи
input logic [31:0] char_tiff_wdata_i,// отображаемые пиксели в текущей позиции шрифта
output logic [31:0] char_tiff_rdata_o,// сигнал чтения пикселей шрифта
output logic [3:0] vga_r_o, // красный канал vga
output logic [3:0] vga_g_o, // зеленый канал vga
output logic [3:0] vga_b_o, // синий канал vga
output logic vga_hs_o, // линия горизонтальной синхронизации vga
output logic vga_vs_o // линия вертикальной синхронизации vga
);
```
Для управления данным модулем, необходимо написать модуль-контроллер со следующим прототипом:
```SystemVerilog
module vga_sb_ctrl (
input logic clk_i,
input logic rst_i,
input logic clk100m_i,
input logic req_i,
input logic write_enable_i,
input logic [3:0] mem_be_i,
input logic [31:0] addr_i,
input logic [31:0] write_data_i,
output logic [31:0] read_data_o,
output logic ready_o,
output logic [3:0] vga_r_o,
output logic [3:0] vga_g_o,
output logic [3:0] vga_b_o,
output logic vga_hs_o,
output logic vga_vs_o
);
```
Реализация данного модуля исключительно простая. В первую очередь необходимо подключить одноименные сигналы напрямую:
* `clk_i`,
* `rst_i`,
* `clk100m_i`,
* `vga_r_o`,
* `vga_g_o`,
* `vga_b_o`,
* `vga_hs_o`,
* `vga_vs_o`
Кроме того, необходимо:
1. подключить напрямую сигнал `write_data_i` ко входам:
1. `char_map_wdata_i`,
2. `col_map_wdata_i`,
3. `char_tff_wdata_i`,
2. подключить биты `addr_i[11:2]` ко входам:
1. `char_map_addr_i`,
2. `col_map_addr_i`,
3. `char_tiff_addr_i`,
3. сигнал `mem_be_i` подключить ко входам:
1. `char_map_be_i`,
2. `col_map_be_i`,
3. `char_tiff_be_i`.
Остается только разобраться с сигналами `write_enable_i` и `read_data_o`.
Оба эти сигнала мультиплексируются / демультиплексируются с помощью одного и того же управляющего сигнала: `addr_i[13:12]` в соответствии с диапазонами адресов (рис. 3):
* `addr_i[13:12] == 2'b00` — сигнал `write_enable_i` поступает на вход `char_map_we_i`, выход `char_map_rdata_o` записывается в выходной регистр `read_data_o`;
* `addr_i[13:12] == 2'b01` — сигнал `write_enable_i` поступает на вход `col_map_we_i`, выход `col_map_rdata_o` записывается в выходной регистр `read_data_o`;
* `addr_i[13:12] == 2'b10` — сигнал `write_enable_i` поступает на вход `char_tiff_we_i`, выход `char_tiff_rdata_o` записывается в выходной регистр `read_data_o`.