mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
ЛР12. Обновление методички
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
4. Семисегментные индикаторы
|
||||
5. UART-приемник
|
||||
6. UART-передатчик
|
||||
7. Видеоадаптер
|
||||
|
||||
В таком случае, если мы захотим обратиться в четвертый регистр семисегментных индикаторов, мы должны будем использовать адрес `0x03000004`. Старшие 8 бит (`0x03`) определяют выбранное периферийное устройство, оставшиеся 24 бита определяют конкретный адрес в адресном пространстве этого устройства.
|
||||
|
||||
@@ -81,19 +82,30 @@ module riscv_unit(
|
||||
|
||||
// Входы и выходы периферии
|
||||
input logic [15:0] sw_i, // Переключатели
|
||||
|
||||
output logic [15:0] led_o, // Светодиоды
|
||||
|
||||
input logic kclk_i, // Тактирующий сигнал клавиатуры
|
||||
input logic kdata_i, // Сигнал данных клавиатуры
|
||||
|
||||
output logic [ 6:0] hex_led_o, // Вывод семисегментных индикаторов
|
||||
output logic [ 7:0] hex_sel_o, // Селектор семисегментных индикаторов
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Эти порты нужно подключить к одноименным портам ваших контроллеров периферии (**речь идет только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**).
|
||||
Эти порты нужно подключить к одноименным портам ваших контроллеров периферии (**речь идет только о реализуемых вами контроллерах, остальные порты должны остаться неподключенными**). Иными словами, в описании модуля должны быть все указанные входы и выходы. Но использовать вам нужно только порты, связанные с теми периферийными устройствами, реализацию которых вам необходимо подключить к процессорной системе в рамках индивидуального задания.
|
||||
|
||||
Обратите внимание на то, что изменился сигнал сброса (`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
|
||||
|
||||

|
||||
|
||||
_Рисунок 2. Карта памяти периферийных устройств_
|
||||
|
||||
Работа с картой осуществляется следующим образом. Под названием каждого периферийного устройства указана старшая часть адреса (чему должны быть равны старшие 8 бит адреса, чтобы было сформировано обращение к данному периферийному устройству). Например, для переключателей это значение равно `0x01`, для светодиодов `0x02` и т.п.
|
||||
В самом левом столбце указаны используемые/неиспользуемые адреса в адресном пространстве данного периферийного устройства. Например для переключателей есть только один используемый адрес: `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).
|
||||
4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit).
|
||||
1. Подключите в проект файл `sys_clk_rst_gen.sv`.
|
||||
2. Добавьте в модуль `riscv_unit` входы и выходы периферии. Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать.
|
||||
2. Добавьте в модуль `riscv_unit` входы и выходы периферии. **Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать**.
|
||||
3. Создайте в начале описания модуля `riscv_unit` экземпляр модуля `sys_clk_rst_gen`, скопировав приведенный фрагмент кода.
|
||||
4. Замените подключение тактового сигнала исходных подмодулей `riscv_unit` на появившийся сигнал `sysclk`. Убедитесь, что на модули имеющие сигнал сброса приходит сигнал `rst`.
|
||||
5. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти ([_рис. 2_](../../.pic/Labs/lab_12_periph/fig_02.png)). Это означает, что если вы реализуете контроллер светодиодов, на его вход `req_i` должна подаваться единица в случае если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`.
|
||||
1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо.
|
||||
2. При этом во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров.
|
||||
3. Даже если вы не используете какие-то входные/выходные сигналы в модуле `riscv_unit` (например по варианту вам не достался контроллер клавиатуры и поэтому вы не используете сигналы `kclk_i` и `kdata_i`), вы все равно должны их описать во входах и выходах модуля `riscv_unit`.
|
||||
6. Проверьте работу процессорной системы с помощью моделирования.
|
||||
1. Для каждой пары контроллеров периферии предложено две программы: с обновлением данных по опросу и по прерываниям. Запустите моделирование сначала для одной программы, затем для другой (для этого необходимо обновить файл, инициализирующий память инструкций). После проверки работоспособности процессора, сравните поведение сигналов LSU для этих программ.
|
||||
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` уже записывается новое, еще непрочитанное значение).
|
||||
|
||||
Обнуление регистра `scan_code_is_unread` должно происходить и в случае получения сигнала `interrupt_return_i` (однако опять же, если в этот момент приходит сигнал `keycode_valid_o`, обнулять регистр не нужно).
|
||||
|
||||
В случае запроса на чтение по адресу `0x04` необходимо вернуть значение регистра `scan_code_is_unread`.
|
||||
|
||||
В случае запроса на запись по адресу `0x24` со значением `1`, необходимо осуществить сброс регистров `scan_code` и `scan_code_is_unread` в `0`.
|
||||
@@ -505,6 +522,7 @@ module uart_rx_sb_ctrl(
|
||||
*/
|
||||
|
||||
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 write_enable_i,
|
||||
output logic [31:0] read_data_o,
|
||||
output logic ready_o,
|
||||
|
||||
/*
|
||||
Часть интерфейса модуля, отвечающая за подключение передающему,
|
||||
@@ -551,21 +570,21 @@ module uart_tx_sb_ctrl(
|
||||
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` модуля-контроллера равен единице.
|
||||
|
||||
Выходной сигнал `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`.
|
||||
|
||||
В регистр `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` равен нулю. Доступ на чтение этого регистра может осуществляться в любой момент времени.
|
||||
|
||||
@@ -595,3 +614,162 @@ endmodule
|
||||
|0x10 | RW | [0:1] | Чтение/запись регистра `parity`, отвечающего за включение отключение проверки данных через бит четности |
|
||||
|0x14 | RW | [0:1] | Чтение/запись регистра `stopbit`, отвечающего за длину стопового бита |
|
||||
|0x24 | W | 1 | Запись сигнала сброса |
|
||||
|
||||
### Видеоадаптер
|
||||
|
||||
Видеоадаптер позволяет выводить информацию на экран через интерфейс **VGA**. Предоставляемый в данной лабораторной работе vga-модуль способен выводить `80х30` символов (разрешение символа `8x16`). Таким образом, итоговое разрешение экрана, поддерживаемого vga-модулем будет `80*8 x 30*16 = 640x480`. VGA-модуль поддерживает управление цветовой схемой для каждого поля символа в сетке `80х30`. Это значит что каждый символ (и фон символа) может быть отрисован отдельным цветом из диапазона 16-ти цветов.
|
||||
|
||||

|
||||
|
||||
_Рисунок 3. Пример вывода на экран символьной информации_
|
||||
|
||||
Для управления выводимым на экран содержимым, адресное пространство модуля разделено на следующие диапазоны:
|
||||
|
||||

|
||||
|
||||
_Рисунок 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-значения:
|
||||
|
||||

|
||||
|
||||
_Рисунок 5. Цветовая палитра vga-модуля_
|
||||
|
||||
Таким образом, для установки цветовой схемы, необходимо выбрать два цвета из палитры, склеить их (в старших разрядах идет цвет символа, в младших — цвет фона) и записать получившееся 8-битное значение по адресу выбранной позиции в диапазоне адресов цветовой схемы (color_map).
|
||||
|
||||
К примеру, мы хотим установить черный фоновый цвет и белый цвет в качестве цвета символа для верхней левой позиции. В этом случае, мы должны записать значение `f0` (f(15) — код белого цвета, 0 — код черного цвета) по адресу `0x0000_1000` (нулевой адрес в диапазоне `color_map`).
|
||||
|
||||
Для отрисовки символов, мы условно поделили экран на сетку `80х30`, и для каждой позиции в этой сетке определили фоновый и активный цвет. Чтобы модуль мог отрисовать символ на очередной позиции (которая занимает `16х8` пикселей), ему необходимо знать в какой цвет необходимо окрасить каждый пиксель для каждого ascii-кода. Для этого используется память шрифтов.
|
||||
|
||||
Допустим, нам необходимо отрисовать символ `F` (ascii-код `0x46`).
|
||||
|
||||

|
||||
_Рисунок 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`.
|
||||
|
||||
|
Reference in New Issue
Block a user