mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
* СП. Обновление предисловия * СП. Обновление введения * СП. Обновление лаб * СП. Обновление доп материалов * СП. Введение * СП. Введение * СП. ЛР№4, 15 * СП. Базовые конструкции Verilog * Update Implementation steps.md * СП. ЛР 4,5,7,8,14 * СП. ЛР№8 * Синхронизация правок * СП. Финал * Исправление ссылки на рисунок * Обновление схемы * Синхронизация правок * Добавление белого фона .drawio-изображениям * ЛР2. Исправление нумерации рисунка
237 lines
16 KiB
Markdown
237 lines
16 KiB
Markdown
# Описание регистров в SystemVerilog
|
||
|
||
Перед тем, как описывать память, необходимо научиться описывать отдельные регистры. **Регистр** — устройство для записи, хранения и считывания n-разрядных двоичных данных и выполнения других операций над ними [1, стр. 32]. В современной электронике, регистр чаще всего строится на D-триггерах. В лабораторной работе по АЛУ уже вскользь упоминалось, что как для описания проводов, так и для описания регистров, используется тип `logic`.
|
||
|
||
```Verilog
|
||
logic reg_name;
|
||
```
|
||
|
||

|
||
|
||
У регистра может быть несколько входов и один выход. Основных входов, без которых не может существовать регистр два: вход данных и вход тактирующего синхроимпульса. На рисунке они обозначены как `D` и `clk`. Опциональный вход сигнала сброса (`rst`) позволяет обнулять содержимое регистра вне зависимости от входных данных и может работать как с тактовым синхроимпульсом (синхронный сброс), так и без него (асинхронный сброс).
|
||
|
||
Помимо прочего у регистра также может быть входной сигнал разрешения записи (`enable`), который определяет будут ли записаны данные с входного сигнала данных в регистр или нет, опциональный вход установки (`set`), позволяющий принудительно выставить значение регистра в единицу.
|
||
|
||
Выход у регистра один. На рисунке выше он обозначен как `Q`.
|
||
|
||
Важно понимать, что названия приведенных портов не являются чем-то высеченным на камне, они просто описывают функциональное назначение. В процессе описания работы регистра вы будете оперировать только над именем регистра, и сигналами, которые подводите к нему.
|
||
|
||
Поскольку все сигналы в цифровой схеме передаются по цепям, удобно представлять, что к выходу регистра всегда неявно подключен провод, с именем, совпадающим с именем регистра, поэтому вы можете использовать имя регистра в дальнейшей цифровой логике:
|
||
|
||

|
||
|
||
Итак, мы добавили регистр на холст схемы, но как соединить его с какой-то логикой? Предположим, у нас есть сигнал тактового синхроимпульса и данные, которые мы хотим записать:
|
||
|
||

|
||
|
||
Данной схеме соответствует код:
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic data,
|
||
output logic reg_data
|
||
);
|
||
|
||
logic reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Очевидно, мы хотим подключить сигнал `clk` ко входу тактирующего сигнала регистра, вход `data` ко входу данных, а выход регистра к выходу `reg_data`:
|
||
|
||

|
||
|
||
Запись в регистр возможна только по фронту тактирующего синхроимпульса. **Фронт** — это переход сигнала из нуля в единицу (**положительный фронт**), либо из единицы в ноль (**отрицательный фронт**).
|
||
|
||
Описание регистра, а также указание фронта и тактирующего сигнала происходит в конструкции `always_ff`:
|
||
|
||
```Verilog
|
||
always_ff @(posedge clk)
|
||
```
|
||
|
||
Далее, внутри данной конструкции необходимо указать, что происходит с содержимым регистра. В нашем случае, происходит запись с входного сигнала `data`
|
||
|
||
```Verilog
|
||
always_ff @(posedge clk) begin
|
||
reg_name <= data;
|
||
end
|
||
```
|
||
|
||
> [!IMPORTANT]
|
||
> Обратите внимание на оператор `<=`. В данном случае, это не знак "меньше либо равно", а оператор **неблокирующего присваивания**. Существует оператор **блокирующего присваивания** (`=`), который может поменять способ построения схемы для такого же выражения справа от оператора. Хоть это и плохая практика в обучении, но пока вам надо просто запомнить: **при описании записи в регистр всегда используйте оператор неблокирующего присваивания `<=`**. Подробнее о рассказано в документе "[О различиях между блокирующими и неблокирующими присваиваниями](./Assignments.md)".
|
||
|
||
Помимо прочего, нам необходимо связать выход схемы с выходом регистра. Это можно сделать уже известным вам оператором **непрерывного присваивания** `assign`.
|
||
|
||
Таким образом, итоговый код описания данной схемы примет вид:
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic data,
|
||
output logic reg_data
|
||
);
|
||
|
||
logic reg_name;
|
||
|
||
always_ff @(posedge clk) begin
|
||
reg_name <= data;
|
||
end
|
||
|
||
assign reg_data = reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Предположим, мы хотим добавить управление записью в регистр через сигналы `enable` и `reset`. Это, например, можно сделать следующим образом:
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic data,
|
||
input logic reset,
|
||
input logic enable,
|
||
output logic reg_data
|
||
);
|
||
|
||
logic reg_name;
|
||
|
||
always_ff @(posedge clk) begin
|
||
if(reset) begin
|
||
reg_name <= 1'b0;
|
||
end
|
||
else if(enable) begin
|
||
reg_name <= data;
|
||
end
|
||
end
|
||
|
||
assign reg_data = reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Обратите внимание на очередность условий. В первую очередь, мы проверяем условие **сброса**, и только после этого условие **разрешения на запись**.
|
||
Если сперва проверить разрешение на запись, а затем в блоке `else` описать логику сброса, то регистр не будет сбрасываться в случае, если `enable` будет равен `1` (запись в регистр будет приоритетней его сброса). Если сброс описать не в блоке `else`, а в отдельном блоке `if`, то может возникнуть неопределенное поведение: нельзя однозначно сказать, что запишется в регистр, если одновременно придут сигналы `reset` и `enable`. Поэтому при наличии сигнала сброса, остальная логика по записи в регистр должна размещаться в блоке `else`.
|
||
|
||
Кроме того, САПР-ы смотрят на паттерн описания элемента схемы, и когда распознают его, реализуют элемент так как задумывал разработчик. Поэтому при описании регистра всегда сперва описывается сигнал сброса (если он используется) и только затем в блоке `else` описывается вся остальная часть логики записи.
|
||
|
||
Итоговая схема регистра со сбросом и сигналом разрешения записи:
|
||
|
||

|
||
|
||
Помимо прочего есть еще одно важное правило, которое необходимо знать при описании регистра:
|
||
|
||
**Присваивание регистру может выполняться только в одном блоке `always`**
|
||
|
||
Даже если вдруг САПР не выдаст сразу сообщение об ошибке, в конечном итоге, на этапе синтеза схемы она рано или поздно появится в виде сообщения связанного с **"multiple drivers"**.
|
||
|
||
В блоке присваивания регистру можно описывать и комбинационную логику, стоящую перед ним, например схему:
|
||
|
||

|
||
|
||
можно описать как
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic A,
|
||
input logic B,
|
||
input logic reset,
|
||
input logic enable,
|
||
output logic reg_data
|
||
);
|
||
|
||
logic reg_name;
|
||
|
||
always_ff @(posedge clk) begin
|
||
if(reset) begin
|
||
reg_name <= 1'b0;
|
||
end
|
||
else if(enable) begin
|
||
reg_name <= A & B;
|
||
end
|
||
end
|
||
|
||
assign reg_data = reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Однако это всего лишь упрощение. Если вы умеете описывать регистр с подключением к нему всего одного провода на входе данных, вы все равно сможете описать эту схему:
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic A,
|
||
input logic B,
|
||
input logic reset,
|
||
input logic enable,
|
||
output logic reg_data
|
||
);
|
||
|
||
logic reg_name; // Обратите внимание, что несмотря на то, что
|
||
logic ab; // и reg_name и ab объявлены типом logic,
|
||
// ab станет проводом, а reg_name - регистром
|
||
// (из-за непрерывного присваивания на ab, и блока
|
||
// always_ff для reg_name)
|
||
|
||
assign ab = A & B;
|
||
|
||
always_ff @(posedge clk) begin
|
||
if(reset) begin
|
||
reg_name <= 1'b0;
|
||
end
|
||
else if(enable) begin
|
||
reg_name <= ab;
|
||
end
|
||
end
|
||
|
||
assign reg_data = reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Поэтому так важно разобраться в базовом способе описания регистра.
|
||
|
||
Более того, с точки зрения синтезатора данное описание проще для синтеза, т.к. ему не разделять из одного `always` блока комбинационную и синхронные части.
|
||
|
||
Вообще говоря, регистр в общем смысле этого слова представляет собой многоразрядную конструкцию (в рассмотренном ранее примере, 1-битный регистр мог представлять из себя простой D-триггер).
|
||
Создание многоразрядного регистра мало отличается от создания многоразрядного провода, а описание логики записи в многоразрядный регистр ничем не отличается от логики записи в одноразрядный регистр:
|
||
|
||
```Verilog
|
||
module reg_example(
|
||
input logic clk,
|
||
input logic [7:0] data,
|
||
output logic [7:0] reg_data
|
||
);
|
||
|
||
logic [7:0] reg_name;
|
||
|
||
always_ff @(posedge clk) begin
|
||
reg_name <= data;
|
||
end
|
||
|
||
assign reg_data = reg_name;
|
||
|
||
endmodule
|
||
```
|
||
|
||
## Итоги главы
|
||
|
||
1. [Регистр](https://ru.wikipedia.org/wiki/Регистр_(цифровая_техника)) — это базовая ячейка памяти, позволяющая хранить состояние, пока на схему подается питание.
|
||
2. Для объявления регистра используется тип `logic`, при необходимости после типа указывается разрядность будущего регистра.
|
||
3. Для описания логики записи в регистр используется блок `always_ff`, в круглых скобках которого указывается тактирующий сигнал и фронт, по которому будет вестись запись, а также (в случае асинхронного сброса), сигнал сброса.
|
||
4. Регистр может иметь различные управляющие сигналы: установки/сброса/разрешения на запись. Логика этих управляющих сигналов является частью логики записи в этот регистр и так же описывается в блоке `always_ff`.
|
||
5. При описании логики записи в регистр, необходимо пользоваться оператором **неблокирующего присваивания** `<=`.
|
||
6. Нельзя описывать логику записи в регистр более чем в одном блоке `always` (иными словами, операция присваивания для каждого регистра может находиться только в одном блоке always).
|
||
|
||
## Проверь себя
|
||
|
||
Как, по-вашему, описать на языке SystemVerilog схему, приведённую ниже?
|
||
|
||

|
||
|
||
## Список источников
|
||
|
||
1. Ш. Габриелян, Е. Вахтина / Электротехника и электроника. Методические рекомендации. — Ставрополь: Аргус, 2013
|