Переработка лабораторных работ, связанных с памятью (#89)

* Переработка лабораторных работ, связанных с памятью

Существенно переработаны ЛР3 и ЛР7:

Из ЛР3 убрано задание реализовать память данных. Эта память
использовалась только студентами ИВТ и только в рамках одной лабы.
В итоге использовалась готовая память, и ничего не мешает использовать
ее с самого начала.

Задание по реализации памяти инструкций также претерпело изменения.
Теперь код памяти инструкций предоставляется сразу. Это объясняется тем,
что код модуля состоит в общем-то из 4х строк, на которые тратится
слишком много времени (с учетом добавления тестбенча и проверок).
Кроме того, использование готового кода позволяет дать модуль чуть
посложнее (с параметризацией размера).

По итогу правок, третья лабораторная работа превращается чисто в
лабораторную по написанию регистрового файла, что должно положительно
сказаться на кривой сложности лаб. После второй лабы происходит слишком
резкий скачок в объемах работы.

Соответственно, в связи с тем, что память данных больше не делается на
третьей лабе, дополнительная ЛР по памяти данных с byte enable
необходимо перенести до реализации тракта данных.

* ЛР3, 5, 6. Правки из ревью

* ЛР7. Добавление иллюстраций
This commit is contained in:
Andrei Solodovnikov
2024-07-02 10:24:41 +03:00
committed by GitHub
parent 06dc07c03f
commit 85883858ac
36 changed files with 278 additions and 368 deletions

4
.github/SUMMARY.md vendored
View File

@@ -21,8 +21,8 @@
- [Лабораторная №3. Регистровый файл и внешняя память](Labs/03.%20Register%20file%20and%20memory/README.md) - [Лабораторная №3. Регистровый файл и внешняя память](Labs/03.%20Register%20file%20and%20memory/README.md)
- [Лабораторная №4. Простейшее программируемое устройство](Labs/04.%20Primitive%20programmable%20device/README.md) - [Лабораторная №4. Простейшее программируемое устройство](Labs/04.%20Primitive%20programmable%20device/README.md)
- [Лабораторная №5. Основной дешифратор](Labs/05.%20Main%20decoder/README.md) - [Лабораторная №5. Основной дешифратор](Labs/05.%20Main%20decoder/README.md)
- [Лабораторная №6. Тракт данных](Labs/06.%20Datapath/README.md) - [Лабораторная №6. Основная память](Labs/06.%20Main%20memory/README.md)
- [Лабораторная №7. Внешняя память](Labs/07.%20External%20memory/README.md) - [Лабораторная №7. Тракт данных](Labs/07.%20Datapath/README.md)
- [Лабораторная №8. Блок загрузки и сохранения](Labs/08.%20Load-store%20unit/README.md) - [Лабораторная №8. Блок загрузки и сохранения](Labs/08.%20Load-store%20unit/README.md)
- [Лабораторная №9. Интеграция LSU](Labs/09.%20LSU%20Integration/README.md) - [Лабораторная №9. Интеграция LSU](Labs/09.%20LSU%20Integration/README.md)
- [Лабораторная №10. Подсистема прерываний](Labs/10.%20Interrupt%20subsystem/README.md) - [Лабораторная №10. Подсистема прерываний](Labs/10.%20Interrupt%20subsystem/README.md)

4
.github/index.md vendored
View File

@@ -24,7 +24,7 @@
4. Простейшее программируемое устройство ([04. Primitive programmable device](Labs/04.%20Primitive%20programmable%20device)) 4. Простейшее программируемое устройство ([04. Primitive programmable device](Labs/04.%20Primitive%20programmable%20device))
5. Основной дешифратор ([05. Main decoder](Labs/05.%20Main%20decoder)) 5. Основной дешифратор ([05. Main decoder](Labs/05.%20Main%20decoder))
6. 6.
1. Тракт данных ([06. Datapath](Labs/06.%20Datapath)) 1. Тракт данных ([07. Datapath](Labs/07.%20Datapath))
2. Интеграция блока загрузки и сохранения ([09. LSU Integration](Labs/09.%20LSU%20Integration)) 2. Интеграция блока загрузки и сохранения ([09. LSU Integration](Labs/09.%20LSU%20Integration))
3. Интеграция подсистемы прерываний ([11. Interrupt Integration](Labs/11.%20Interrupt%20integration)) 3. Интеграция подсистемы прерываний ([11. Interrupt Integration](Labs/11.%20Interrupt%20integration))
7. Периферийные устройства ([13. Peripheral units](Labs/13.%20Peripheral%20units)) 7. Периферийные устройства ([13. Peripheral units](Labs/13.%20Peripheral%20units))
@@ -37,7 +37,7 @@
1. Память ([03. Register file and memory](Labs/03.%20Register%20file%20and%20memory)), 1. Память ([03. Register file and memory](Labs/03.%20Register%20file%20and%20memory)),
2. Простейшее программируемое устройство ([04. Primitive programmable device](Labs/04.%20Primitive%20programmable%20device)) 2. Простейшее программируемое устройство ([04. Primitive programmable device](Labs/04.%20Primitive%20programmable%20device))
3. Основной дешифратор ([05. Main decoder](Labs/05.%20Main%20decoder)) 3. Основной дешифратор ([05. Main decoder](Labs/05.%20Main%20decoder))
4. Тракт данных ([06. Datapath](Labs/06.%20Datapath)) 4. Тракт данных ([07. Datapath](Labs/07.%20Datapath))
5. 5.
1. Модуль загрузки и сохранения ([08. Load-store unit](Labs/08.%20Load-store%20unit)) 1. Модуль загрузки и сохранения ([08. Load-store unit](Labs/08.%20Load-store%20unit))
2. Интеграция блока загрузки и сохранения ([09. LSU Integration](Labs/09.%20LSU%20Integration)) 2. Интеграция блока загрузки и сохранения ([09. LSU Integration](Labs/09.%20LSU%20Integration))

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 619 KiB

After

Width:  |  Height:  |  Size: 619 KiB

View File

Before

Width:  |  Height:  |  Size: 744 KiB

After

Width:  |  Height:  |  Size: 744 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 243 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,4 +1,4 @@
# Лабораторная работа 3 "Регистровый файл и внешняя память" # Лабораторная работа 3 "Регистровый файл и память инструкций"
Процессор — это программно-управляемое устройство, выполняющее обработку информации и управление этим процессом. Очевидно, программа, которая управляет процессором, должна где-то храниться. Данные, с которыми процессор работает, тоже должны быть в доступном месте. Нужна память! Процессор — это программно-управляемое устройство, выполняющее обработку информации и управление этим процессом. Очевидно, программа, которая управляет процессором, должна где-то храниться. Данные, с которыми процессор работает, тоже должны быть в доступном месте. Нужна память!
@@ -6,11 +6,10 @@
Описать на языке SystemVerilog элементы памяти для будущего процессора: Описать на языке SystemVerilog элементы памяти для будущего процессора:
- память команд (Instruction Memory); - память инструкций (Instruction Memory);
- память данных (Data Memory);
- регистровый файл (Register File). - регистровый файл (Register File).
## Допуск к лабораторной работе ## Материалы для подготовки к лабораторной работе
Для успешного выполнения лабораторной работы, вам необходимо освоить: Для успешного выполнения лабораторной работы, вам необходимо освоить:
@@ -21,7 +20,7 @@
1. Изучить способы организации памяти (раздел [#теория про память](#теория-про-память)). 1. Изучить способы организации памяти (раздел [#теория про память](#теория-про-память)).
2. Изучить конструкции SystemVerilog для реализации запоминающих элементов (раздел [#инструменты](#инструменты-для-реализации-памяти)). 2. Изучить конструкции SystemVerilog для реализации запоминающих элементов (раздел [#инструменты](#инструменты-для-реализации-памяти)).
3. В проекте с прошлой лабораторной реализовать модули: Instruction Memory, Data Memory и Register File ([#задание](#задание-по-реализации-памяти)). 3. В проекте с прошлой лабораторной реализовать модули: Instruction Memory и Register File ([#задание](#задание-по-реализации-памяти)).
4. Проверить с помощью тестового окружения корректность их работы. 4. Проверить с помощью тестового окружения корректность их работы.
5. Проверить работу регистрового файла в ПЛИС. 5. Проверить работу регистрового файла в ПЛИС.
@@ -66,7 +65,7 @@ _Рисунок 1. Примеры блоков ПЗУ и ОЗУ._
_Рисунок 2. Структурная схема логического блока в ПЛИС[[1]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._ _Рисунок 2. Структурная схема логического блока в ПЛИС[[1]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._
В логическом блоке есть **таблицы подстановки** (Look Up Table, LUT), которые представляют собой не что иное как память, которая переконфигурируется под нужды хранения, а не реализацию логики. Таким образом, трехвходовой LUT может выступать в роли восьмиразрядной памяти. В логическом блоке есть **таблицы подстановки** (Look Up Table, LUT), которые представляют собой не что иное как память, которая переконфигурируется под нужды хранения, а не реализацию логики. Таким образом, трехвходовой LUT может выступать в роли 8-битной памяти.
Однако LUT будет сложно приспособить под многопортовую память: посмотрим на схему еще раз: три входа LUT формируют адрес одной из восьми ячеек. Это означает, что среди этих восьми ячеек нельзя обратиться к двум из них одновременно. Однако LUT будет сложно приспособить под многопортовую память: посмотрим на схему еще раз: три входа LUT формируют адрес одной из восьми ячеек. Это означает, что среди этих восьми ячеек нельзя обратиться к двум из них одновременно.
@@ -116,6 +115,12 @@ logic [19:0] memory3 [1:16]; // А вот memory3 хоть и совпадае
Для обращения к конкретной ячейке памяти используются квадратные скобки с указанием нужного адреса `memory[addr]`. Грубо говоря, то, что указывается в квадратных скобках будет подключено ко входу адреса памяти `memory`. Для обращения к конкретной ячейке памяти используются квадратные скобки с указанием нужного адреса `memory[addr]`. Грубо говоря, то, что указывается в квадратных скобках будет подключено ко входу адреса памяти `memory`.
Чтение из памяти может быть сделано двумя способами: синхронно и асинхронно.
Синхронное чтение подразумевает ожидание следующего тактового синхроимпульса для выдачи данных после получения адреса. Иными словами, данные будут установлены на выходе не в тот же такт, когда был выставлен адрес на вход памяти данных, а на следующий. Не смотря на то, что в таком случае на каждой операции чтения "теряется" один такт, память с синхронным чтением имеет значительно меньший критический путь, чем положительно сказывается на временных характеристиках итоговой схемы.
Память с асинхронным чтением выдает данные в том же такте, что и получает адрес (т.е. ведет себя как комбинационная схема). Несмотря на то, что такой подход кажется быстрее, память с асинхронным чтением обладает длинным критическим путем, причем чем большего объема будет память, тем длиннее будет критический путь.
Реализация асинхронного подключения к выходу памяти осуществляется оператором `assign`. А, если требуется создать память с синхронным чтением, то присваивание выходу требуется описать внутри блока`always_ff`. Реализация асинхронного подключения к выходу памяти осуществляется оператором `assign`. А, если требуется создать память с синхронным чтением, то присваивание выходу требуется описать внутри блока`always_ff`.
Так как запись в память является синхронным событием, то описывается она в конструкции `always_ff`. При этом, как и при описании регистра, можно реализовать управляющий сигнал разрешения на запись через блок вида `if(write_enable)`. Так как запись в память является синхронным событием, то описывается она в конструкции `always_ff`. При этом, как и при описании регистра, можно реализовать управляющий сигнал разрешения на запись через блок вида `if(write_enable)`.
@@ -186,7 +191,7 @@ module rom16_8 (
logic [7:0] ROM [0:15]; // создать память с 16-ю 8-битными ячейками logic [7:0] ROM [0:15]; // создать память с 16-ю 8-битными ячейками
initial begin initial begin
$readmemh("rom_data.mem", ROM); // поместить в память RAM содержимое $readmemh("rom_data.mem", ROM); // поместить в память ROM содержимое
end // файла rom_data.mem end // файла rom_data.mem
@@ -210,11 +215,10 @@ endmodule
## Задание по реализации памяти ## Задание по реализации памяти
Необходимо описать на языке SystemVerilog три вида памяти: Необходимо описать на языке SystemVerilog два вида памяти:
1. память инструкций; 1. память инструкций;
2. память данных; 2. регистровый файл.
3. регистровый файл.
### 1. Память инструкций ### 1. Память инструкций
@@ -234,44 +238,53 @@ mоdulе instr_mеm(
При этом по спецификации процессор RISC-V использует память с побайтовой адресацией. Байтовая адресация означает, что процессор способен обращаться к отдельным байтам в памяти (за каждым байтом памяти закреплен свой индивидуальный адрес). При этом по спецификации процессор RISC-V использует память с побайтовой адресацией. Байтовая адресация означает, что процессор способен обращаться к отдельным байтам в памяти (за каждым байтом памяти закреплен свой индивидуальный адрес).
Однако, если у памяти будут 32-рязрядные ячейки, доступ к конкретному байту будет осложнен, ведь каждая ячейка — это 4 байта. Как получить данные третьего байта памяти? Если обратиться к третьей ячейке в массиве — придут данные 12-15-ых байт (поскольку каждая ячейка содержит по 4 байта). Чтобы получить данные третьего байта, необходимо **разделить значение пришедшего адреса на 4** (отбросив остаток от деления). `3/4=0` — и действительно, если обратиться к нулевой ячейке памяти — будут получены данные 3-го, 2-го, 1-го и 0-го байт. То, что помимо значения третьего байта есть еще данные других байт нас в данный момент не интересует, важна только сама возможность указать адрес конкретного байта. Однако, если у памяти будут 32-битные ячейки, доступ к конкретному байту будет осложнен, ведь каждая ячейка — это 4 байта. Как получить данные третьего байта памяти? Если обратиться к третьей ячейке в массиве — придут данные 12-15-ых байт (поскольку каждая ячейка содержит по 4 байта). Чтобы получить данные третьего байта, необходимо **разделить значение пришедшего адреса на 4** (отбросив остаток от деления). `3/4=0` — и действительно, если обратиться к нулевой ячейке памяти — будут получены данные 3-го, 2-го, 1-го и 0-го байт. То, что помимо значения третьего байта есть еще данные других байт нас в данный момент не интересует, важна только сама возможность указать адрес конкретного байта.
Деление на 2<sup>n</sup> можно осуществить, отбросив `n` младших бит числа. Учитывая то, что для адресации 1024 ячеек памяти мы будем использовать 10 бит адреса, память инструкций должна выдавать на выход данные, расположенные по адресу `addr_i[11:2]`. Деление на 2<sup>n</sup> можно осуществить, отбросив `n` младших бит числа. Учитывая то, что для адресации 1024 ячеек памяти мы будем использовать 10 бит адреса, память инструкций должна выдавать на выход данные, расположенные по адресу `addr_i[11:2]`.
### 2. Память данных Не смотря на заданный размер памяти инструкций в 1024 32-битных ячейки, на практике удобно параметризовать это значение, чтобы в ситуациях, когда требуется меньше или больше памяти можно было получить обновленное значение, не переписывая код во множестве мест. Подобное новшество вы сможете оценить на практике, получив возможность существенно сокращать время синтеза процессора, уменьшая размер памяти до необходимого минимума путем изменения значения одного лишь параметра.
У данного модуля будет шесть входных/выходных сигналов: Для этого можно например создать параметр: `INSTR_MEM_SIZE_BYTES`, показывающий размер памяти инструкций в байтах. Однако, поскольку у данной памяти 32-битные ячейки, нам было бы удобно иметь и параметр `INSTR_MEM_SIZE_WORDS`, который говорит сколько в памяти 32-битных ячеек.
При этом `INSTR_MEM_SIZE_WORDS = INSTR_MEM_SIZE_BYTES / 4` (т.е. в 32-битном слове 4 байта).
- вход тактового синхроимпульса В случае подобной параметризации, необходимо иметь возможность подстраивать количество используемых бит адреса. Для 1024 ячеек памяти мы использовали 10 бит адреса, для 512 ячеек нам потребуется уже 9 бит. Нетрудно заметить, что нам нужно такое число бит данных, возведя в степень которого `2`, мы получим размер нашей памяти (либо число, превышающее этот размер в случае, если размер памяти не является степенью двойки). Иными словами, нам нужен логарифм по основанию 2 от размера памяти, с округлением до целого вверх. И неудивительно, что в SystemVerilog есть специальная конструкция, которая позволяет считать подобные числа. Эта конструкция называется `$clog2` (`с` означает "ceil" — операцию округления вверх).
- вход запроса на работу с памятью
- вход сигнала разрешения записи Поскольку реализация памяти состоит буквально из нескольких строчек, но при этом использование параметров может вызвать некоторые затруднения, код памяти инструкций предоставляется в готовом виде:
- 32-битный вход адреса
- 32-битный вход данных записи
- 32-битный выход данных синхронного чтения
```SystemVerilog ```SystemVerilog
mоdulе data_mеm( mоdulе instr_mеm
inрut logic clk_i, import memory_pkg::INSTR_MEM_SIZE_BYTES;
input logic mem_req_i, import memory_pkg::INSTR_MEM_SIZE_WORDS;
inрut logic write_enable_i, (
inрut logic [31:0] addr_i, inрut logic [31:0] addr_i,
inрut logic [31:0] write_data_i,
оutрut logic [31:0] rеаd_dаtа_o оutрut logic [31:0] rеаd_dаtа_o
); );
logic [31:0] ROM [INSTR_MEM_SIZE_WORDS]; // создать память с
// <INSTR_MEM_SIZE_WORDS>
// 32-битных ячеек
initial begin
$readmemh("program.mem", ROM); // поместить в память ROM содержимое
end // файла program.mem
// Реализация асинхронного порта на чтение, где на выход идет ячейка памяти
// инструкций, расположенная по адресу addr_i, в котором обнулены два младших
// бита, а также биты, двоичный вес которых превышает размер памяти данных
// в байтах.
// Два младших бита обнулены, чтобы обеспечить выровненный доступ к памяти,
// в то время как старшие биты обнулены, чтобы не дать обращаться в память
// по адресам несуществующих ячеек (вместо этого будут выданы данные ячеек,
// расположенных по младшим адресам).
assign rеаd_dаtа_o = R0M[addr_i[$clog2(INSTR_MEM_SIZE_BYTES)-1:2]];
endmodule
``` ```
Как и память инструкций, память данных будет состоять из 32-разрядных ячеек. Только теперь их будет 4096, а значит при обращении к ячейкам памяти нужно использовать не 10 бит адреса, а 12. При этом по-прежнему необходимо разделить пришедший адрес на 4, т.е. нужно отбросить два младших бита. Таким образом, обращение к ячейкам памяти (для записи и чтения) должно осуществляться по адресу `addr_i[13:2]`.
Однако в отличие от памяти инструкций, в память данных добавлено два управляющих сигнала (`mem_req_i`и `write_enable_i`). Сигнал `mem_req_i` является сигналом запроса на работу с памятью. Без этого сигнала память не должна выполнять операции чтения/записи (вне зависимости от сигнала `write_enable`, определяющего происходит сейчас запись или чтение). Как сделать так, чтобы не происходило чтение без запроса? Например, не обновлять значение, считанное во время предыдущей операции чтения.
Если `mem_req_i == 1` и `write_enable_i == 1`, то происходит запрос на запись в память. В этом случае, необходимо записать значение `write_data_i` в ячейку по адресу `addr_i[13:2]`. Во всех других случаях (любой из сигналов `mem_req_i`, `write_enable_i` равен нулю), запись в память не производится.
Если `mem_req_i == 1` и `write_enable_i == 0`, то происходит запрос на чтение из памяти. В этом случае, необходимо записать в выходной регистр `read_data_o` значение из ячейки по адресу `addr_i[13:2]`. Во всех других случаях чтение из памяти не производится (`read_data_o` сохраняет предыдущее значение).
### 3. Регистровый файл ### 3. Регистровый файл
На языке SystemVerilog необходимо реализовать модуль регистрового файла (`rf_r𝚒sсv`) для процессора с архитектурой RISC-V, представляющего собой трехпортовое ОЗУ с двумя портами на чтение и одним портом на запись и состоящей из 32-х 32-битных регистров, объединенных в массив с именем `rf_mem`.
У данного модуля будет восемь входных/выходных сигналов: У данного модуля будет восемь входных/выходных сигналов:
- вход тактового синхроимпульса - вход тактового синхроимпульса
@@ -299,46 +312,24 @@ mоdulе rf_r𝚒sсv(
``` ```
На языке SystemVerilog необходимо реализовать модуль регистрового файла (`rf_r𝚒sсv`) для процессора с архитектурой RISC-V, представляющего собой трехпортовую ОЗУ с двумя портами на чтение и одним портом на запись и состоящей из 32-х 32-битных регистров с именем `rf_mem`. По адресу `0` должно всегда считываться значение `0` вне зависимости от того, какое значение в этой ячейке памяти, и есть ли она вообще. Такая особенность обусловлена тем, что при выполнении операций очень часто используется ноль (сравнение с нулем, инициализация переменных нулевым значением, копирование значения одного регистра в другой посредством сложения с нулем и записи результата и т.п.). Эту особенность регистрового файла можно реализовать несколькими способами:
При этом по адресу `0` должно всегда считываться значение `0` вне зависимости от того, какое значение в этой ячейке памяти, и есть ли она вообще. Такая особенность обусловлена тем, что при выполнении операций очень часто используется ноль (сравнение с нулем, инициализация переменных нулевым значением, копирование значения одного регистра в другой посредством сложения с нулем и записи результата и т.п.). - можно решить эту задачу с помощью мультиплексора, управляющим сигналом которого является сигнал сравнения адреса на чтение с нулем;
- либо же можно проинициализировать нулевую ячейку памяти нулем с запретом записи в неё каких-либо значений. В этом случае в ячейке всегда будет ноль, а значит и считываться с нулевого адреса будет только он.
Как и в случае реализации [памяти инструкций](#1-память-инструкций), вы можете решить эту задачу с помощью мультиплексора, управляющим сигналом которого является сигнал сравнения адреса на чтение с нулем. Инициализация ячейки памяти может быть осуществлена (только при проектировании под ПЛИС) с помощью присваивания в блоке `initial`.
Либо же можно проинициализировать нулевую ячейку памяти нулем с запретом записи в неё каких-либо значений. В этом случае в ячейке всегда будет ноль, а значит и считываться с нулевого адреса будет только он.
## Порядок выполнения работы ## Порядок выполнения работы
1. Внимательно ознакомьтесь с заданием. В случае возникновения вопросов, проконсультируйтесь с преподавателем. 1. Внимательно ознакомьтесь с заданием. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Реализуйте память инструкций. Для этого: 2. Добавьте в проект файл [`memory_pkg.sv`](memory_pkg.sv). Этот файл содержит объявление пакета `memory_pkg`, в котором прописаны размеры памяти инструкций и памяти данных (реализуется позднее).
3. Реализуйте память инструкций. Для этого:
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `instr_mem.sv`. 1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `instr_mem.sv`.
2. Опишите в нем модуль памяти инструкций с таким же именем и портами, как указано в задании. 2. Опишите в нем модуль памяти инструкций по предоставленному коду.
1. Сперва необходимо создать память (массив регистров). Как это сделать, сказано в разделе [описание памяти на языке SystemVerilog](#описание-памяти-на-языке-systemverilog). Разрядность ячеек памяти должна быть 32 бита, количество ячеек — 1024.
2. Добавить в `Design Sources` проекта [`файл с содержимым памяти инструкций`](program.mem). Данный файл будет использоваться при вызове системной функции `$readmemh` в описании памяти инструкций.
3. К созданной памяти необходимо подключить выход модуля `read_data_o`. При подключении должен быть использован вход модуля `addr_i`, значение которого должно быть уменьшено в 4 раза (побайтовая адресация).
4. При реализации выхода `read_data_o` помните, что обращаясь к ячейке памяти, вам необходимо использовать `[11:2]` биты адреса.
5. Реализуемый порт на чтение памяти инструкций должен быть **асинхронным**.
3. После описания памяти инструкций, её необходимо проверить с помощью тестового окружения.
1. Тестовое окружение находится [`здесь`](tb_instr_mem.sv).
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_instr_mem`).
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
3. Реализуйте память данных. Для этого:
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `data_mem.sv`.
2. Опишите в нем модуль памяти данных с таким же именем и портами, как указано в задании.
1. Описание модуля будет схожим с описанием модуля памяти инструкций, однако порт чтения в этот раз будет **синхронным** (запись в него будет происходить в блоке `always_ff`). Количество ячеек в памяти данных — 4096. Кроме того, необходимо будет описать логику записи данных в память.
2. Запись в ячейки памяти описывается подобно записи данных в [регистры](../../Basic%20Verilog%20structures/Registers.md), только при этом, происходит доступ к конкретной ячейке памяти с помощью входа `addr_i` (как осуществляется доступ к ячейкам памяти сказано в разделе [описание памяти на языке SystemVerilog](#описание-памяти-на-языке-systemverilog)).
3. Доступ к ячейкам (на запись и чтение) осуществляется по адресу `addr_i[13:2]`.
4. Обратите внимание что работа с памятью должна осуществляться только когда сигнал `mem_req_i == 1`, в противном случае запись не должна производиться, а на шине `read_data_o` должен оставаться результат предыдущего чтения.
3. После описания памяти данных, её необходимо проверить с помощью тестового окружения.
1. Тестовое окружение находится [`здесь`](tb_data_mem.sv).
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_data_mem`).
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
4. Реализуйте регистровый файл. Для этого: 4. Реализуйте регистровый файл. Для этого:
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `rf_riscv.sv`. 1. В `Design Sources` проекта создайте `SystemVerilog`-файл `rf_riscv.sv`.
2. Опишите в нем модуль регистрового файла с таким же именем и портами, как указано в задании. 2. Опишите в нем модуль регистрового файла с таким же именем и портами, как указано в задании.
1. Обратите внимание, что имя памяти (не название модуля, а имя объекта памяти внутри модуля) должно быть `rf_mem`. Такое имя необходимо для корректной работы верификационного окружения. 1. Обратите внимание, что имя памяти (не название модуля, а имя массива регистров внутри модуля) должно быть `rf_mem`. Такое имя необходимо для корректной работы верификационного окружения.
2. Как и у памяти инструкций, порты чтения регистрового файла должны быть **асинхронными**. 2. Как и у памяти инструкций, порты чтения регистрового файла должны быть **асинхронными**.
3. Не забывайте, что у вас 2 порта на чтение и 1 порт на запись, при этом каждый порт не зависит от остальных (в модуле 3 независимых входа адреса). 3. Не забывайте, что у вас 2 порта на чтение и 1 порт на запись, при этом каждый порт не зависит от остальных (в модуле 3 независимых входа адреса).
4. Чтение из нулевого регистра (чтение по адресу 0) всегда должно возвращать нулевое значение. Этого можно добиться двумя путями: 4. Чтение из нулевого регистра (чтение по адресу 0) всегда должно возвращать нулевое значение. Этого можно добиться двумя путями:

View File

@@ -0,0 +1,18 @@
/* -----------------------------------------------------------------------------
* 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 memory_pkg;
localparam INSTR_MEM_SIZE_BYTES = 512;
localparam INSTR_MEM_SIZE_WORDS = INSTR_MEM_SIZE_BYTES / 4;
localparam DATA_MEM_SIZE_BYTES = 512;
localparam DATA_MEM_SIZE_BYTES = DATA_MEM_SIZE_BYTES / 4;
endpackage

View File

@@ -1,110 +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) : Nikita Bulavin
* Email(s) : nekkit6@edu.miet.ru
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------
*/
module tb_data_mem();
parameter ADDR_SIZE = 16384;
parameter TIME_OPERATION = 20;
parameter STEP = 8;
logic CLK;
logic REQ;
logic WE;
logic [31:0] A;
logic [31:0] WD;
logic [31:0] RD;
data_mem DUT (
.clk_i (CLK),
.mem_req_i (REQ),
.write_enable_i (WE ),
.addr_i (A ),
.write_data_i (WD),
.read_data_o (RD)
);
logic [31:0] RDa;
integer i, hash, err_count = 0;
assign A = i;
parameter CLK_FREQ_MHz = 100;
parameter CLK_SEMI_PERIOD= 1e3/CLK_FREQ_MHz/2;
initial CLK <= 0;
always #CLK_SEMI_PERIOD CLK = ~CLK;
initial begin
$timeformat(-9, 2, " ns", 3);
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
REQ = 1;
WE = 0;
@(posedge CLK);
for (i = 0; i < ADDR_SIZE; i = i + STEP) begin
hash = (i+4)*8/15*16/23*42;
WE = 1;
WD = hash;
@(posedge CLK)#3;
end
WE = 0;
@(posedge CLK);
for (i = 0; i < ADDR_SIZE; i = i + STEP) begin
@(posedge CLK)#3;
hash = (i+4)*8/15*16/23*42;
if(RD !== hash) begin
$error("Read data: %0h is unequal written data: %0h at addres: %0h, time: %t", RD, hash, i, $time);
err_count = err_count + 1;
end
end
for (i = 0; i < (ADDR_SIZE+STEP); i = i + 1 + $urandom() % STEP) begin
REQ = |($urandom %10);
WE = 0;
#TIME_OPERATION;
RDa = RD;
WD = $urandom;
#TIME_OPERATION;
WE = $urandom % 2;
#TIME_OPERATION;
if ((!WE && REQ) && RD !== RDa) begin
$error("When reading (write_enable_i = %h), the data %h is overwritten with data %h at address %h, time: %t", WE, RDa, RD, A, $time);
err_count = err_count + 1;
end
#TIME_OPERATION;
end
#TIME_OPERATION;
REQ = 1;
WE = 0;
#TIME_OPERATION;
for (i = 0; i < 4; i = i + 1) begin
if(i==0) begin
repeat(2)@(posedge CLK);
#1; RDa = RD;
end else
if(RD !== RDa) begin
$error("incorrect conversion of the reading address = %h, time: %t", A, $time);
err_count = err_count + 1;
end
#TIME_OPERATION;
end
i = 0; WE = 0; REQ = 1;
@(posedge CLK);
@(negedge CLK);
i = 4;
#1; RDa = RD;
@(posedge CLK); #1;
if (RD == RDa) begin
$error("reading from data memory must be synchronous, time: %t", $time);
err_count = err_count + 1;
end
@(posedge CLK);
$display("Number of errors: %d", err_count);
if( !err_count ) $display("\ndata_mem SUCCESS!!!\n");
$finish();
end
endmodule

View File

@@ -1,75 +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) : Nikita Bulavin
* Email(s) : nekkit6@edu.miet.ru
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------
*/
module tb_instr_mem();
parameter ADDR_SIZE = 4096;
parameter TIME_OPERATION = 10;
parameter STEP = 8;
logic [31:0] addr;
logic [31:0] RD;
logic [31:0] RDref;
instr_mem_ref DUTref(
.addr_i(addr),
.read_data_o(RDref)
);
instr_mem DUT (
.addr_i(addr),
.read_data_o(RD)
);
integer i, err_count = 0;
assign addr = i;
initial begin
$timeformat (-9, 2, "ns");
$display( "\nStart test: \n\n==========================\nCLICK THE BUTTON 'Run All'\n==========================\n"); $stop();
for (i = 0; i < ADDR_SIZE + STEP; i = i + 1 + $urandom() % STEP) begin
#TIME_OPERATION;
if ( RD !== RDref) begin
$display("time = %0t, address %d. Invalid data %h, correct data %h", $time, addr, RD, RDref);
err_count = err_count + 1;
end
end
$display("Number of errors: %d", err_count);
if( !err_count ) $display("\n instr_mem SUCCESS!!!\n");
$finish();
end
endmodule
module instr_mem_ref(
input [31:0] addr_i,
output logic [31:0] read_data_o
);
`define akjsdnnaskjdn $clog2(128)
`define cdyfguvhbjnmk $clog2(`akjsdnnaskjdn)
`define qwenklfsaklasd $clog2(`cdyfguvhbjnmk)
`define asdasdhkjasdsa (34 >> `cdyfguvhbjnmk)
reg [31:0] RAM [0:1023];
initial $readmemh("program.mem", RAM);
always_comb begin
read_data_o['h1f:'h1c]=RAM[{2'b00, addr_i[5'd28^5'o27:2]}][{5{1'b1}}:{3'd7,2'b00}];
read_data_o[42-23-:`asdasdhkjasdsa]=RAM[{2'b00, addr_i[5'h1C-5'd17:2]}][19:{1'b1,4'h0}];
read_data_o[`akjsdnnaskjdn-:`asdasdhkjasdsa]=RAM[{2'b00, addr_i[5'd28^5'o27:2]}][{3{1'b1}}:{1'b1,2'h0}];
read_data_o[42-19-:`asdasdhkjasdsa]=RAM[{2'b00, addr_i[5'h1C-5'd17:2]}][23:{{2{2'b10}},1'b0}];
read_data_o['h1b:'h18]=RAM[{2'b00, addr_i[5'h1C-5'd17:2]}][27:{2'b11,3'b000}];
read_data_o[`akjsdnnaskjdn+`asdasdhkjasdsa:(`akjsdnnaskjdn+`asdasdhkjasdsa)-`cdyfguvhbjnmk]=RAM[{2'b00, addr_i[5'h1C-5'd17:2]}][11:8];
read_data_o[`akjsdnnaskjdn-`asdasdhkjasdsa-:`asdasdhkjasdsa]=RAM[{2'b00, addr_i[5'd28^5'o27:2]}][3:0];
read_data_o[(`akjsdnnaskjdn<<(`asdasdhkjasdsa-`cdyfguvhbjnmk)) + (`asdasdhkjasdsa-`cdyfguvhbjnmk):12 ]=RAM[{2'b00, addr_i[5'h1C-5'd17:2]}][{4{1'b1}}:12];
end
endmodule

View File

@@ -117,7 +117,7 @@ _Таблица 1. Описание портов декодера инструк
- `mem_req_o` — этот сигнал должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти (считать или записать данные); - `mem_req_o` — этот сигнал должен быть выставлен в 1 каждый раз, когда необходимо обратиться к памяти (считать или записать данные);
- `mem_we_o` — этот сигнал должен быть выставлен в 1, если необходимо записать данные в память, (0 при чтении); - `mem_we_o` — этот сигнал должен быть выставлен в 1, если необходимо записать данные в память, (0 при чтении);
- `mem_size_o`— этот сигнал указывает размер порции данных для передачи (возможные значения этого сигнала указаны в _Таблице 2_). Для удобства использования, данные значения определены в виде параметров в пакете `riscv_pkg`. - `mem_size_o`— этот сигнал указывает размер порции данных для передачи (возможные значения этого сигнала указаны в _Таблице 2_). Для удобства использования, данные значения определены в виде параметров в пакете `decoder_pkg`.
|Параметр|Значение `mem_size_o`| Пояснение | |Параметр|Значение `mem_size_o`| Пояснение |
|--------|---------------------|------------------------------| |--------|---------------------|------------------------------|
@@ -218,7 +218,7 @@ _Таблица 4. Описание типов форматов кодирова
В _Таблице 5_ представлены все опкоды реализуемых нами инструкций. Представленные в ней коды операций 5-битные потому, что 2 младших бита полноценного 7-битного кода операции в реализуемых нами инструкциях должны всегда быть равны `11`. Если это не так, то вся инструкция уже запрещенная и не нуждается в дальнейшем декодировании. В _Таблице 5_ представлены все опкоды реализуемых нами инструкций. Представленные в ней коды операций 5-битные потому, что 2 младших бита полноценного 7-битного кода операции в реализуемых нами инструкциях должны всегда быть равны `11`. Если это не так, то вся инструкция уже запрещенная и не нуждается в дальнейшем декодировании.
Для удобства, значения кодов операций определены в виде параметров в пакете riscv_pkg. Для удобства, значения кодов операций определены в виде параметров в пакете `decoder_pkg`.
|Параметр|Opcode| Описание группы операций | Краткая запись | |Параметр|Opcode| Описание группы операций | Краткая запись |
|--------|------|-------------------------------------------------------------------------------------------------------|------------------------------------| |--------|------|-------------------------------------------------------------------------------------------------------|------------------------------------|
@@ -398,7 +398,7 @@ module decoder_riscv (
output logic jalr_o, output logic jalr_o,
output logic mret_o output logic mret_o
); );
import riscv_pkg::*; import decoder_pkg::*;
endmodule endmodule
``` ```
@@ -412,7 +412,7 @@ endmodule
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `decoder_riscv.sv`. 1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `decoder_riscv.sv`.
2. Опишите в нем модуль декодера инструкций с таким же именем и портами, как указано в [задании](#задание). 2. Опишите в нем модуль декодера инструкций с таким же именем и портами, как указано в [задании](#задание).
1. Для удобства дальнейшего описания модуля, рекомендуется сперва создать сигналы `opcode`, `func3`, `func7` и присвоить им соответствующие биты входного сигнала инструкции. 1. Для удобства дальнейшего описания модуля, рекомендуется сперва создать сигналы `opcode`, `func3`, `func7` и присвоить им соответствующие биты входного сигнала инструкции.
2. При описании модуля вы можете воспользоваться параметрами, объявленными **пакетах** `riscv_pkg`, `csr_pkg` и `alu_opcodes_pkg`, описанных в файлах [riscv_pkg.sv](riscv_pkg.sv), [csr_pkg.sv](csr_pkg.sv) и [alu_opcodes_pkg.sv](alu_opcodes_pkg.sv) соответственно. 2. При описании модуля вы можете воспользоваться параметрами, объявленными **пакетах** `decoder_pkg`, `csr_pkg` и `alu_opcodes_pkg`, описанных в файлах [decoder_pkg.sv](decoder_pkg.sv), [csr_pkg.sv](csr_pkg.sv) и [alu_opcodes_pkg.sv](alu_opcodes_pkg.sv) соответственно.
3. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`. 3. Модуль может быть описан множеством способов: каждый выходной сигнал может быть описан через собственную комбинационную логику в отдельном блоке `case`, однако проще всего будет описать все сигналы через вложенные `case` внутри одного блока `always_comb`.
4. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового. 4. Внутри блока `always_comb` до начала блока `case` можно указать базовые значения для всех выходных сигналов. Это не то же самое, что вариант `default` в блоке `case`. Здесь вы можете описать состояния, которые будут использованы чаще всего, и в этом случае, присваивание сигналу будет выполняться только в том месте, где появится инструкция, требующая значение этого сигнала, отличное от базового.
5. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду. 5. Далее вы можете описать базовый блок `case`, где будет определен тип операции по ее коду.

View File

@@ -8,7 +8,7 @@
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details. See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------ * ------------------------------------------------------------------------------
*/ */
package riscv_pkg; package decoder_pkg;
import alu_opcodes_pkg::*; import alu_opcodes_pkg::*;
import csr_pkg::*; import csr_pkg::*;
@@ -54,7 +54,7 @@ package riscv_pkg;
/* /*
Hack that makes nested opcodes be Hack that makes nested opcodes be
visible with just one import of visible with just one import of
riscv_pkg decoder_pkg
*/ */
export alu_opcodes_pkg::ALU_OP_WIDTH; export alu_opcodes_pkg::ALU_OP_WIDTH;

View File

@@ -11,7 +11,7 @@ See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
module tb_decoder_riscv(); module tb_decoder_riscv();
import riscv_pkg::*; import decoder_pkg::*;
typedef class riscv_instr; typedef class riscv_instr;
riscv_instr instr = new(); riscv_instr instr = new();

View File

@@ -0,0 +1,122 @@
# Лабораторная работа 6 "Основная память"
Процессор CYBERcobra 2000 использовал в качестве основного хранилища данных регистровый файл, однако на практике 31-го регистра недостаточно для выполнения сложных программ. Для этих целей используется **основная память**, роль которой в нашей системе будет выполнять **память данных**.
## Цель
Описать память данных, с побайтовой адресацией.
## Материал для подготовки к лабораторной работе
Для успешного выполнения лабораторной работы, вам необходимо использовать навыки, полученные при написании [лабораторной работы №3](../03.%20Register%20file%20and%20memory/) "Регистровый файл и память инструкций";
## Теория
В задании по реализации памяти инструкций [лабораторной работы №3](../03.%20Register%20file%20and%20memory/) байтовая адресация была описана следующим образом:
> Байтовая адресация означает, что процессор способен обращаться к отдельным байтам в памяти (за каждым байтом памяти закреплен свой индивидуальный адрес).
Данное описание было дано не совсем корректным образом, чтобы в третьей лабораторной работе было более четкое понимание задания. В чем заключается некорректность? Процессор должен быть способен не только **обращаться** к отдельным байтам в памяти, но и **обновлять** в памяти любой отдельный байт, а также **считывать** отдельные байты.
Вопрос считывания отдельного байта будет решаться специальным модулем **загрузки и сохранения**, памяти данных при этом будет достаточно возвращать всё слово, содержащее запрашиваемый байт как это было уже делалось для памяти инструкций.
Нас интересует возможность памяти обновлять любой из байт в слове. Подобный функционал часто используется при реализации памяти и в системных интерфейсах, например AXI4 или APB. Для этого используется специальный сигнал, который называется `byte enable`. Разрядность этого сигнала равна числу байт в шине данных (в нашем случае разрядность `byte enable` составляет 4). Вы можете представить этот сигнал, как 4 провода, каждый из которых является сигналом разрешения записи для отдельной памяти с шириной данный в 1 байт.
Давайте разберемся как это будет работать. Допустим, мы хотим записать значение `0xA5` по адресу `0x6`. Поскольку мы работаем с байтовой адресацией, как и при реализации памяти инструкций, пришедший адрес необходимо будет разделить на 4 (см. _рис. 1_). В итоге мы получим указатель на первую 32-битную ячейку памяти (`6 / 4 = 1`). Однако, чтобы пришедшие данные были в итоге записаны не в нулевой байт первого слова (четвертый байт памяти), во второй, мы будем использовать сигнал `byte enable`, второй бит которого будет равен единице.
Чтобы данные остальных байт не были испорчены, при описании памяти на SystemVerilog нужно разделить запись в отдельные байты. Для того, чтобы получить доступ к отдельным диапазонам бит ячейки памяти, после указания индекса ячейки необходимо указать диапазон бит, к которым вы хотите получить доступ. К примеру, чтобы получить доступ к битам с 5-го по 3-ий 18-ой ячейки памяти, необходимо использовать следующую запись:
```SystemVerilog
mem[18][5:3];
```
Учитывайте и то, что комбинации значений бит в сигнале `byte enable` могут быть любыми: `0000`, `0100`, `0110`, `1111` и т.п.
## Задание
Реализовать память данных с поддержкой обновления отдельных байт в выбранной ячейке памяти.
У данного модуля будет шесть входных/выходных сигналов:
- вход тактового синхроимпульса
- вход запроса на работу с памятью
- вход сигнала разрешения записи
- 32-битный вход адреса
- 32-битный вход данных записи
- 32-битный выход данных синхронного чтения
Прототип модуля следующий:
```SystemVerilog
module data_mem
import memory_pkg::DATA_MEM_SIZE_BYTES;
import memory_pkg::DATA_MEM_SIZE_WORDS;
(
input logic clk_i,
input logic mem_req_i,
input logic write_enable_i,
input logic [ 3:0] byte_enable_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
);
```
Как и память инструкций, память данных будет состоять из 32-битных ячеек, количество которых определяется параметром. Как и в памяти инструкций, необходимо использовать только младшие биты адреса в количестве, равном логарифму по основанию 2 от количества ячеек памяти, начиная со второго бита (см. код памяти инструкций из ЛР№3).
Отличие от памяти инструкций будет заключаться в:
- синхронном порте на чтение;
- наличии порта на запись;
- посредством этого порта на запись можно менять отдельные байты ячейки памяти.
Синхронный порт на чтение означает, что выдача данных по предоставленному адресу осуществляется не сразу же, а на следующий такт (см. _рис. 2_). Для этого, перед выходным сигналом ставится отдельный регистр. Таким образом, выдача данных с порта на чтение будет осуществляться не с помощью непрерывного присваивания, а посредством блока `always_ff` (см. практическую часть ЛР№3).
Также в памяти появилось три управляющих сигнала:
- `mem_req_i`,
- `write_enable_i`,
- `byte_enable_i`
и один статусный:
- `ready_o`.
Сигнал `mem_req_i` является сигналом запроса на работу с памятью. Без этого сигнала память не должна выполнять операции чтения/записи. Как сделать так, чтобы не происходило чтение без запроса? Например, не обновлять значение, считанное во время предыдущей операции чтения.
Сигнал `write_enable_i` является сигналом разрешения записи. Этот сигнал определяет, является ли пришедший запрос в память **запросом на запись**, либо же **запросом на чтение**.
Если `mem_req_i == 1` и `write_enable_i == 0`, то происходит запрос на чтение из памяти. В этом случае, необходимо записать в выходной регистр `read_data_o` значение из ячейки, на которую указывает `addr_i`. Во всех других случаях чтение из памяти не производится (`read_data_o` сохраняет предыдущее значение).
![../../.pic/Labs/lab_06_main_memory/fig_02.wavedrom.svg](../../.pic/Labs/lab_06_main_memory/fig_02.wavedrom.svg)
_Рисунок 2. Операции запросов на чтение._
Если `mem_req_i == 1` и `write_enable_i == 1`, то происходит запрос на запись в память. В этом случае, необходимо записать значение `write_data_i` в ячейку по, на которую указывает `addr_i`. Во всех других случаях (любой из сигналов `mem_req_i`, `write_enable_i` равен нулю), запись в память не производится. Запись необходимо производить только в те байты указанной ячейки, которым соответствуют биты сигнала `byte_enable_i`, равные единице.
![../../.pic/Labs/lab_06_main_memory/fig_03.wavedrom.svg](../../.pic/Labs/lab_06_main_memory/fig_03.wavedrom.svg)
_Рисунок 3. Операции запросов на запись._
Перед выходом `ready_o` должен стоять регистр, в который каждый такт записывается значение `mem_req_i` (см. _рис. 2_). Такая простая логика этого сигнала обеспечивается тем, что любой запрос обрабатывается гарантированно за 1 такт. В реальности, обращение в память может занимать сотни тактов процессора, причем их число бывает недетерминированным (нельзя заранее предсказать сколько тактов займет очередной запрос в память). Именно поэтому стандартные интерфейсы обычно используют такие сигналы как `ready` или `valid`, позволяющие синхронизировать разные блоки системы.
## Порядок выполнения работы
1. Внимательно ознакомьтесь с заданием. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Реализуйте память данных. Для этого:
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `data_mem.sv`.
2. Опишите в нем модуль памяти данных с таким же именем и портами, как указано в задании.
1. Описание модуля будет схожим с описанием модуля памяти инструкций, однако порт чтения в этот раз будет **синхронным** (запись в него будет происходить в блоке `always_ff`). Количество ячеек памяти данных определяется параметром `DATA_MEM_SIZE_WORDS`, определенным в `memory_pkg`. Кроме того, необходимо будет описать логику записи данных в память.
2. Запись в ячейки памяти описывается подобно записи данных в [регистры](../../Basic%20Verilog%20structures/Registers.md), только при этом, происходит доступ к конкретной ячейке памяти с помощью входа `addr_i`.
3. Перед тем как обратиться к ячейке памяти, значение с `addr_i` необходимо преобразовать по аналогии с памятью инструкций.
4. Обратите внимание что работа с памятью должна осуществляться только когда сигнал `mem_req_i == 1`. В противном случае запись не должна производиться, а на шине `read_data_o` должен оставаться результат предыдущего чтения.
5. При этом запись должна вестись только в те байты выбранной ячейки памяти, которым соответствуют биты сигнала `byte_enable_i`, выставленные в `1`.
6. У памяти есть дополнительный выход `ready_o`, перед которым необходимо реализовать регистр, в который каждый такт записывается значение `mem_req_i`.
3. После описания памяти данных, её необходимо проверить с помощью тестового окружения.
1. Тестовое окружение находится [`здесь`](tb_data_mem.sv).
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_data_mem`).
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**

View File

@@ -0,0 +1,18 @@
/* -----------------------------------------------------------------------------
* 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 memory_pkg;
localparam INSTR_MEM_SIZE_BYTES = 512;
localparam INSTR_MEM_SIZE_WORDS = INSTR_MEM_SIZE_BYTES / 4;
localparam DATA_MEM_SIZE_BYTES = 512;
localparam DATA_MEM_SIZE_BYTES = DATA_MEM_SIZE_BYTES / 4;
endpackage

View File

@@ -4,7 +4,7 @@
## Цель ## Цель
Описать на языке **SystemVerilog** процессор с архитектурой **RISC-V**, реализовав его тракт данных, используя разработанные ранее блоки, и подключив к нему устройство управления. Итогом текущей лабораторной работы станет процессор RISC-V, который пока что сможет обрабатывать лишь слова (то есть БЕЗ инструкций, связанных с байтами и полусловами: `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`). Описать на языке **SystemVerilog** процессор с архитектурой **RISC-V**, реализовав его тракт данных, используя разработанные ранее блоки, и подключив к нему устройство управления. Итогом текущей лабораторной работы станет процессор RISC-V, который пока что сможет работать с памятью данных лишь посредством 32-битных слов (то есть БЕЗ инструкций, связанных с байтами и полусловами: `lh`, `lhu`, `lb`, `lbu`, `sh`, `sb`).
## Ход работы ## Ход работы
@@ -40,7 +40,7 @@ module riscv_core (
endmodule endmodule
``` ```
![../../.pic/Labs/lab_06_dp/fig_01.drawio.svg](../../.pic/Labs/lab_06_dp/fig_01.drawio.svg) ![../../.pic/Labs/lab_07_dp/fig_01.drawio.svg](../../.pic/Labs/lab_07_dp/fig_01.drawio.svg)
_Рисунок 1. Микроархитектура ядра процессора RISC-V._ _Рисунок 1. Микроархитектура ядра процессора RISC-V._
@@ -69,7 +69,7 @@ module riscv_unit(
endmodule endmodule
``` ```
![../../.pic/Labs/lab_06_dp/fig_02.drawio.svg](../../.pic/Labs/lab_06_dp/fig_02.drawio.svg) ![../../.pic/Labs/lab_07_dp/fig_02.drawio.svg](../../.pic/Labs/lab_07_dp/fig_02.drawio.svg)
_Рисунок 2. Микроархитектура процессора._ _Рисунок 2. Микроархитектура процессора._
@@ -90,7 +90,7 @@ _Рисунок 2. Микроархитектура процессора._
Предположим, мы описываем содержимое памяти инструкций и у нас есть очередная инструкция `0xDEADBEEF` (`jal`). Если она должна быть размещена в памяти, начиная с адреса `4`, то байт `EF` должен находиться по 4-ому адресу, байт `BE` — по пятому и т.п. Допустим, мы разделили байты инструкций символами переноса строк (и что строки в файле нумеруются с нуля). Тогда соответствие между строкой, байтом инструкции и адресом в памяти, где этот байт должен быть расположен будет следующим: Предположим, мы описываем содержимое памяти инструкций и у нас есть очередная инструкция `0xDEADBEEF` (`jal`). Если она должна быть размещена в памяти, начиная с адреса `4`, то байт `EF` должен находиться по 4-ому адресу, байт `BE` — по пятому и т.п. Допустим, мы разделили байты инструкций символами переноса строк (и что строки в файле нумеруются с нуля). Тогда соответствие между строкой, байтом инструкции и адресом в памяти, где этот байт должен быть расположен будет следующим:
![../../.pic/Labs/lab_06_dp/fig_02.excel.png](../../.pic/Labs/lab_06_dp/fig_02.excel.png) ![../../.pic/Labs/lab_07_dp/fig_02.excel.png](../../.pic/Labs/lab_07_dp/fig_02.excel.png)
Если после разделения инструкции переносами, мы не изменим порядок байт в файле, при считывании файла САПР будет инициализировать память наоборот: ячейка с младшим адресом будет проинициализирована строкой с младшим номером. Если оставить все как есть, процессор считает из памяти инструкцию `0xEFBEADDE` (вместо jal получаем нелегальную инструкцию, т.к. младшие 2 бита не равны 1). Если после разделения инструкции переносами, мы не изменим порядок байт в файле, при считывании файла САПР будет инициализировать память наоборот: ячейка с младшим адресом будет проинициализирована строкой с младшим номером. Если оставить все как есть, процессор считает из памяти инструкцию `0xEFBEADDE` (вместо jal получаем нелегальную инструкцию, т.к. младшие 2 бита не равны 1).
@@ -189,12 +189,12 @@ _Рисунок 2. Микроархитектура процессора._
5. **Во время симуляции убедитесь, что в логе есть сообщение о завершении теста!** 5. **Во время симуляции убедитесь, что в логе есть сообщение о завершении теста!**
6. Вполне возможно, что после первого запуска вы столкнетесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/Debug%20manual.md) эти ошибки на временной диаграмме и исправить их в вашем модуле. 6. Вполне возможно, что после первого запуска вы столкнетесь с сообщениями о множестве ошибок. Вам необходимо [исследовать](../../Vivado%20Basics/Debug%20manual.md) эти ошибки на временной диаграмме и исправить их в вашем модуле.
4. Проверьте работоспособность вашей цифровой схемы в ПЛИС. Для этого: 4. Проверьте работоспособность вашей цифровой схемы в ПЛИС. Для этого:
1. Добавьте файлы из папки [`board files`](https://github.com/MPSU/APS/tree/master/Labs/06.%20Datapath/board%20files) в проект. 1. Добавьте файлы из папки [`board files`](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files) в проект.
1. Файл [nexys_riscv_unit.sv](https://github.com/MPSU/APS/tree/master/Labs/06.%20Datapath/board%20files/nexys_riscv_unit.sv) необходимо добавить в `Design Sources` проекта. 1. Файл [nexys_riscv_unit.sv](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files/nexys_riscv_unit.sv) необходимо добавить в `Design Sources` проекта.
2. Файл [nexys_a7_100t.xdc](https://github.com/MPSU/APS/tree/master/Labs/06.%20Datapath/board%20files/nexys_a7_100t.xdc) необходимо добавить в `Constraints` проекта. В случае, если вы уже добавляли одноименный файл в рамках предыдущих лабораторных работ, его содержимое необходимо заменить содержимым нового файла. 2. Файл [nexys_a7_100t.xdc](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files/nexys_a7_100t.xdc) необходимо добавить в `Constraints` проекта. В случае, если вы уже добавляли одноименный файл в рамках предыдущих лабораторных работ, его содержимое необходимо заменить содержимым нового файла.
2. Выберите `nexys_riscv_unit` в качестве модуля верхнего уровня (`top-level`). 2. Выберите `nexys_riscv_unit` в качестве модуля верхнего уровня (`top-level`).
3. Выполните генерацию битстрима и сконфигурируйте ПЛИС. Для этого воспользуйтесь [следующей инструкцией](../../Vivado%20Basics/How%20to%20program%20an%20fpga%20board.md). 3. Выполните генерацию битстрима и сконфигурируйте ПЛИС. Для этого воспользуйтесь [следующей инструкцией](../../Vivado%20Basics/How%20to%20program%20an%20fpga%20board.md).
4. Описание логики работы модуля верхнего уровня и связи периферии ПЛИС с реализованным модулем находится в папке [`board files`](https://github.com/MPSU/APS/tree/master/Labs/06.%20Datapath/board%20files). 4. Описание логики работы модуля верхнего уровня и связи периферии ПЛИС с реализованным модулем находится в папке [`board files`](https://github.com/MPSU/APS/tree/master/Labs/07.%20Datapath/board%20files).
--- ---

View File

@@ -33,7 +33,7 @@ _Рисунок 1. Структурная схема модуля `nexys_riscv_u
Прототип определяет тип операции по младшим 7 битам инструкции. Прототип определяет тип операции по младшим 7 битам инструкции.
Если тип операции является легальным в рамках процессорного устройства, реализуемого на лабораторных работах, то отображается соответствующий опкод. Опкоды описаны в [riscv_pkg.sv](../../05.%20Main%20decoder/riscv_pkg.sv). Если определенный прототипом тип операции является нелегальным, то на семисегментных индикаторах отображается `ILL` (от **ill**egal). Если тип операции является легальным в рамках процессорного устройства, реализуемого на лабораторных работах, то отображается соответствующий опкод. Опкоды описаны в [decoder_pkg.sv](../../05.%20Main%20decoder/decoder_pkg.sv). Если определенный прототипом тип операции является нелегальным, то на семисегментных индикаторах отображается `ILL` (от **ill**egal).
Соответствие операции ее отображению на семисегментных индикаторах представлено на _рис. 2_: Соответствие операции ее отображению на семисегментных индикаторах представлено на _рис. 2_:

View File

@@ -116,7 +116,7 @@ module nexys_riscv_unit(
assign opcode = instr[6:0]; assign opcode = instr[6:0];
Char op_chars[0:3]; Char op_chars[0:3];
import riscv_pkg::*; import decoder_pkg::*;
always_comb begin always_comb begin
op_chars = '{4{CH_SPACE}}; op_chars = '{4{CH_SPACE}};

View File

@@ -8,7 +8,7 @@
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details. See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------ * ------------------------------------------------------------------------------
*/ */
package riscv_pkg; package decoder_pkg;
import alu_opcodes_pkg::*; import alu_opcodes_pkg::*;
export alu_opcodes_pkg::*; export alu_opcodes_pkg::*;

View File

@@ -0,0 +1,18 @@
/* -----------------------------------------------------------------------------
* 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 memory_pkg;
localparam INSTR_MEM_SIZE_BYTES = 512;
localparam INSTR_MEM_SIZE_WORDS = INSTR_MEM_SIZE_BYTES / 4;
localparam DATA_MEM_SIZE_BYTES = 512;
localparam DATA_MEM_SIZE_BYTES = DATA_MEM_SIZE_BYTES / 4;
endpackage

View File

@@ -1,80 +0,0 @@
# Лабораторная работа 7 "Внешняя память"
Ранее вы реализовали процессор архитектуры RISC-V с оговоркой что он не поддерживает инструкции `sh`, `sb`, `lh`, `lhu`, `lb`, `lbu`. Данное ограничение имеет две причины:
- вам нужен модуль, который будет определенным образом взаимодействовать с памятью в зависимости от конкретной инструкции загрузки или сохранения (**модуль загрузки и сохранения**, о котором будет рассказано на [следующей](../08.%20Load-store%20unit) лабораторной работе);
- вам нужна внешняя память, которая будет способна делать то, что от нее запросит модуль загрузки и сохранения.
Реализации такой памяти и будет посвящена эта лабораторная работа.
## Цель
Описать внешнюю память данных, полностью поддерживающую побайтовую адресацию.
## Допуск к лабораторной работе
Для успешного выполнения лабораторной работы, вам необходимо:
- выполнить [лабораторную работу №3](../03.%20Register%20file%20and%20memory/) "Регистровый файл и внешняя память";
- освоить оператор конкатенации ([Concatenation.md](../../Basic%20Verilog%20structures/Concatenation.md)).
## Теория
В задании по реализации памяти инструкций [лабораторной работы №3](../03.%20Register%20file%20and%20memory/) байтовая адресация была описана следующим образом:
> Байтовая адресация означает, что процессор способен обращаться к отдельным байтам в памяти (за каждым байтом памяти закреплен свой индивидуальный адрес).
Данное описание было дано не совсем корректным образом, чтобы в третьей лабораторной работе было более четкое понимание задания. В чем заключается некорректность? Процессор должен быть способен не только **обращаться** к отдельным байтам в памяти, но и **обновлять** в памяти любой отдельный байт, а также **считывать** отдельные байты.
Вопрос считывания отдельного байта будет решаться модулем загрузки и сохранения, данному модулю будет достаточно возвращать все слово, содержащее запрашиваемый байт как это было сделано в рамках лабораторной работы №3.
Нас интересует возможность памяти обновлять любой из байт в слове. Для этого используется специальный сигнал, который называется `byte_enable_i`. Разрядность этого сигнала равна числу байт в ячейке памяти (в данном случае разрядность `byte_enable_i` составляет 4). Вы можете представить `byte_enable_i`, как 4 провода, каждый из которых является сигналом разрешения записи для 8-ми D-триггеров, формирующих соответствующий этому проводу байт.
При этом не стоит забывать о том, что записью управляет еще и сигнал `write_enable_i`, определяющий режим работы памяти: запись или чтение.
Таким образом, для каждого байта `addr_i / 4`-ой ячейки памяти:
**если** `write_enable_i` равен единице **и** соответствующий этому байту бит сигнала `byte_enable_i`, то в данный байт памяти записывается соответствующий байт сигнала `write_data_i`.
При этом значение `byte_enable_i` может быть любым: если `byte_enable_i == 4'b1001` (при `write_enable == 1'b1`), то данные должны быть записаны в старший и младший байты `addr_i / 4`-ой ячейки памяти.
## Задание
Реализовать память данных с поддержкой обновления отдельных байт в выбранной ячейке памяти. Прототип модуля следующий (обратите внимание что название модуля отличается от названия памяти данных из третьей лабораторной работы — так мы сможем различать их):
```SystemVerilog
module ext_mem(
input logic clk_i,
input logic mem_req_i,
input logic write_enable_i,
input logic [ 3:0] byte_enable_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
);
```
Как и память данных из лабораторной работы №3, память данных в данной лабораторной состоит из 4096-и 32-разрядных ячеек и обладает портом синхронного чтения, обновляющим данные только по запросу на чтение (`mem_req_i & !write_enable_i`).
Иными словами, логика реализации порта на чтение повторяет логику памяти данных лабораторной работы №3 (можно скопировать эту логику).
Если `mem_req_i == 1` и `write_enable_i == 1` (т.е. если происходит запрос на запись в память), то необходимо обновить данные в тех байтах `addr_i / 4`-ой ячейки памяти, которые соответствуют единичным битам сигнала `byte_enable_i`.
Сигнал `ready_o` тождественно равен единице.
## Порядок выполнения работы
1. Внимательно ознакомьтесь с заданием. В случае возникновения вопросов, проконсультируйтесь с преподавателем.
2. Реализуйте память данных. Для этого:
1. В `Design Sources` проекта создайте `SystemVerilog`-файл `ext_mem.sv`.
2. Опишите в нем модуль памяти данных с таким же именем и портами, как указано в задании.
1. Данный модуль будет очень похож на память данных из лабораторной работы №3 (в частности, логика порта на чтение будет в точности повторять логику той памяти данных).
2. Отличие заключается в двух новых сигналах `ready_o` и `byte_enable_i`.
1. Сигнал `ready_o` тождественно равен единице.
2. Сигнал `byte_enable_i` используется в качестве сигнала, разрешающего запись (в случае операции записи) в конкретный байт ячейки памяти.
3. После описания памяти данных, её необходимо проверить с помощью тестового окружения.
1. Тестовое окружение находится [`здесь`](tb_ext_mem.sv).
2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_ext_mem`).
4. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**

View File

@@ -9,7 +9,7 @@ See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------ * ------------------------------------------------------------------------------
*/ */
module tb_lsu(); module tb_lsu();
import riscv_pkg::*; import decoder_pkg::*;
logic clk_i ; logic clk_i ;
logic rst_i ; logic rst_i ;
logic core_req_i ; logic core_req_i ;
@@ -227,7 +227,7 @@ module riscv_lsu_ref(
input logic mem_ready_i input logic mem_ready_i
); );
import riscv_pkg::*; import decoder_pkg::*;
logic enable; logic enable;
logic [32:0] cursed_numbers; logic [32:0] cursed_numbers;

View File

@@ -9,7 +9,7 @@ See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------ * ------------------------------------------------------------------------------
*/ */
module ext_mem( module data_mem(
input logic clk_i, input logic clk_i,
input logic mem_req_i, input logic mem_req_i,
input logic write_enable_i, input logic write_enable_i,

View File

@@ -27,7 +27,7 @@ module riscv_lsu(
input logic mem_ready_i input logic mem_ready_i
); );
import riscv_pkg::*; import decoder_pkg::*;
logic enable; logic enable;
logic [32:0] cursed_numbers; logic [32:0] cursed_numbers;

View File

@@ -15,8 +15,8 @@
- [3. Регистровый файл и внешняя память (RF)](#3-регистровый-файл-и-внешняя-память-rf) - [3. Регистровый файл и внешняя память (RF)](#3-регистровый-файл-и-внешняя-память-rf)
- [4. Простейшее программируемое устройство (PPD)](#4-простейшее-программируемое-устройство-ppd) - [4. Простейшее программируемое устройство (PPD)](#4-простейшее-программируемое-устройство-ppd)
- [5. Основной дешифратор команд (MD)](#5-основной-дешифратор-команд-md) - [5. Основной дешифратор команд (MD)](#5-основной-дешифратор-команд-md)
- [6. Тракт данных (DP)](#6-тракт-данных-dp) - [6. Основная память](#6-основная-память)
- [7. Внешняя память](#7-внешняя-память) - [7. Тракт данных (DP)](#7-тракт-данных-dp)
- [8. Блог загрузки и сохранения данных (LSU)](#8-блог-загрузки-и-сохранения-данных-lsu) - [8. Блог загрузки и сохранения данных (LSU)](#8-блог-загрузки-и-сохранения-данных-lsu)
- [9 Интеграция LSU](#9-интеграция-lsu) - [9 Интеграция LSU](#9-интеграция-lsu)
- [10. Подсистема прерывания (IC)](#10-подсистема-прерывания-ic) - [10. Подсистема прерывания (IC)](#10-подсистема-прерывания-ic)
@@ -53,7 +53,7 @@
4. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device)) 4. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device))
5. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder)) 5. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder))
6. 6.
1. Тракт данных ([06. Datapath](06.%20Datapath)) 1. Тракт данных ([07. Datapath](07.%20Datapath))
2. Интеграция блока загрузки и сохранения ([09. LSU Integration](09.%20LSU%20Integration)) 2. Интеграция блока загрузки и сохранения ([09. LSU Integration](09.%20LSU%20Integration))
3. Интеграция подсистемы прерываний ([11. Interrupt Integration](11.%20Interrupt%20integration)) 3. Интеграция подсистемы прерываний ([11. Interrupt Integration](11.%20Interrupt%20integration))
7. Периферийные устройства ([13. Peripheral units](13.%20Peripheral%20units)) 7. Периферийные устройства ([13. Peripheral units](13.%20Peripheral%20units))
@@ -66,7 +66,7 @@
1. Память ([03. Register file and memory](03.%20Register%20file%20and%20memory)), 1. Память ([03. Register file and memory](03.%20Register%20file%20and%20memory)),
2. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device)) 2. Простейшее программируемое устройство ([04. Primitive programmable device](04.%20Primitive%20programmable%20device))
3. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder)) 3. Основной дешифратор ([05. Main decoder](05.%20Main%20decoder))
4. Тракт данных ([06. Datapath](06.%20Datapath)) 4. Тракт данных ([07. Datapath](07.%20Datapath))
5. 5.
1. Модуль загрузки и сохранения ([08. Load-store unit](08.%20Load-store%20unit)) 1. Модуль загрузки и сохранения ([08. Load-store unit](08.%20Load-store%20unit))
2. Интеграция блока загрузки и сохранения ([09. LSU Integration](09.%20LSU%20Integration)) 2. Интеграция блока загрузки и сохранения ([09. LSU Integration](09.%20LSU%20Integration))
@@ -131,17 +131,17 @@ https://github.com/MPSU/APS/assets/17159587/4daac01f-dc9a-4ec8-8d3f-c5dc1ef97119
![../.pic/Labs/lab_05_md.drawio.svg](../.pic/Labs/lab_05_md.drawio.svg) ![../.pic/Labs/lab_05_md.drawio.svg](../.pic/Labs/lab_05_md.drawio.svg)
Пятая лабораторная посвящена разработке устройства управления основному дешифратору команд. Функция основного дешифратора — получать на вход коды выполняемых операций и преобразовывать их в управляющие сигналы для всех блоков процессора (АЛУ, память, регистровый файл, мультиплексоры). Работа требует внимательности в реализации, а ее результат проверяется заранее подготовленными автоматическими тестами. Пятая лабораторная посвящена разработке устройства управления основному дешифратору команд. Функция основного дешифратора — получать на вход коды выполняемых операций и преобразовывать их в управляющие сигналы для всех блоков процессора (АЛУ, память, регистровый файл, мультиплексоры). Работа требует внимательности в реализации, а ее результат проверяется заранее подготовленными автоматическими тестами.
## 6. Тракт данных (DP) ## 6. Основная память
![../.pic/Labs/lab_06_datapath.drawio.svg](../.pic/Labs/lab_06_datapath.drawio.svg)
Разработанные блоки объединяются, образуя тракт данных, управляемый основным дешифратором команд. Результатом шестой лабораторной работы является однотактный процессор, с архитектурой RISC-V, поддерживающий стандартный набор целочисленных инструкций RV32I. В качестве проверки на процессоре запускаются программы, заранее написанные на языке ассемблера RISC-V. Сравнивается результат полученный на симуляторе и на разработанном процессоре.
## 7. Внешняя память
Недостатком реализации процессора из предыдущей лабораторной работы была его неспособность выполнять операции `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Отчасти это связано с ограничением реализованной ранее памяти (в этой памяти не было возможности обновить отдельный байт в ячейке памяти). Недостатком реализации процессора из предыдущей лабораторной работы была его неспособность выполнять операции `LB`, `LBU`, `SB`, `LH`, `LHU`, `SH`. Отчасти это связано с ограничением реализованной ранее памяти (в этой памяти не было возможности обновить отдельный байт в ячейке памяти).
Данная вспомогательная лабораторная работа позволяет реализовать память без этого ограничения. Данная вспомогательная лабораторная работа позволяет реализовать память без этого ограничения.
## 7. Тракт данных (DP)
![../.pic/Labs/lab_07_datapath.drawio.svg](../.pic/Labs/lab_07_datapath.drawio.svg)
Разработанные блоки объединяются, образуя тракт данных, управляемый основным дешифратором команд. Результатом шестой лабораторной работы является однотактный процессор, с архитектурой RISC-V, поддерживающий стандартный набор целочисленных инструкций RV32I. В качестве проверки на процессоре запускаются программы, заранее написанные на языке ассемблера RISC-V. Сравнивается результат полученный на симуляторе и на разработанном процессоре.
## 8. Блог загрузки и сохранения данных (LSU) ## 8. Блог загрузки и сохранения данных (LSU)
![../.pic/Labs/lab_08_lsu.drawio.svg](../.pic/Labs/lab_08_lsu.drawio.svg) ![../.pic/Labs/lab_08_lsu.drawio.svg](../.pic/Labs/lab_08_lsu.drawio.svg)