Похожие презентации:
05_Введение_в_RTL_разработку_Verilog_HDL
1. Введение в RTL разработку Язык описания аппаратуры Verilog
Данила Хайдуков2 марта 2026
2. Содержание лекции
Введение в RTL разработку
Языки описания аппаратуры
Разработка на Verilog
• Комбинационные схемы
• Последовательностные схемы
Тестирование на Verilog
• Testbench
Приложения
Алгоритмы
Языки программирования
Операционные системы
Архитектура
Микроархитектура
Register-Transfer Level (RTL)
Circuits
Схемотехника
Физика
2
3. Введение в RTL разработку
34. Введение в RTL разработку
• Все начинается с идеи продукта, продиктованнойбизнесом
• На основе анализа рынка и работы с заказчиками,
формируются требования к продукту
• На основе требований формируется архитектурная
спецификация
• Архитектурные решения проверяются с помощью
моделирования -> архитектура уточняется
• Зафиксированные архитектурные решения
имплементируются в RTL и верифицируются
• Имплементированный RTL передается на этап
физического проектирования
• Прототип готового чипа тестируется
4
5. Разработка цифровых схем
Архитектурная спецификацияRTL-имплементация и верификация
Синтез RTL-кода в нетлист
Построение предварительного floorplan
Вставка тестовых элементов для отладки
на уровне готового чипа в нетлист
6. Построение конечного floorplan
7. Размещение логических ячеек с учетом
геометрических размеров
8. Построение тактового дерева
9. Сведение таймингов между транзисторами
10. Финальная верификация
11. Генерация финального файла с
топологией микросхемы
1.
2.
3.
4.
5.
5
6. RTL разработка
АрхитектураАрхитектурная спецификация
RTL разработка
ТЗ
Уточнение
Микроархитектура
На основе полученного архитектурного ТЗ
составляется микроархитектурная
спецификация разрабатываемого блока
По микроархитектруной спецификации
имплементируется RTL на языке описания
аппаратуры
Имплементированный RTL тестируется и
отлаживается с помощью testbench
Завершенный RTL блок передается в работу
командам топологии и верификации
Микроархитектурная
спецификация
RTL разработка
Тестирование и отладка
RTL-кода
RTL и спецификация
баги
Верификация
Верификационное
окружение и тесты
RTL
Критические пути
Топология
Физический дизайн
и верификация
6
7. Языки описания аппаратуры
78. Языки описания аппаратуры
• VHDL – был создан во времена, когда проектировали на уровне транзисторов/логических ячеек вручную, как средство документации
• ГОСТ 1995 года
• EST (expression of a system task) – первое название Verilog
• Не был стандартизован, развивался и менялся с 1984 по 1990 гг.
• Verilog-95 – общедоступная стандартизация языка
• Стандарт IEEE Std 1364-1995, в 1995 году
• Verilog-2001 – модификация, устраняющая недостатки предыдущего Verilog-95
• Стандарт IEEE Std 1364-2001, в 2001 году
• Verilog-2005 – очередная модификация, уточняющая предыдущую и добавляющая
новые возможности
• Стандарт IEEE Std 1364-2005, в 2005 году
• SystemVerilog – расширение Verilog-2005, включающее множество новых средств,
возможностей для проектирования, моделирования и проектирования
• Стандарт IEEE Std 1800-2009, в 2009 году
8
9. Разработка на Verilog
910. Модуль
• Модуль – основная единица иерархии в Verilogmodule <name> (<ports>);
<module logic>
endmodule
• У модуля могут быть входные и выходные порты
Вход: input <name>
Выход: output <name>
• Непрерывное присваивание логического
выражения сигналу или шине
assign <signal or bus name> = <expression>;
Для комбинационной логики
x
!x
x
!x
0
1
1
0
module invertor (
input signal_i,
output signal_o
);
assign signal_o = !signal_i;
endmodule
10
11. Шины
Шина – массив из нескольких сигналов
[<border1>:<border2>] <bus name>
x
х & y
y
При объявлении шины, как правило, используют
border2 = 0, а border1 = WIDTH-1
• 8-битное число: input [7:0] number
Для присвоения части шины границы могут быть
произвольными
• assign result[4:2] = number[6:4];
x
y
х & y
0
0
0
0
1
0
1
0
0
1
1
1
module and (
input [7:0] first_i,
input [7:0] second_i,
output [7:0] result_o
);
assign result_o = first_i & second_i;
endmodule
11
12. Параметры
xy
х | y
0
0
0
0
1
1
1
0
1
1
1
module or #(
parameter WIDTH = 8
)(
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
output [WIDTH-1:0] result_o
);
1
x
х|y
y
• Параметры позволяют делать модули
универсальными
В зависимости от требований, модуль
может быть использован с различными
наборами параметров
assign result_o = first_i | second_i;
endmodule
12
13. Логические и побитовые операции
• На практике простейшие логические операции невыделяют в отдельный модуль
• Используют в дизайне непосредственно
• Логические операции для работы с однобитными
сигналами
• Результат однобитный
• !signal; signal1 && signal2; signal1 || signal2; signal1 ^
signal2
Операция
Логическая
Побитовая
НЕ
!
~
И
&&
&
ИЛИ
||
|
ИСКЛ. ИЛИ
^
^
• Побитовые операции для работы с шинами
• Битность результата совпадает с битностью
операндов
• ~bus, bus1 & bus2; bus1 | bus2; bus1 ^ bus2
• Операции редукции для работы с одной шиной
• Результат однобитный
• &bus[3:0] равносильно bus[3] && bus[2] && bus[1] &&
bus[0]
• |bus[3:0] равносильно bus[3] || bus[2] || bus[1] || bus[0]
• ^bus[3:0] равносильно bus[3] ^ bus[2] ^ bus[1] ^ bus[0]
13
14. Тернарный оператор
firstresult
Тернарный оператор
• Тернарный оператор равносилен
if-else-блоку
• <expr> ? <true_case> : <false_case>
• Если <expr> истинно, то <true_case>,
иначе – <false_case>
• Незаменим для оператора assign
• Использование if-else в операторе
assign невозможно
second
module mux2 #(
parameter WIDTH = 8
)(
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
input
select_i,
output [WIDTH-1:0] result_o
);
select
select
result
0
first
1
second
assign result_o = select_i ? second_i : first_i;
endmodule
14
15. Always-блок. Оператор case
module mux4 #(parameter WIDTH = 8
)(
input [WIDTH-1:0] d0_i,
input [WIDTH-1:0] d1_i,
Для работы с логикой используется always-блок
input [WIDTH-1:0] d2_i,
• always @(*) begin
Внутренняя переменная input [WIDTH-1:0] d3_i,
• <combinational logic>
модуля
input [1:0]
sel_i,
• end
output [WIDTH-1:0] res_o
@(*) означает чувствительность к изменению любого
);
сигнала
reg [WIDTH-1:0] res_internal;
Внутренняя переменная типа reg для always-блока
always @(*) begin
• Блокирующее присваивание (=) для комбинационной
res_internal = {WIDTH{1'b0}};
логики
case (sel_i)
Значение по умолчанию
2'b00: res_internal = d0_i;
Значение, в зависимости
2'b01: res_internal = d1_i;
от sel_i
2'b10: res_internal = d2_i;
2'b11: res_internal = d3_i;
endcase
end
assign res_o = res_internal;
endmodule
Always-блок. Оператор case
15
16. Универсальный мультиплексор
• Универсализация мультиплексора путем введенияпараметра NUMBER – количество входов
Логарифм по основанию 2
• На основе NUMBER вычисляется ширина шины sel_i
SEL_WIDTH = $clog2(NUMBER)
Двумерный массив сигналов
Индексация массива по внешней
размерности (NUMBER)
module mux_any #(
parameter WIDTH
= 8,
parameter NUMBER
= 4,
parameter SEL_WIDTH = $clog2(NUMBER)
)(
input [NUMBER-1:0][WIDTH-1:0] d_i,
input [SEL_WIDTH-1:0]
sel_i,
output
[WIDTH-1:0] res_o
);
assign res_o = d_i[sel_i];
endmodule
16
17. Универсальный дешифратор (декодер)
• Дешифратор: в зависимости от значенияopcode_i, соответствующий бит в result_o
становится равным 1
2^OP_WIDTH
module decoder #(
parameter OP_WIDTH = 2,
parameter DEC_WIDTH = 2**OP_WIDTH
)(
input
[OP_WIDTH-1:0] opcode_i,
output [DEC_WIDTH-1:0] result_o
);
reg [DEC_WIDTH-1:0] result_internal;
Значение по умолчанию
Присваивание 1 биту, в
зависимости от значения opcode
always @(*) begin
result_internal = {DEC_WIDTH{1'b0}};
result_internal[opcode_i] = 1'b1;
end
assign result_o = result_internal;
endmodule
17
18. Сумматор
Перенос с предыдущегоразряда
Сумматор
module adder (
input first_i,
input second_i,
input carry_i,
output sum_o,
• Однобитный сумматор, учитывающий
output carry_o
перенос с предыдущего разряда
• Внутренний провод (wire) для присваивания );
промежуточного результата
• Поскольку не используется always-блок, а
assign, то используем не reg, а wire
Промежуточный результат
Перенос на следующий
разряд
wire sum_operands;
assign sum_operands = first_i ^ second_i;
assign sum_o = sum_operands ^ carry_i;
assign carry_o = (first_i & second_i) |
(sum_operands & carry_i);
endmodule
x
y
c
+
sum
carry
18
19. 8-битный сумматор
Инстанс однобитногосумматора для 0 разряда
8-битный сумматор
adder adder_0_inst (
.first_i (first_i[0] ),
.second_i(second_i[0]),
.carry_i (carry[0]
),
.sum_o
(sum_o[0]
),
.carry_o (carry[1]
)
);
• 8-битный сумматор, построенный из 8
однобитных
• 8 инстансов модуля однобитного
сумматора
Внутренняя переменная
для переноса
Перенос для 0 разряда
равен 0
module adder_8bit #(
parameter WIDTH = 8
)(
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
output [WIDTH-1:0] sum_o
);
wire [WIDTH-1:0] carry;
assign carry[0] = 1'b0;
…
adder adder_7_inst (
.first_i (first_i[7] ),
.second_i(second_i[7]),
.carry_i (carry[7]
),
.sum_o
(sum_o[7]
),
.carry_o (/*void*/
)
);
endmodule
Для 7 разряда выход
переноса не нужен
19
20. Generate-блок
• Для логики, которую можнообобщить для разных
параметров, используют
generate-блок
• generate
• <logic with for-loop or if-else>
• endgenerate
module multibit_adder #(
parameter WIDTH = 8
)(
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
Ширина: WIDTH + 1
output [WIDTH-1:0] sum_o
);
Перенос для 0 разряда
wire [WIDTH:0] carry;
равен 0
assign carry[0] = 1'b0;
genvar j;
Переменная для generate
generate
for (j = 0; j < WIDTH; j = j + 1) begin : ADDER_GEN
adder adder_inst (
Инстансы для WIDTH
.first_i (first_i[j] ),
разрядов
.second_i(second_i[j]),
.carry_i (carry[j]
),
.sum_o
(sum_o[j]
),
.carry_o (carry[j+1] )
);
end
carry подключен ко всем,
endgenerate
хотя для старшего разряда
20
endmodule
он не нужен
21. Арифметические операции
ОперацияОбозначение
Комментарий
Сложение
x + y
Знаковые или беззнаковые
операнды
Умножение
x * y
Знаковые или беззнаковые
операнды
Сравнение
x < y; x == y;
x != y; x <= y;
x > y; x >= y
Результат 1 бит
x << y
Логический сдвиг x влево
на y бит
Сдвиг влево
module signed_adder #(
parameter WIDTH = 8
) (
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
output [WIDTH-1:0] sum_o
);
wire signed [WIDTH-1:0] first;
wire signed [WIDTH-1:0] second;
Сдвиг вправо
x >> y
Логический сдвиг x вправо
на y бит
Арифметический
сдвиг вправо
x >>> y
Арифметический (знаковый)
сдвиг x вправо на y бит
assign first = first_i;
assign second = second_i;
assign sum_o = first + second;
endmodule
21
22. Пример ALU
В зависимости от opcode, ALU
способно выполнить различные
операции над 2 (или 1) операндами
module alu #(
parameter WIDTH = 8
)(
input [WIDTH-1:0] first_i,
input [WIDTH-1:0] second_i,
input [1:0]
opcode_i,
output [WIDTH-1:0] result_o
);
reg [WIDTH-1:0] result;
opcode
result
2’b00
A + B
2’b01
NOT B
2’b10
A << B
always @(*) begin
result = {WIDTH{1'b0}};
case (opcode_i)
2'b00: result = first_i + second_i;
2'b01: result = ~second_i;
2'b10: result = first_i << second_i;
2'b11: result = first_i & second_i;
endcase
end
2’b11
A AND B
assign result_o = result;
Значение по умолчанию
Различные операции, в
зависимости от значения opcode
endmodule
22
23. Последовательностная логика
• Для последовательностной логики в модульдобавляется входной порт clk тактовой частоты
• Для последовательностной логики используется
always-блок
• always @(posedge clk_i) begin
• <sequential logic>
• end
• @(posedge clk_i) означает чувствительность к
положительному фронту сигнала clk_i
• Неблокирующее присваивание (<=) для
последовательностной логики
D
clk
Qt-1
Qt
D
0
Q
Q
D
1
Q
Q
D
1→0
Q
Q
D
0→1
Q
D
Q
D
clk
module dff #(
parameter WIDTH = 8
)(
input
clk_i,
input [WIDTH-1:0] d_i,
output [WIDTH-1:0] q_o
);
reg [WIDTH-1:0] d_ff;
always @(posedge clk_i) begin
d_ff <= d_i;
end
assign q_o = d_ff;
endmodule
23
24. Сброс
Сигнал сброса• Для последовательностной логики в модуль
часто добавляется сигнал сброса
• Синхронный с тактовым сигналом clk_i
сброс с активным высоким уровнем
(логическая 1)
• Распространен в RTL разработке на ПЛИС
Синхронный сброс с
активным высоким уровнем
Значение по сбросу
module dff_rst #(
parameter WIDTH = 8
)(
input
clk_i,
input
rst_i,
input [WIDTH-1:0] d_i,
output [WIDTH-1:0] q_o
);
reg [WIDTH-1:0] d_ff;
always @(posedge clk_i) begin
if (rst_i) begin
d_ff <= {WIDTH{1'b0}};
end else begin
d_ff <= d_i;
end
end
assign q_o = d_ff;
24
endmodule
25. Сброс
module dff_arstn #(• Для последовательностной логики в
модуль часто добавляется сигнал сброса parameter WIDTH = 8
• Асинхронный с тактовым сигналом clk_i )(
input
clk_i,
сброс с активным низким уровнем
input
arstn_i,
(логический 0)
input [WIDTH-1:0] d_i,
• Распространен в RTL разработке чипов
• always-блок чувствителен не только к
положительному фронту тактового
сигнала clk_i, но и к отрицательному
фронту сигнала сброса arstn_i
Значение по сбросу
Сигнал сброса
output [WIDTH-1:0] q_o
);
reg [WIDTH-1:0] d_ff;
always @(posedge clk_i or negedge arstn_i) begin
if (!arstn_i) begin
d_ff <= {WIDTH{1'b0}};
end else begin
d_ff <= d_i;
Асинхронный сброс с
end
активным низким уровнем
end
assign q_o = d_ff;
25
endmodule
26. Счетчик
• Модуль счетчика с входным сигналомразрешения счета cnt_en_i и выходным
сигналом переполнения ovf_o
Регистр счетчика шириной
CNT_WIDTH + 1, где старший
бит для учета переполнения
Обнуление счетчика при сбросе
Если сигнал cnt_en_i == 1, то
делаем инкремент
{ovf_o, cnt_o} – конкатенация
cnt_o и ovf_o: в результате
ширина CNT_WIDTH + 1
module cnt_en #(
parameter CNT_WIDTH = 4
)(
input
clk_i,
input
arstn_i,
input
cnt_en_i,
output [CNT_WIDTH-1:0] cnt_o,
output
ovf_o
);
reg [CNT_WIDTH:0] cnt_ff;
always @(posedge clk_i or negedge arstn_i) begin
if (!arstn_i) begin
cnt_ff <= {CNT_WIDTH+1{1'b0}};
end else begin
cnt_ff <= cnt_en_i ? cnt_ff + 1 : cnt_ff;
end
end
assign {ovf_o, cnt_o} = cnt_ff;
26
endmodule
27. Массив памяти
• Массив памяти для данных ширинойDATA_WIDTH и адреса шириной ADDR_WIDTH
• Локальный параметр DEPTH, вычисляемый на
основе ADDR_WIDTH – количество элементов в
массиве
• Поскольку только один входной порт адреса,
данный массив однопортовый
• Запись: при активном сигнале wr_en_i данные из
wdata_i запишутся в память по адресу addr_i (по
положительному фронту clk_i)
• Чтение: при неактивном сигнале wr_en_i данные
из памяти по адресу addr_i будут доступны на
rdata_o (комбинационно)
• Массивы памяти, как правило, не сбрасываются
module mem_bank #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4
)(
input
clk_i,
input
wr_en_i,
input [ADDR_WIDTH-1:0] addr_i,
input [DATA_WIDTH-1:0] wdata_i,
output [DATA_WIDTH-1:0] rdata_o
);
localparam DEPTH = 2**ADDR_WIDTH;
reg [DEPTH-1:0][DATA_WIDTH-1:0] mem;
always @(posedge clk_i) begin
if (wr_en_i) begin
mem[addr_i] <= wdata_i;
end
end
assign rdata_o = !wr_en_i
? mem[addr_i]
: {DATA_WIDTH{1'b0}};
endmodule
27
28. Тестирование на Verilog
2829. Testbench
• Для тестирования имплементированных модулей, необходимо написатьотдельный модуль, называемый testbench
• Тестируемый модуль встраивается в testbench
• Логика внутри testbench подает различные воздействия на входные порты
тестируемого модуля
• Testbench может включать несинтезируемые конструкции, поскольку не является
частью модуля, а служит исключительно для его проверки
• С помощью подачи различных воздействий на входные порты тестируемого
модуля в testbench, можно сгенерировать временные диаграммы, на которых
наглядно будет отражено, как изменение входных воздействий влияет на
выходные
29
30. Testbench для multibit_adder
`timescale 1ns/1psInitial-блок для подачи разных
воздействий
Модуль testbench для multibit_adder
initial begin
$dumpvars;
$dumpvars; для построения
module multibit_adder_tb();
first_i = 8'hAA;
временных диаграмм
second_i = 8'hBB;
Локальный параметр для инстанса модуля
localparam WIDTH = 8;
#20;
#20; для ожидания в
first_i = 8'hCC;
Объявление
reg [WIDTH-1:0] first_i;
20 единиц времени
second_i = 8'hDD;
внутренних
reg [WIDTH-1:0] second_i;
#20;
переменных
$finish(); для
wire [WIDTH-1:0] sum_o;
$finish;
testbench,
завершения
end
которые будут
моделирования
multibit_adder #(
входами (reg) и
.WIDTH(WIDTH)
endmodule
выходами (wire)
) multibit_adder_inst (
модуля
.first_i (first_i ),
.second_i (second_i),
.sum_o
(sum_o
)
);
Инстанс тестируемого модуля
30
31. Testbench для dff_arstn
`timescale 1ns/1psДожидаемся положительного
module dff_arstn_tb();
initial begin
фронта
clk_i
и
ждем
еще
#0.5;
localparam WIDTH = 8;
$dumpvars;
reg
clk_i;
@(posedge clk_i); #0.5;
reg
arstn_i = 1'b1;
arstn_i = 1'b0;
reg [WIDTH-1:0] d_i;
#3;
Подаем сброс
wire [WIDTH-1:0] q_o;
@(posedge clk_i); #0.5;
Ждем #3;
integer seed = 100;
Снимаем сброс
arstn_i = 1'b1;
Переменная для Инициализируем
dff_arstn #(
d_i = 8'h00;
рандомизации
.WIDTH(WIDTH)
for (integer j=0; j<10; j=j+1) begin
) dff_arstn_inst (
@(posedge clk_i); #0.5;
Формируем 10 раз
.clk_i (clk_i ),
d_i = $urandom(seed);
рандомные значения на
.arstn_i(arstn_i),
end
d_i, удерживая каждое в
.d_i
(d_i
),
#2;
течение 1 такта
.q_o
(q_o
)
$finish;
always-блок для генерации
);
end
тактового сигнала
always begin
clk_i = 1'b0; #1;
endmodule
clk_i = 1'b1; #1;
31
end
32. Вопросы?
3233. Спасибо за внимание!
module hello_world();initial begin
$display("Hello, World!");
end
endmodule
Спасибо за внимание!
33
Программирование