Переход на использование mem-файлов (#38)

Раньше в вивадо была проблема с использованием mem-файлов.
Они нормально моделировались, но при этом не использовались в
синтезе, пока им не выставишь тип "Memory Initialization File".

Однако с выставлением этого типа файл нельзя было открыть в
вивадо (и даже поменять тип этого файла).

Поэтому, все файлы в курсе носили расширение .txt, чтобы их можно
было легко открыть в любом другом редакторе, а в вивадо
выставлялся злополучный тип "Memory Initialization File" вручную.

Сейчас же, судя по всему, вивадо нормально выполняет синтез и
с "Memory File" тоже, а значит нет нужды в каком-либо изменении
типов, лишь бы файлы носили расширение .mem. При этом файлы этого
типа можно открывать и редактировать в редакторе вивадо.
This commit is contained in:
Andrei Solodovnikov
2024-01-17 14:31:49 +03:00
committed by Andrei Solodovnikov
parent 6b2f0c2bab
commit 1bb2fdd323
18 changed files with 29 additions and 62 deletions

View File

@@ -153,7 +153,9 @@ module mem16_20 ( // создать блок с именем
endmodule
```
В случае реализации ПЗУ нет необходимости в описании входов для записи. В таком случае описание памяти занимает всего пару строк. Чтобы инициализировать такую память (то есть поместить в нее начальные значения, чтобы было что из нее читать), требуемое содержимое нужно добавить к прошивке, вместе с которой данные попадут в ПЛИС. Для этого в проект добавляется текстовый файл с содержимым памяти (более подробно об этом в [`Как добавить файл с содержимым памяти в проект`](../../Vivado%20Basics/How%20to%20add%20a%20mem-file.md)). Для того, чтобы отметить данный файл в качестве инициализирующего память, необходимо использовать одну из двух системных функций: `$readmemh` и `$readmemb`. `$readmemh` используется для файлов, описывающих содержимое памяти 16-ричными цифрами, в то время как `$readmemb` используется для файлов, описывающих содержимое памяти двоичными цифрами. Любую из этих системных функций необходимо поместить внутрь блока `initial`. У этих системных функций есть два обязательных аргумента:
В случае реализации ПЗУ нет необходимости в описании входов для записи, поэтому описание памяти занимает всего пару строк. Чтобы проинициализировать такую память (то есть поместить в нее начальные значения, которые можно было бы из нее читать), требуемое содержимое нужно добавить к прошивке, вместе с которой данные попадут в ПЛИС. Для этого в проект добавляется текстовый файл формата `.mem` с содержимым памяти. Для того, чтобы отметить данный файл в качестве инициализирующего память, можно использовать системную функцию `$readmemh`.
У данной функции есть два обязательных аргумента:
- имя инициализирующего файла
- имя инициализируемой памяти
@@ -163,13 +165,13 @@ endmodule
- стартовый адрес, начиная с которого память будет проинициализирована данным файлом (по-умолчанию равен нулю)
- конечный адрес, на котором инициализация закончится (даже если в файле были ещё какие-то данные).
Пример полного вызова одной из этих системных функций выглядит так:
Пример полного вызова выглядит так:
`$readmemb("<data file name>",<memory name>,<start address>,<end address>);`
`$readmemh("<data file name>",<memory name>,<start address>,<end address>);`
Однако на деле обычно используются только обязательные аргументы:
`$readmemb("<data file name>",<memory name>);`
`$readmemh("<data file name>",<memory name>);`
Пример описанной выше памяти:
@@ -184,8 +186,8 @@ module rom16_8 (
logic [7:0] ROM [0:15]; // создать память с 16-ю 8-битными ячейками
initial begin
$readmemh("mem.txt", ROM); // поместить в память RAM содержимое
end // файла mem.txt
$readmemh("rom_data.mem", ROM); // поместить в память RAM содержимое
end // файла rom_data.mem
assign read_data1 = R0M[addr1]; // реализация первого порта на чтение
@@ -194,7 +196,7 @@ module rom16_8 (
endmodule
```
Содержимое файла `mem.txt`, к примеру может быть таким (каждая строка соответствует значению отдельной ячейки памяти, начиная со стартового адреса):
Содержимое файла `rom_data.mem`, к примеру может быть таким (каждая строка соответствует значению отдельной ячейки памяти, начиная со стартового адреса):
```hex
FA
@@ -204,6 +206,8 @@ endmodule
A7
```
**Для того, чтобы при сборке модуля не было проблем с путями, по которым будет искаться данный файл, обычно его необходимо добавить в проект. В случае Vivado, чтобы тот распознал этот файл как инициализирующий память, необходимо чтобы у этого файла было расширение `.mem`.**
## Задание по реализации памяти
Необходимо описать на языке SystemVerilog три вида памяти:
@@ -307,7 +311,7 @@ mоdulе rf_r𝚒sсv(
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemVerilog`-файл `instr_mem.sv`.
2. Опишите в нем модуль памяти инструкций с таким же именем и портами, как указано в задании.
1. Сперва необходимо создать память (массив регистров). Как это сделать, сказано в разделе [описание памяти на языке SystemVerilog](#описание-памяти-на-языке-systemverilog). Разрядность ячеек памяти должна быть 32 бита, количество ячеек — 1024.
2. Добавить в проект [`файл с содержимым памяти инструкций`](program.txt)([`как добавить файл, инициализирующий память`](../../Vivado%20Basics/How%20to%20add%20a%20mem-file.md)). Данный файл будет использоваться при вызове системной функции `$readmemh` в описании памяти инструкций.
2. Добавить в проект [`файл с содержимым памяти инструкций`](program.mem). Данный файл будет использоваться при вызове системной функции `$readmemh` в описании памяти инструкций.
3. К созданной памяти необходимо подключить выход модуля `read_data_o`. При подключении должен быть использован вход модуля `addr_i`, значение которого должно быть уменьшено в 4 раза (побайтовая адресация).
4. При реализации выхода `read_data_o` помните, что обращаясь к ячейке памяти, вам необходимо использовать `[11:2]` биты адреса.
3. После описания памяти инструкций, её необходимо проверить с помощью тестового окружения.

View File

@@ -60,7 +60,7 @@ module instr_mem_ref(
`define asdasdhkjasdsa (34 >> `cdyfguvhbjnmk)
reg [31:0] RAM [0:1023];
initial $readmemh("program.txt", RAM);
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}];

View File

@@ -293,7 +293,7 @@
Разработать процессор `CYBERcobra`, объединив ранее разработанные модули:
- Память инструкций (проинициализированную в двоичном формате файлом [`example.txt`](example.txt))
- Память инструкций (проинициализированную в двоичном формате файлом [`example.mem`](example.mem))
- Регистровый файл
- Арифметико-логическое устройство
- 32-битный сумматор
@@ -331,13 +331,13 @@ endmodule
5. Мультиплексор, выбирающий источник записи в регистровый файл.
3. После описания модуля, его необходимо проверить с помощью [`тестового окружения`](../../Basic%20Verilog%20structures/Testbench.md).
1. Тестовое окружение находится [`здесь`](tb_cybercobra.sv).
2. Программа, которой необходимо проинициализировать память инструкций находится [`здесь`](example.txt). Алгоритм работы программы приведен в разделе [`Финальный обзор`](#финальный-обзор).
2. Программа, которой необходимо проинициализировать память инструкций находится [`здесь`](example.mem). Алгоритм работы программы приведен в разделе [`Финальный обзор`](#финальный-обзор).
3. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md).
4. Перед запуском симуляции убедитесь, что выбран правильный модуль верхнего уровня.
5. **Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!**
6. В этот раз, в конце не будет сообщения о том, работает ли ваше устройство или в нем есть ошибки. Вы должны самостоятельно проверить работу модуля, перенеся его внутренние сигналы на временную диаграмму, и [проверив](../../Vivado%20Basics/Debug%20manual.md) логику их работы.
4. Добавьте в проект модуль верхнего уровня ([nexys_cybercobra_demo.sv](board%20files/nexys_cybercobra_demo.sv)), соединяющий процессор с периферией в ПЛИС. Описание работы модуля находится [здесь](board%20files)
5. Замените содержимое файла, инициализирующего память инструкций новой программой, которая размещена [`здесь`](board%20files/demo.txt).
5. Замените содержимое файла, инициализирующего память инструкций новой программой, которая размещена [`здесь`](board%20files/demo.mem).
6. Убедитесь, что у файла, инициализирующего память инструкций выставлен тип `Memory Initialization Files`, а не `Memory File`.
7. Подключите к проекту файл ограничений ([nexys_a7_100t.xdc](board%20files/nexys_a7_100t.xdc)), если тот еще не был подключен, либо замените его содержимое данными из файла к этой лабораторной работе.
8. Проверьте работу процессора в ПЛИС.

View File

@@ -2,7 +2,7 @@
Если вы не понимаете, что лежит в этой папке, или если надо вспомнить, как прошить ПЛИС, можно воспользоваться [`этой инструкцией`](../../../Vivado%20Basics/Program%20nexys%20a7.md)
Файл [`nexys_cybercobra_demo.sv`](nexys_cybercobra_demo.sv), который нужно запускать с [`демонстрационным файлом инструкций`](demo.txt), является демонстрацией возможностей кобры, реализующий лишь декодирование выходных значений в формат для отображения на семисегментных индикаторах, а вся логика работы реализована инструкциями в текстовом файле.
Файл [`nexys_cybercobra_demo.sv`](nexys_cybercobra_demo.sv), который нужно запускать с [`демонстрационным файлом инструкций`](demo.mem), является демонстрацией возможностей кобры, реализующий лишь декодирование выходных значений в формат для отображения на семисегментных индикаторах, а вся логика работы реализована инструкциями в текстовом файле.
Сначала выводится приветствие `≡ALOHA≡`, меняя положение восьми правых переключателей, последовательно нажимая на кнопку `BTND` (на рисунке выделена синим цветом), можно включать или выключать `один` из выбранных сегментов. Кнопка `CPU RESET` (на рисунке выделена красным цветом) возвращает все исходное состояние. Попробуйте погасить все слово, а потом снова его зажечь.

View File

@@ -154,10 +154,10 @@ cyberconverter принимает до двух аргументов. Поряд
cyberconverter --help
```
2. Преобразование программы, записанной в файле `test.txt`, с записью результата в файл `program.txt`:
2. Преобразование программы, записанной в файле `test.txt`, с записью результата в файл `program.mem`:
```bash
cyberconverter test.txt program.txt
cyberconverter test.txt program.mem
```
3. Если не указан второй аргумент, результат будет записан в файл:
@@ -169,7 +169,7 @@ cyberconverter принимает до двух аргументов. Поряд
Результат будет записан в файл `test_converted.txt`.
4. Если программа будет запущена без аргументов, то исходным файлом будет считаться файл `program.txt`.
4. Если программа будет запущена без аргументов, то исходным файлом будет считаться файл `program.mem`.
В случае отсутствия исходного файла, наличия неподдерживаемых символов или неверной длины инструкции будет выведено сообщение об ошибке.

View File

@@ -15,11 +15,11 @@ void print_help(const std::string program_name)
cout << "in hex-format.\n\n";
cout << "If output file omitted, the <input_file_base>_converted.<input_file_ext>\n";
cout << "will be produced.\n\n";
cout << "If input file omitted, program.txt will be used.\n\n";
cout << "If input file omitted, program.mem will be used.\n\n";
cout << "Example:\n\n";
cout << program_name << " open \"program.txt\" and produce \"program_converted.txt\"\n";
cout << program_name << " open \"program.mem\" and produce \"program_converted.mem\"\n";
cout << program_name << " test.txt open \"test.txt\" and produce \"test_converted.txt\"\n";
cout << program_name << " test.txt myname.dat open \"test.txt\" and produce \"myname.dat\"\n";
cout << program_name << " test.txt myname.mem open \"test.txt\" and produce \"myname.mem\"\n";
}
@@ -39,8 +39,8 @@ int main(int argc, char ** argv)
switch (argc)
{
case 1:
ifname = "program.txt";
ofname = "program_converted.txt";
ifname = "program.mem";
ofname = "program_converted.mem";
break;
case 2:
if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))

View File

@@ -2,4 +2,4 @@
Если вы не понимаете, что лежит в этой папке, или если надо вспомнить, как прошить ПЛИС, можно воспользоваться [`этой инструкцией`](../../../Vivado%20Basics/Program%20nexys%20a7.md)
Файл [`nexys_riscv_unit.sv`](nexys_riscv_unit.sv), который нужно запускать с [`демонстрационным файлом инструкций`](../program.txt), является демонстрацией работы вашего ядра, каждое нажатие на BTND формирует тактовый импульс, впоследствии пошагово переходя по инструкциям, которые в свою очередь отображаются на семисегментных индикаторах.
Файл [`nexys_riscv_unit.sv`](nexys_riscv_unit.sv), который нужно запускать с [`демонстрационным файлом инструкций`](../program.mem), является демонстрацией работы вашего ядра, каждое нажатие на BTND формирует тактовый импульс, впоследствии пошагово переходя по инструкциям, которые в свою очередь отображаются на семисегментных индикаторах.

View File

@@ -21,4 +21,4 @@
1. Обратите внимание, что что в модуле `riscv_core` появились новые входные и выходные сигналы: `irq_req_i` и `irq_ret_o`. Эти сигналы должны быть использованы при подключении `riscv_core` в модуле `riscv_unit`.
1. Ко входу `irq_req_i` должен быть подключен провод `irq_req`, другой конец которого пока не будет ни к чему подключен (в следующей лабораторной это будет изменено).
2. К выходу `irq_ret_o` необходимо подключить провод `irq_ret`, который также пока не будет использован.
2. После интеграции модулей, проверьте процессорную систему с помощью [программы](irq_program.txt), текст которой [был представлен](../10.%20Interrupt%20subsystem#пример-обработки-перехвата) в ЛР10 с помощью предоставленного [тестбенча](tb_irq_unit.sv).
2. После интеграции модулей, проверьте процессорную систему с помощью [программы](irq_program.mem), текст которой [был представлен](../10.%20Interrupt%20subsystem#пример-обработки-перехвата) в ЛР10 с помощью предоставленного [тестбенча](tb_irq_unit.sv).

View File

@@ -9,7 +9,7 @@ module instr_mem(
`define asdasdhkjasdsa (34 >> `cdyfguvhbjnmk)
reg [31:0] RAM [0:1023];
initial $readmemh("program.txt", RAM);
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}];