diff --git a/Labs/13. Peripheral units/README.md b/Labs/13. Peripheral units/README.md index 65fcd8a..1f44e1b 100644 --- a/Labs/13. Peripheral units/README.md +++ b/Labs/13. Peripheral units/README.md @@ -154,31 +154,37 @@ _Рисунок 2. Карта памяти периферийных устрой 1. Внимательно ознакомьтесь с [примером описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md). 2. Внимательно ознакомьтесь со спецификацией контроллеров периферии своего варианта. В случае возникновения вопросов, проконсультируйтесь с преподавателем. -3. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен в [примере описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md). +3. Добавьте в проект пакет [`peripheral_pkg`](peripheral_pkg.sv). Данный пакет содержит старшие части адресов периферии в виде параметров, а также вспомогательные вызовы, используемые тестбенчем. +4. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен в [примере описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md). 1. Готовые модули периферии, управление которыми должны осуществлять модули-контроллеры хранятся в папке `peripheral modules`. -4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit). +5. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit). 1. Подключите в проект файл `sys_clk_rst_gen.sv`. 2. Добавьте в модуль `riscv_unit` входы и выходы периферии, а так же замените вход `rst_i` входом `resetn_i`. **Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать**. 3. Создайте в начале описания модуля `riscv_unit` экземпляр модуля `sys_clk_rst_gen`, скопировав приведенный фрагмент кода. 4. Замените подключение тактового сигнала исходных подмодулей `riscv_unit` на появившийся сигнал `sysclk`. Убедитесь, что на модули, имеющие сигнал сброса, приходит сигнал `rst`. -5. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти ([_рис. 2_](../../.pic/Labs/lab_13_periph/fig_02.png)). Это означает, что если вы реализуете контроллер светодиодов, на его вход `req_i` должна подаваться единица в случае, если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`. +6. Интегрируйте модули контроллеров периферии в процессорную систему по приведенной схеме руководствуясь старшими адресами контроллеров, представленными на карте памяти ([_рис. 2_](../../.pic/Labs/lab_13_periph/fig_02.png)). Это означает, что если вы реализуете контроллер светодиодов, на его вход `req_i` должна подаваться единица в случае, если `mem_req_o == 1` и старшие 8 бит адреса равны `0x02`. 1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо. 2. Во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров. -6. Проверьте работу процессорной системы с помощью моделирования. - 1. Для каждой пары контроллеров в папке `firmware/mem_files` представлены файлы, инициализирующие память инструкций. Обратите внимание, что для пары "PS2-VGA" также необходим файл, инициализирующий память данных (в модуле `data_mem` необходимо добавить вызов инициализирующей функции `$readmemh` в блоке `initial`). - 2. Для проверки тестбенч имитирует генерацию данных периферийных устройств ввода. Перед проверкой желательно найти в тестбенче `initial`-блок своего устройства ввода (`sw_block`, `ps2_block`, `uart_block`) — по этому блоку будет понятно, какие данные будет передавать устройство ввода. Именно эти данные в итоге должны оказаться на шине `mem_rd_i`. - 3. Для того, чтобы понять, что устройство работает должным образом, в первую очередь необходимо убедиться, что контроллер устройства ввода успешно осуществил прием данных (сгенерированные тестбенчем данные оказались в соответствующем регистре контроллера периферийного устройства) и выполнил запрос на прерывание. - 4. После чего, необходимо убедиться, что процессор среагировал на данное прерывание, и в процессе его обработки в контроллер устройства вывода были поданы выходные данные. - 5. Для того, чтобы лучше понимать как именно процессор будет обрабатывать прерывание, рекомендуется ознакомиться с исходным кодом исполняемой программы, расположенным в папке `firmware/software`. +7. Проверьте работу процессорной системы с помощью моделирования. + 1. Для моделирования используйте тестбенч `lab_13_tb_system`. + 2. Для каждой пары контроллеров в папке `firmware/mem_files` представлены файлы, инициализирующие память инструкций. Обратите внимание, что для пары "PS2-VGA" также необходим файл, инициализирующий память данных (в модуле `data_mem` необходимо добавить вызов инициализирующей функции `$readmemh` в блоке `initial`). + 3. Для проверки тестбенч имитирует генерацию данных периферийных устройств ввода. Перед проверкой желательно найти в тестбенче `initial`-блок своего устройства ввода (`sw_block`, `ps2_block`, `uart_block`) — по этому блоку будет понятно, какие данные будет передавать устройство ввода. Именно эти данные в итоге должны оказаться на шине `mem_rd_i`. + 4. Для того, чтобы понять, что устройство работает должным образом, в первую очередь необходимо убедиться, что контроллер устройства ввода успешно осуществил прием данных (сгенерированные тестбенчем данные оказались в соответствующем регистре контроллера периферийного устройства) и выполнил запрос на прерывание. + 5. После чего, необходимо убедиться, что процессор среагировал на данное прерывание, и в процессе его обработки в контроллер устройства вывода были поданы выходные данные. + 6. Для того, чтобы лучше понимать как именно процессор будет обрабатывать прерывание, рекомендуется ознакомиться с исходным кодом исполняемой программы, расположенным в папке `firmware/software`. 1. Общая логика программ для всех периферий сводится к ожиданию в бесконечном цикле прерывания от устройства ввода, после чего в процессе обработки прерывания процессор загружает данные от устройства ввода и (возможно преобразовав их) выдает их на устройство вывода. 2. В случае правильной работы программы на временной диаграмме это будет отображено следующим образом: сразу после поступления прерывания от устройства ввода, на системной шине начинается операция чтения из устройства ввода (это легко определить по старшей части адреса, к которому обращается процессор), после чего выполняются операции записи в устройство вывода (аналогично, обращение к устройству вывода можно определить по адресу, к которому обращается процессор). - 6. При моделировании светодиодов лучше уменьшить значение, до которого считает счетчик в режиме "моргания" в 1000 раз, чтобы сократить время моделирования до очередного переключения светодиодов. Перед генерацией битстрима это значение будет необходимо восстановить, иначе моргание станет слишком быстрым и его нельзя будет воспринять невооруженным взглядом. + 7. При моделировании светодиодов лучше уменьшить значение, до которого считает счетчик в режиме "моргания" в 1000 раз, чтобы сократить время моделирования до очередного переключения светодиодов. Перед генерацией битстрима это значение будет необходимо восстановить, иначе моргание станет слишком быстрым и его нельзя будет воспринять невооруженным взглядом. -7. Переходить к следующему пункту можно только после того, как вы полностью убедились в работоспособности модуля на этапе моделирования (увидели корректные значения на выходных сигналах периферии, либо (если по сигналам периферии сложно судить о работоспособности), значениям в контрольных/статусных регистрах модуля-контроллера этой периферии). Генерация битстрима будет занимать у вас долгое время, а итогом вы получите результат: заработало / не заработало, без какой-либо дополнительной информации, поэтому без прочного фундамента на моделировании далеко уехать у вас не выйдет. +8. Переходить к следующему пункту можно только после того, как вы полностью убедились в работоспособности модуля на этапе моделирования (увидели корректные значения на выходных сигналах периферии, либо (если по сигналам периферии сложно судить о работоспособности), значениям в контрольных/статусных регистрах модуля-контроллера этой периферии). Генерация битстрима будет занимать у вас долгое время, а итогом вы получите результат: заработало / не заработало, без какой-либо дополнительной информации, поэтому без прочного фундамента на моделировании далеко уехать у вас не выйдет. -8. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе. -9. Проверьте работу вашей процессорной системы на отладочном стенде с ПЛИС. +9. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе. +10. Проверьте работу вашей процессорной системы на отладочном стенде с ПЛИС. 1. Обратите внимание, что в данной лабораторной уже не будет модуля верхнего уровня `nexys_...`, так как ваш модуль процессорной системы уже полностью самостоятелен и взаимодействует непосредственно с ножками ПЛИС через модули, управляемые контроллерами периферии. + 2. Для проверки периферии переключателей и светодиодов будет достаточно одного лишь отладочного стенда. Для проверки всей остальной периферии может могут потребоваться: компьютер (для uart_rx / uart_tx), клавиатура (для контролера клавиатуры) и VGA-монитор для VGA-контроллера. + 1. Чтобы проверить работоспособность контроллеров UART, необходимо запустить на компьютере программу Putty, в настройках программы указать настройки, которыми будет сконфигурирован программой ваш контроллер (либо указать значения, которыми сбрасываются регистры, если программа ничего не настраивает) и COM-порт, через который компьютер будет общаться с контроллером. Определить нужный COM-порт на операционной системе Windows можно через "Диспетчер устройств", который можно открыть через меню пуск. + В данном окне необходимо найти вкладку "Порты (COM и LPT)", раскрыть ее, а затем подключить отладочный стенд через USB-порт (если тот еще не был подключен). В списке появится новое устройство, а в скобках будет указан нужный COM-порт. + 2. Не смотря на то, что описанный контроллер клавиатуры позволяет управлять клавиатурой с интерфейсом PS/2, некоторые платы (например Nexys A7) позволяют подключать вместо них клавиатуры с USB-интерфейсом. Дело в том, что PS/2 уже давно устарел и найти клавиатуры с таким интерфейсом — задача непростая. Однако протокол передачи по этому интерфейсу очень удобен для образовательны целей, поэтому некоторые производители просто ставят на платы переходник с USB на PS/2, позволяя объединить простоту разработки с удобством использования. --- diff --git a/Labs/13. Peripheral units/lab_13.tb_system.sv b/Labs/13. Peripheral units/lab_13.tb_system.sv new file mode 100644 index 0000000..92da381 --- /dev/null +++ b/Labs/13. Peripheral units/lab_13.tb_system.sv @@ -0,0 +1,103 @@ +/* ----------------------------------------------------------------------------- +* Project Name : Architectures of Processor Systems (APS) lab work +* Organization : National Research University of Electronic Technology (MIET) +* Department : Institute of Microdevices and Control Systems +* Author(s) : Andrei Solodovnikov +* Email(s) : hepoh@org.miet.ru + +See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details. +* ------------------------------------------------------------------------------ +*/ +module lab_13_tb_system(); + +import peripheral_pkg::*; + +logic clk_i; +logic resetn; +logic [15:0] sw_i; +logic [15:0] led_o; +logic ps2_clk; +logic ps2_dat; + +logic [ 6:0] hex_led_o; +logic [ 7:0] hex_sel_o; +logic rx_i; +logic tx_o; + + +initial begin clk_i = 0; end +always #5ns clk_i = ~clk_i; + +initial #4ms $finish(); + +initial begin + resetn = 1; + repeat(20)@(posedge clk_i); + resetn = 0; + repeat(20) @(posedge clk_i); + resetn = 1; +end + +riscv_unit dut( + .clk_i (clk_i ), + .resetn_i (resetn ), + .sw_i (sw_i ), + .led_o (led_o ), + .kclk_i (ps2_clk ), + .kdata_i (ps2_dat ), + .hex_led_o(hex_led_o), + .hex_sel_o(hex_sel_o), + .rx_i (rx_i ), + .tx_o (tx_o ) +); + +initial begin: sw_block + sw_i = 16'd0; + repeat(1000) @(posedge clk_i); + sw_i = 16'hdead; + repeat(1000) @(posedge clk_i); + sw_i = 16'h5555; + repeat(1000) @(posedge clk_i); + sw_i = 16'hbeef; + repeat(1000) @(posedge clk_i); + sw_i = 16'haaaa; +end + +initial begin: ps2_initial_block + ps2_clk = 1'b1; + ps2_dat = 1'b1; + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h1C, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'hf0, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h1C, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h32, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'hf0, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h32, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h21, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'hf0, ps2_clk, ps2_dat); + repeat(1000) @(posedge clk_i); + ps2_send_scan_code(8'h21, ps2_clk, ps2_dat); +end + +initial begin: uart_rx_initial_block + rx_i = 1'b1; + repeat(1000) @(posedge clk_i); + uart_rx_send_char(8'h1c, 115200, rx_i); + repeat(1000) @(posedge clk_i); + uart_rx_send_char(8'h0D, 115200, rx_i); + repeat(1000) @(posedge clk_i); + uart_rx_send_char(8'h0D, 115200, rx_i); + repeat(1000) @(posedge clk_i); + uart_rx_send_char(8'h7F, 115200, rx_i); + repeat(1000) @(posedge clk_i); + uart_rx_send_char(8'h7F, 115200, rx_i); +end + +endmodule diff --git a/Labs/13. Peripheral units/peripheral_pkg.sv b/Labs/13. Peripheral units/peripheral_pkg.sv new file mode 100644 index 0000000..0387391 --- /dev/null +++ b/Labs/13. Peripheral units/peripheral_pkg.sv @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------------- +* Project Name : Architectures of Processor Systems (APS) lab work +* Organization : National Research University of Electronic Technology (MIET) +* Department : Institute of Microdevices and Control Systems +* Author(s) : Andrei Solodovnikov +* Email(s) : hepoh@org.miet.ru + +See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details. +* ------------------------------------------------------------------------------ +*/ +package peripheral_pkg; + + localparam DMEM_ADDR_HIGH = 8'h00; + localparam SW_ADDR_HIGH = 8'h01; + localparam LED_ADDR_HIGH = 8'h02; + localparam PS2_ADDR_HIGH = 8'h03; + localparam HEX_ADDR_HIGH = 8'h04; + localparam RX_ADDR_HIGH = 8'h05; + localparam TX_ADDR_HIGH = 8'h06; + localparam VGA_ADDR_HIGH = 8'h07; + localparam TIMER_ADDR_HIGH = 8'h08; + + task automatic ps2_send_scan_code(input logic [7:0] code, ref logic ps2_clk, ref logic ps2_dat); + logic [11:0] data = {2'b11, !(^code), code, 1'b0}; + for(int i = 0; i < 11; i++) begin + ps2_dat = data[i]; + #15us; + ps2_clk = 1'b0; + #15us; + ps2_clk = 1'b1; + end + endtask + + task automatic uart_rx_send_char(input logic [7:0] char, input logic [31:0] baudrate, ref logic tx); + logic [11:0] data = {2'b11, (^char), char, 1'b0}; + for(int i = 0; i < 12; i++) begin + tx = data[i]; + #(1s/baudrate); + end + endtask + +endpackage \ No newline at end of file diff --git a/Labs/13. Peripheral units/tb_riscv_unit.sv b/Labs/13. Peripheral units/tb_riscv_unit.sv deleted file mode 100644 index 8057416..0000000 --- a/Labs/13. Peripheral units/tb_riscv_unit.sv +++ /dev/null @@ -1,124 +0,0 @@ -/* ----------------------------------------------------------------------------- -* Project Name : Architectures of Processor Systems (APS) lab work -* Organization : National Research University of Electronic Technology (MIET) -* Department : Institute of Microdevices and Control Systems -* Author(s) : Andrei Solodovnikov -* Email(s) : hepoh@org.miet.ru - -See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details. -* ------------------------------------------------------------------------------ -*/ -module tb_riscv_unit(); - -logic clk; -logic ps2_clk; -logic ps2_dat; -logic resetn; -logic [15:0] sw_i; -logic [15:0] led_o; -logic parity; -logic starter; - -logic [ 6:0] hex_led_o; -logic [ 7:0] hex_sel_o; -logic rx_i; -logic tx_o; - -logic [3:0] cntr; - -initial begin clk = 0; ps2_clk = 0; end - -always #5ns clk = ~clk; -always #50000 if(starter || (cntr > 0)) ps2_clk = ~ps2_clk; else ps2_clk = 1; - -logic [11:0] data, uart_data; - -initial #6ms $finish(); - -initial begin - resetn = 1; - repeat(20)@(posedge clk); - resetn = 0; - repeat(20) @(posedge clk); - resetn = 1; -end - -riscv_unit dut( - .clk_i (clk ), - .resetn_i (resetn ), - .sw_i (sw_i ), - .led_o (led_o ), - .kclk_i (ps2_clk ), - .kdata_i (ps2_dat ), - .hex_led_o(hex_led_o), - .hex_sel_o(hex_sel_o), - .rx_i (rx_i ), - .tx_o (tx_o ) -); - -initial begin: sw_block - sw_i = 16'd0; - repeat(1000) @(posedge clk); - sw_i = 16'hdead; - repeat(1000) @(posedge clk); - sw_i = 16'h5555; - repeat(1000) @(posedge clk); - sw_i = 16'hbeef; - repeat(1000) @(posedge clk); - sw_i = 16'haaaa; -end - - -always @(negedge ps2_clk) begin: ps2_always_block - if(starter || (cntr > 0)) - if(cntr == 10) - cntr <= 0; - else - cntr <= cntr + 1; -end - -assign ps2_dat = cntr>0? data[cntr-1] : 1; - -initial begin: ps2_initial_block - cntr = 0; - starter = 0; - data = 0; - repeat(10000) @(posedge clk); - ps2_send_scan_code(8'h1c); - ps2_send_scan_code(8'he0); - ps2_send_scan_code(8'hf0); - ps2_send_scan_code(8'h1c); - ps2_send_scan_code(8'h5c); -end - -task ps2_send_scan_code(input logic [7:0] code); - data = {2'b11, !(^code), code, 1'b0}; - starter = 1; - @(posedge ps2_clk); - starter = 0; - repeat(10) @(posedge ps2_clk); -endtask - - -initial begin: uart_rx_initial_block - uart_data = '1; - repeat(1000) @(posedge clk); - uart_rx_send_char(8'h1c, 115200); - uart_rx_send_char(8'h0D, 115200); - uart_rx_send_char(8'h0D, 115200); - uart_rx_send_char(8'h7F, 115200); - uart_rx_send_char(8'h7F, 115200); -end -assign rx_i = uart_data[0]; -int uart_cntr; -task uart_rx_send_char(input logic [7:0] char, input logic [31:0] baudrate); - uart_data = {2'b11, (^char), char, 1'b0}; - uart_cntr = 0; - while(uart_cntr <= 15) begin - #(1s/baudrate); - uart_data = {1'b1, uart_data[11:1]}; - uart_cntr++; - end -endtask - -endmodule