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

This commit is contained in:
Andrei Solodovnikov
2023-11-27 12:01:27 +03:00
parent 9bbbe0c820
commit 7cc9b9accf
14 changed files with 333 additions and 231 deletions

View File

@@ -157,6 +157,7 @@ _Рисунок 2. Карта памяти периферийных устрой
1. Внимательно ознакомьтесь с [примером описания модуля контроллера](../../Basic%20Verilog%20structures/Controllers.md).
2. Внимательно ознакомьтесь со спецификацией контроллеров периферии своего варианта. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
3. Реализуйте модули контроллеров периферии. Имена модулей и их порты будут указаны в [описании контроллеров](#описание-контроллеров-периферийных-устройств). Пример разработки контроллера приведен [здесь](../../Basic%20Verilog%20structures/Controllers.md).
1. Готовые модули периферии, управление которыми должны осуществлять модули-контроллеры хранятся в папке `peripheral modules`.
4. Обновите модуль `riscv_unit` в соответствии с разделом ["Дополнительные правки модуля riscv_unit"](#дополнительные-правки-модуля-riscv_unit).
1. Подключите в проект файл `sys_clk_rst_gen.sv`.
2. Добавьте в модуль `riscv_unit` входы и выходы периферии. **Необходимо добавить порты даже тех периферийных устройств, которые вы не будете реализовывать**.
@@ -166,9 +167,12 @@ _Рисунок 2. Карта памяти периферийных устрой
1. При интеграции вы должны подключить только модули-контроллеры вашего варианта. Контроллеры периферии других вариантов подключать не надо.
2. При этом во время интеграции, вы должны использовать старшую часть адреса, представленную в карте памяти для формирования сигнала `req_i` для ваших модулей-контроллеров.
6. Проверьте работу процессорной системы с помощью моделирования.
1. Для каждой пары контроллеров периферии предложено две программы: с обновлением данных по опросу и по прерываниям. Запустите моделирование сначала для одной программы, затем для другой (для этого необходимо обновить файл, инициализирующий память инструкций). После проверки работоспособности процессора, сравните поведение сигналов LSU для этих программ.
1. Для каждой пары контроллеров в папке `firmware/mem_files` представлены файлы, инициализирующие память инструкций. Обратите внимание, что для пары "PS2-VGA" так же необходим файл, инициализирующий память данных (в модуле `ext_mem` необходимо прописать блок `$readmemh`).
2. Исходный код программ с адресами и результирующими инструкциями находится в папке `firmware/software`.
3. При моделировании светодиодов лучше уменьшить значение, до которого считает счетчик в режиме "моргания" в 10 раз, чтобы уменьшить время моделирования. Перед генерацией битстрима это значение будет необходимо восстановить.
<!-- 1. Для каждой пары контроллеров периферии предложено две программы: с обновлением данных по опросу и по прерываниям. Запустите моделирование сначала для одной программы, затем для другой (для этого необходимо обновить файл, инициализирующий память инструкций). После проверки работоспособности процессора, сравните поведение сигналов LSU для этих программ. -->
7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
8. Проверьте работу вашей процессорной системы с помощью отладочного стенда с ПЛИС и (при соответствующем варианте) клавиатуры/рабочего компьютера.
8. Проверьте работу вашей процессорной системы на отладочном стенде с ПЛИС.
1. Обратите внимание, что в данной лабораторной уже не будет модуля верхнего уровня `nexys_...`, так как ваш модуль процессорной системы уже полностью самостоятелен и взаимодействует непосредственно с ножками ПЛИС через модули, управляемые контроллерами периферии.
---

View File

@@ -0,0 +1,43 @@
030000b7
04000137
0e000193
0f000213
00e00413
00f00493
00000593
00100293
30429073
03400293
00028293
30529073
00000063
0000a383
04338263
04438c63
00700333
00435313
00612223
00f3f393
00712023
00b04c63
00012a23
00012823
00012623
00012423
00300513
000005b3
00356513
02a12023
30200073
00812a23
00012823
03056513
02a12023
00158593
30200073
00912623
00012423
00c56513
02a12023
00158593
30200073

View File

@@ -0,0 +1,16 @@
030000b7
07000137
070011b7
96018193
00100293
30429073
02400293
30529073
00000063
0000a383
00038403
00812023
00110113
00315463
30200073
07000137

View File

@@ -0,0 +1,23 @@
00000000
00000000
00000000
007E0900
00000000
00317100
737A0000
00327761
64786300
00333465
66762000
00357274
68626E00
00367967
6A6D0000
00383775
696B2C00
0039306F
6C2F2E00
002D703B
00270000
00003D5B
5D0D0000

View File

@@ -0,0 +1,22 @@
010000b7
02000137
0000b1b7
aaa18193
00005237
55520213
00100313
00100293
30429073
03400293
00028293
30529073
00000063
0000a383
00338863
00438a63
00712023
30200073
00612223
30200073
02612223
30200073

View File

@@ -0,0 +1,65 @@
_start:
# Инициализируем начальные значения регистров
0: 030000b7 li x1 , 0x03000000 # сохраняем базовый адрес клавиатуры
4: 04000137 li x2 , 0x04000000 # сохраняем базовый адрес хекс-контроллера
8: 0e000193 li x3 , 0x000000e0 # сохраняем сканкод e0
c: 0f000213 li x4 , 0x000000f0 # сохраняем сканкод f0
10: 00e00413 li x8 , 0x0000000e # сохраняем значение e
14: 00f00493 li x9 , 0x0000000f # сохраняем значение f
18: 00000593 li x11, 0x00000000 # сохраняем ноль
1c: 00100293 li x5 , 0x00000001 # подготавливаем маску прерывания единственного
# (нулевого) входа
20: 30429073 csrw mie, x5 # загружаем маску в регистр маски
24: 03400293 la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
28: 00028293 # только в случае la это число является адресом
# указанного места (адреса обработчика перехвата)
# данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
2c: 30529073 csrw mtvec, x5 # устанавливаем вектор прерывания
# Вызов функции main
main:
30: 00000063 beq x0, x0, main # бесконечный цикл, аналогичный while (1);
# ОБРАБОТЧИК ПЕРЕХВАТА
# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже,
# однако в случае прерывания в программный счетчик будет загружен адрес первой
# нижележащей инструкции.
# Сохраняем используемые регистры на стек
trap_handler:
34: 0000a383 lw x7, 0(x1) # загружаем сканкод
38: 04338263 beq x7, x3, print_e0 # если сканкод e0, отображаем с помощью print_e0
3c: 04438c63 beq x7, x4, print_f0 # если сканкод f0, отображаем с помощью print_f0
40: 00700333 add x6, x0, x7 # дублируем сканкод
44: 00435313 srl x6, x6, 4 # сдвигаем на 4, чтобы получить старший нибл
48: 00612223 sw x6, 4(x2) # записываем старший нибл в первый семисегментник
4c: 00f3f393 andi x7, x7, 0xf # маскируем с f, чтобы получить младший нибл
50: 00712023 sw x7, 0(x2) # записываем младший нибл в нулевой семисегментник
54: 00b04c63 blt x0, x11, print_code # пропускаем обнуление старших хексов
58: 00012a23 sw x0, 20(x2)
5c: 00012823 sw x0, 16(x2) # обнуляем 2-5 семисегментники
60: 00012623 sw x0, 12(x2)
64: 00012423 sw x0, 8(x2)
68: 00300513 addi x10, x0, 3
print_code:
6c: 000005b3 add x11, x0, x0 # обнуляем счетчик
70: 00356513 ori x10, x10, 3 # инициализируем маску, включающую 2 младших хекса
74: 02a12023 sw x10, 32(x2) # записываем маску
78: 30200073 mret # возвращаем управление программе (pc = mepc)
# что означает возврат в бесконечный цикл
print_e0:
7c: 00812a23 sw x8, 20(x2) # записываем e в 5ый семисегментник
80: 00012823 sw x0, 16(x2) # записываем 0 в 4ый семисегментник
84: 03056513 ori x10, x10, 0x30 # включаем отображение 4-5 хексов в маске
88: 02a12023 sw x10, 32(x2) # записываем маску
8c: 00158593 addi x11, x11, 1 # инкрементируем счетчик
90: 30200073 mret
print_f0:
94: 00912623 sw x9, 12(x2) # записываем f в 3ый семисегментник
98: 00012423 sw x0, 8(x2) # записываем 0 в 2ый семисегментник
9c: 00c56513 ori x10, x10, 0xc # включаем отображение 3-2 хексов в маске
a0: 02a12023 sw x10, 32(x2) # записываем маску
a4: 00158593 addi x11, x11, 1 # инкрементируем счетчик
a8: 30200073 mret

View File

@@ -0,0 +1,35 @@
_start:
# Инициализируем начальные значения регистров
0: 030000b7 li x1, 0x03000000 # сохраняем базовый адрес клавиатуры
4: 07000137 li x2, 0x07000000 # сохраняем базовый адрес vga-контроллера
8: 070011b7 li x3, 0x07000960 # количество символов на экране
c: 96018193 li x5, 0x00000001 # подготавливаем маску прерывания единственного
10: # (нулевого) входа
14: 00100293 csrw mie, x5 # загружаем маску в регистр маски
18: 30429073 la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
02400293 # только в случае la это число является адресом
# указанного места (адреса обработчика перехвата)
# данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
1c: 30529073 csrw mtvec, x5 # устанавливаем вектор прерывания
# Вызов функции main
main:
20: 00000063 beq x0, x0, main # бесконечный цикл, аналогичный while (1);
# ОБРАБОТЧИК ПЕРЕХВАТА
# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже,
# однако в случае прерывания в программный счетчик будет загружен адрес первой
# нижележащей инструкции.
# Сохраняем используемые регистры на стек
trap_handler:
24: 0000a383 lw x7, 0(x1) # загружаем сканкод
28: 00038403 lb x8, 0(x7) # берем данные из таблицы подстановки
2c: 00812023 sw x8, 0(x2) # загружаем ascii-значение в vga
30: 00110113 addi x2, x2, 1 # инкрементируем адрес vga
34: 00315463 bge x2, x3, wrap_addr # если адрес vga вышел за границы, то обнуляем
38: 30200073 mret # возвращаем управление программе (pc = mepc)
# что означает возврат в бесконечный цикл
wrap_addr:
3c: 07000137 li x2, 0x07000000 # сохраняем базовый адрес vga-контроллера
40: 30200073 mret

View File

@@ -0,0 +1,40 @@
_start:
# Инициализируем начальные значения регистров
0: 010000b7 li x1, 0x01000000 # сохраняем базовый адрес переключателей
4: 02000137 li x2, 0x02000000 # сохраняем базовый адрес светодиодов
8: 0000b1b7 li x3, 0x0000aaaa # сохраняем спец-код для режима моргания
c: aaa18193
10: 00005237 li x4, 0x00005555 # сохраняем спец-код для сброса
14: 55520213
18: 00100313 li x6, 0x00000001 # сохраняем единицу
1c: 00100293 li x5, 0x00000001 # подготавливаем маску прерывания единственного
# (нулевого) входа
20: 30429073 csrw mie, x5 # загружаем маску в регистр маски
24: 03400293 la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
28: 00028293 # только в случае la это число является адресом
# указанного места (адреса обработчика перехвата)
# данная псевдоинструкция будет разбита на две
# инструкции: lui и addi
2c: 30529073 csrw mtvec, x5 # устанавливаем вектор прерывания
# Вызов функции main
main:
30: 00000063 beq x0, x0, main # бесконечный цикл, аналогичный while (1);
# ОБРАБОТЧИК ПЕРЕХВАТА
# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже,
# однако в случае прерывания в программный счетчик будет загружен адрес первой
# нижележащей инструкции.
# Сохраняем используемые регистры на стек
trap_handler:
34: 0000a383 lw x7, 0(x1) # загружаем значение на переключателях
38: 00338863 beq x7, x3, blink_mode # если пришел спец-код моргания, переходим в blink_mode
3c: 00438a63 beq x7, x4, reset # если пришел спец-код сброса, переходим в reset
40: 00712023 sw x7, 0(x2) # записываем значением с переключателей в светодиоды
44: 30200073 mret # возвращаем управление программе (pc = mepc)
# что означает возврат в бесконечный цикл
blink_mode:
48: 00612223 sw x6, 4(x2) # записываем 1 в led_mode
4c: 30200073 mret
reset:
50: 02612223 sw x6, 0x24(x2) # записываем 1 в led_reset
54: 30200073 mret

View File

@@ -1,5 +1,6 @@
module PS2Receiver(
input logic clk_i,
input logic rst_i,
input logic kclk_i,
input logic kdata_i,
output logic [7:0] keycodeout_o,
@@ -13,13 +14,6 @@ module PS2Receiver(
assign keycode_valid_o = flag_shift[0] && !flag_shift[2];
initial begin //for tb
cnt = 0;
keycodeout_o = 0;
flag_shift = 0;
flag = 0;
end
debouncer debounce(
.clk(clk_i),
.I0(kclk_i),
@@ -28,31 +22,47 @@ debouncer debounce(
.O1(kdataf)
);
always@(posedge clk_i) begin
flag_shift <= (flag_shift << 1) + flag;
if(rst_i) begin
flag_shift <= '0;
end
else begin
flag_shift <= {flag_shift[2:0], flag};
end
end
always_ff @(negedge(kclkf))begin
case(cnt)
0:;
1:keycodeout_o[0]<=kdataf;
2:keycodeout_o[1]<=kdataf;
3:keycodeout_o[2]<=kdataf;
4:keycodeout_o[3]<=kdataf;
5:keycodeout_o[4]<=kdataf;
6:keycodeout_o[5]<=kdataf;
7:keycodeout_o[6]<=kdataf;
8:keycodeout_o[7]<=kdataf;
//TODO ADD PARITY CHECK
9:begin
flag<=1'b1;
end
10:flag<=1'b0;
default: cnt <= 0;
endcase
if(cnt<=9) cnt<=cnt+1;
else if(cnt==10) cnt<=0;
always_ff @(negedge kclkf or posedge rst_i)begin
if(rst_i) begin
cnt <= '0;
end
else if (cnt <= 9) begin
cnt <= cnt + 1;
end
else begin
cnt <= '0;
end
end
always_ff @(negedge kclkf or posedge rst_i) begin
if(rst_i) begin
keycodeout_o <= '0;
end
else begin
case(cnt)
1:keycodeout_o[0]<=kdataf;
2:keycodeout_o[1]<=kdataf;
3:keycodeout_o[2]<=kdataf;
4:keycodeout_o[3]<=kdataf;
5:keycodeout_o[4]<=kdataf;
6:keycodeout_o[5]<=kdataf;
7:keycodeout_o[6]<=kdataf;
8:keycodeout_o[7]<=kdataf;
default: keycodeout_o <= keycodeout_o;
endcase
end
end
assign flag = cnt == 9;
endmodule

View File

@@ -1,112 +0,0 @@
93 00 10 00
13 01 d0 02
93 01 00 08
37 e2 00 00
13 02 b2 06
b7 e2 00 00
93 82 42 07
93 04 f0 0f
13 03 00 08
13 04 50 05
13 0a e0 04
93 05 60 01
13 06 e0 01
93 06 60 02
13 07 50 02
93 07 e0 02
13 08 60 03
93 08 d0 03
13 09 e0 03
93 09 60 04
13 05 50 04
93 03 00 04
93 0a 00 02
13 0b 00 01
93 0c 80 00
13 0d 40 00
93 0d 20 00
37 0e 00 03
13 0e 0e 00
b7 0e 00 04
93 8e 0e 00
23 22 1e 02
23 a2 1e 02
83 2b 4e 00
e3 8e 0b fe
03 2c 0e 00
63 00 2c 04
63 04 4c 04
63 08 5c 04
63 0c 8c 04
63 00 4c 07
63 06 ac 06
63 08 bc 06
63 0a cc 06
63 0c dc 06
63 0e ec 06
63 00 fc 08
63 02 0c 09
63 04 1c 09
63 06 2c 09
63 08 3c 09
6f ff 9f fb
23 22 1e 02
23 a2 1e 02
6f ff 9f f2
e3 84 61 fa
b3 91 11 00
6f ff 1f fa
e3 8e 11 f8
b3 d1 11 00
6f ff 5f f9
b3 e4 91 00
23 a0 9e 02
6f ff 9f f8
b3 e4 91 00
b3 84 34 40
23 a0 9e 02
6f ff 9f f7
b3 0f 00 00
6f 0f c0 04
b3 0f 10 00
6f 0f 40 04
93 0f 20 00
6f 0f c0 03
93 0f 30 00
6f 0f 40 03
93 0f 40 00
6f 0f c0 02
93 0f 50 00
6f 0f 40 02
93 0f 60 00
6f 0f c0 01
93 0f 70 00
6f 0f 40 01
93 0f 80 00
6f 0f c0 00
93 0f 90 00
6f 0f 40 00
63 96 61 00
23 ae fe 01
6f ff df f1
63 96 71 00
23 ac fe 01
6f ff 1f f1
63 96 51 01
23 aa fe 01
6f ff 5f f0
63 96 61 01
23 a8 fe 01
6f ff 9f ef
63 96 91 01
23 a6 fe 01
6f ff df ee
63 96 a1 01
23 a4 fe 01
6f ff 1f ee
63 96 b1 01
23 a2 fe 01
6f ff 5f ed
e3 98 11 ec
23 a0 fe 01
6f ff 9f ec

View File

@@ -1,32 +0,0 @@
01400293
30529073
00200293
30429073
00000063
34202373
00200393
06731063
01000337
00032383
0000BE37
AAAE0E13
01C39463
FCDFF06F
00005E37
555E0E13
01C38A63
02000E37
007E2023
000E2223
02C0006F
02000E37
004E0E13
00100E93
01DE2023
0180006F
02000E37
024E0E13
00100E93
01DE2023
0040006F
30200073

View File

@@ -1,21 +0,0 @@
00100493
0aa00913
0aa00293
00891913
00590933
55500993
00500293
00499993
005989b3
01000e37
02000eb7
000e2a03
012a0a63
013a0c63
014ea023
000ea223
fedff2ef
029ea223
fe5ff2ef
009ea223
fddff2ef

View File

@@ -5,41 +5,42 @@
// Module Name: tb_riscv_unit
// Project Name: RISCV_practicum
// Target Devices: Nexys A7-100T
// Description: tb for peripheral units
//
//////////////////////////////////////////////////////////////////////////////////
module tb_riscv_unit();
localparam variant = 'd1; //1-SW_LED; 2-PS2_HEXLED; 3-UART;
localparam button = 8'h16; //keyboard key code
logic clk;
logic ps2_clk;
logic ps2_dat;
logic resetn;
logic [15:0] sw_i;
logic [15:0] led_o;
logic parity;
logic parity;
logic starter;
logic [ 6:0] hex_led_o;
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 #50 clk = ~clk;
always #50000 if (variant == 2) if(starter || (cntr > 0)) ps2_clk = ~ps2_clk; else ps2_clk = 1;
always #5 clk = ~clk;
always #50000 if(starter || (cntr > 0)) ps2_clk = ~ps2_clk; else ps2_clk = 1;
logic [11:0] data;
initial #5ms $finish();
initial begin
resetn = 1;
repeat(2)@(posedge clk);
repeat(20)@(posedge clk);
resetn = 0;
repeat(2) @(posedge clk);
repeat(20) @(posedge clk);
resetn = 1;
end
@@ -56,43 +57,51 @@ riscv_unit dut(
.tx_o (tx_o )
);
logic [3:0] cntr;
initial begin: sw_block
sw_i = 16'd0;
repeat(260) @(posedge clk);
sw_i = 16'hdead;
repeat(300) @(posedge clk);
sw_i = 16'h5555;
repeat(300) @(posedge clk);
sw_i = 16'hbeef;
repeat(300) @(posedge clk);
sw_i = 16'haaaa;
end
always @(negedge ps2_clk) begin
always @(negedge ps2_clk) begin: ps2_always_block
if(starter || (cntr > 0))
if(cntr == 10)
cntr <= 0;
else
cntr <= cntr + 1;
cntr <= cntr + 1;
end
assign ps2_dat = cntr>0? data[cntr-1] : 1;
initial begin
sw_i = 16'h0;
initial begin: ps2_initial_block
cntr = 0;
starter = 0;
parity = !(^button);
data = 0;
case (variant)
1: begin
#10000;
sw_i = 16'h01AA;
#20000;
sw_i = 16'hFF00;
end
2: begin
data = {2'b11, parity, button, 1'b0};
#100000;
starter = 1;
@(posedge ps2_clk)
starter = 0;
end
3: begin
end
endcase
#100000;
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
// TODO uart block
endmodule