Переход на использование 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

1
.github/SUMMARY.md vendored
View File

@@ -49,7 +49,6 @@
- [Руководство по поиску ошибок](Vivado%20Basics/Debug%20manual.md)
- [Руководство по прошивке ПЛИС](Vivado%20Basics/Program%20nexys%20a7.md)
- [Заголовочные файлы в Verilog](Vivado%20Basics/Verilog%20Header.md)
- [Как добавить файл, инициализирующий память](Vivado%20Basics/How%20to%20add%20a%20mem-file.md)
# Остальное

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}];

View File

@@ -1,35 +0,0 @@
# Как добавить файл с содержимым памяти в проект
Представим ситуацию. Сел, значит, ты делать процессор на ПЛИС, делал-делал, и наконец сделал, прошил его в ПЛИС, но как запустить на нем программу?
Благо, в ПЛИС предусмотрена возможность инициализировать (то есть задавать начальные значения) блочную память начальными значениями. Хранит она их рядом со своей прошивкой. При подаче питания на ПЛИС она конфигурирует себя из энергонезависимой памяти, и инициализирует блочную память.
Программу (то, чем нужно проинициализировать память) нужно подсунуть до этапа сборки прошивки в виде текстового файла, в котором данные представлены ASCII-символами в двоичном или 16-ричном виде. В требуемом verilog-модуле необходимо указать какую память в модуле и из какого файла нужно инициализировать. Вот пример объявления памяти RAM с восьмью 32-битными ячейками:
``` verilog
reg [31:0] RAM [0:7];
initial $readmemh("my_program.txt", RAM);
```
В данном примере в память, именуемую RAM, помещаются значения из файла с названием my_program.txt. Файл не обязательно должен покрывать всю область памяти, количество строк может быть любым между 0 и максимумом. Но длинна строк должна строго соответствовать разрядности ячеек инициализируемой памяти. `$readmemh` для 16-ричнного представления чисел в файле, `$readmemb` для файлов с двоичной записью.
Кроме озвученного нужно сообщить САПРу, что файл памяти относится к проекту, и что он файл памяти, а не какой-то другой. Для этого надо сделать два действия:
1. Добавить файл в проект в качестве `Design-файла`
2. Указать в его свойствах тип файла `Memory Initialization Files`
Файл в проект добавляется точно так же, как при создании Verilog-файла.
![../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_1.png](../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_1.png)
Дальше надо добавить файл с содержимым памяти. Обрати внимание, чтобы его можно было выбрать, нужно поставить в фильтрах `All Files`, как на картинке снизу.
![../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_2.png](../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_2.png)
После добавления файла в проект он отобразится в окне `Sources`, в папке `Text`. Чтобы поменять тип файла на нужный, необходимо выделить этот файл, нажав на него, после чего в окне `Source File Properties`, во вкладке `Properties` найти свойство `FILE_TYPE` и заменить его на `Memory Initialization Files`. Описанное отмечено на картинке далее.
![../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_3.png](../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_3.png)
Если после изменений файл переместился в папку `Memory Initialization Files` в окне `Sources`, значит все сделано правильно!
![../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_4.png](../.pic/Vivado%20Basics/How%20to%20add%20a%20mem-file/how_to_mem_4.png)

View File

@@ -14,5 +14,4 @@
7. [Разобраться с ошибками, при попытке открыть схему / запустить симуляцию](Elaboration%20failed.md)
8. [Находить и исправлять ошибки дизайна, найденные тестовым окружением](Debug%20manual.md)
9. [Добавить заголовочный файл в проект Vivado](Verilog%20Header.md)
10. [Добавить файл инициализации памяти в проект Vivado](How%20to%20add%20a%20mem-file.md)
11. [Понять как работают этапы элаборации/синтеза/имплементации](Implementation%20steps.md)
10. [Понять как работают этапы элаборации/синтеза/имплементации](Implementation%20steps.md)