Source :
https://marsohod.org/verilog
https://marsohod.org/11-blog/77-veriloglesson1
====================================
ПРОВОДА
====================================
wire a;
провод а:
wire - рассматриваются как провода соединяющие входы выходы.
wire b;
провод b :
wire b;
соединяем провода:
assigne a=b;
шина (вектор) проводов:
wire [7:0] c;
wire [7:0] d;
соединение шин ( не!!! копирование значения, а соединение проводов):
assigne d = c;
assign g = f[2]; //назначить сигналу “g” второй бит шины “f”
====================================
МАССИВЫ СИГНАЛЬНЫХ ШИН
====================================
Так же, в большинстве диалектов Verilog, вы можете определить массивы сигнальных шин:
wire [7:0] k [0:19]; //массив из двадцати 8-ми битных шин
====================================
РЕГИСТРЫ
====================================
reg [7:0] m;
reg [0:100] n;
могут использоваться в комбинаторной логике и тогда ведут себя как провода, а могут использоваться в синхронной логике и тогда ведут себя как Д-триггеры.
То есть, если регистру присваивается значение по фронту сигнала.
В англоязычных статьях D-триггер называют flipflop.
====================================
МАССИВЫ РЕГИСТРОВ
====================================
Вы можете определить массив регистров, которые обычно называют “память” (RAM):
reg [7:0] q [0:15]; //память из 16 слов, каждое по 8 бит
====================================
ЦЕЛОЕ ЧИСЛО
====================================
integer i_count;
Похоже на регистр, но всегда 32битное, знаковое.
====================================
МОДУЛИ
====================================
Модули это сгруппированные в блоки логика.
Каждуй блок логики это модуль (module).
Модуль имеет входы и выходы, которые ведут себя как провода (WIRE)
Сначала описывают имя модуля и его порты ввода- вывода:
module my_module_name( port_a, port_b, port_c, w, y, z)
далее указывают направление сигналов
input port_a;
output [7:0] port_ b; // восьмибитный порт вывода
input [0:4] w;
inout y; // двунаправленный сигнал. Используется для внешних контактов микросхем.
и можно сразу декларировать как регистр:
output [3:0] z;
reg [3:0] z;
А можно сразу в объявлении модуля все указать:
module my_module
(
input wire port_a,
output wire [6:0]port_b,
input wire [0:4]w,
inout wire y,
output reg [3:0]z
);
Используем входные данные как провода:
wire r = w[1];
выводим значения наружу модуля:
assign port_b = h[6:0];
То есть подключаем шину h к выходной шине port_b
В конце описания блока модуля указываем ключевое слово endmodule.
Вот как в этом примере:
module my_module_name (input wire a, input wire b, output wire c);
assign c = a & b;
endmodule
====================================
ЧИСЛА!!! в описании:
====================================
Числа, это постоянные сигналы:
wire [12:0] s = 12; //32-х битное десятичное число, которое будет “обрезано” до 13 бит
wire [12:0] z = 13’d12; //13-ти битное десятичное число
wire [3:0] t = 4'b0101; //4-х битное двоичное число
wire [3:0] q = 8'hA5; //8-ми битное шестнадцатеричное число A5
wire [63:0] u = 64'hdeadbeefcafebabe; //64-х битное шестнадцатеричное число
ЕСЛИ НЕ ОПРЕДЕЛИТЬ РАЗМЕР ЧИСЛА, ТО ОНО ПО УМОЛЧАНИЮ ПРИНИМАЕТСЯ 32х разрядным.
Числа можно прибавлять к шине ( лучше ее интерпретировать в этом случае как вектор)
wire [3:0] aa;
wire [3:0] bb;
assign bb = aa + 1;
статьях
====================================
ПОДКЛЮЧЕНИЕ МОДУЛЕЙ В МОДУЛЯХ
====================================
Пусть есть mjb модуля описывающих какую то логику:
module AND2(output OUT, input IN1, input IN2);
assign OUT = IN1 & IN2;
endmodule
module NAND2(output OUT, input IN1, input IN2);
assign OUT = ~(IN1 & IN2);
endmodule
module XOR(output OUT, input IN1, input IN2);
assign OUT = IN1 ^ IN2;
endmodule
Помним, что по определению входи и выходы модуля, это провода (wire)
В одном модуле можно добавить эти модули вместе:
module adder1(output sum, output c_out, input a, input b, input c_in);
// провода для соединений внутри модуля:
wire s1,s2,s3;
// объявление экземпляров внешних модулей и подключение их к проводам:
XOR my_1_xor( .OUT (s1), .IN1 (a), .IN2 (b) );
AND2 my_1_and2( .OUT (s3), .IN1 (a), .IN2 (b) );
XOR my_2_xor( .OUT (sum), .IN1 (s1), .IN2 (c_in) );
AND2 my_2_and2( .OUT (s2), .IN1 (s1), .IN2 (c_in) );
XOR my_3_xor( .OUT (c_out), .IN1 (s2), .IN2 (s3) );
endmodule
Порядок описания экземпляра модуля такой:- Пишем название модуля, экземпляр которого нам нужен.
- Пишем название конкретно этого экземпляра модуля (по желанию).
- Описываем подключения сигналов: точка и затем имя сигнала модуля, затем в скобках имя провода, который сюда подключен.
Альтернативное описание модуля без привлечения внешних модулей:
module adder1(output sum, output c_out, input a, input b, input c_in);
assign sum = (a^b) ^ c_in;
assign c_out = ((a^b) & c_in) ^ (a&b);
endmodule
====================================
АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
====================================
Описаны по ссылке. Ничего необычного.
https://marsohod.org/11-blog/82-veriloglesson3
Единственное, что хотел бы упомянуть тут:
- Оператор условного выбора
Язык C имеет оператор ‘? :’. С его помощью можно выбрать одно значение из двух по результату логического выражения. В Verilog тоже есть подобный оператор. Он фактически реализует мультиплексор. В данном примере на выходе мультиплексора окажется значение operandA если сигнал sel_in единица. И наоборот. Если входной сигнал sel_in равен нулю, то на выходе мультиплексора будет значение operandB.
module simple_mux (
operandA, operandB, sel_in, out_mux);
//входные 8-ми битные операнды
input [7:0] operandA, operandB;
//входной сигнал селектора
input sel_in;
//Выход мультиплексора
output [7:0]out_mux;
assign out_mux = sel_in ? operandA : operandB;
endmodule
====================================
ПОВЕДЕНЧЕСКИЕ БЛОКИ ALWAYS
====================================
(behavioral blocks)
Синтаксис:
always @(<sensitivity_list>) <statements>
Пример:
always @(a or b or d) <statements>
( реакция по изменению сигналов a, b и d )
или :
always @* <statements>
( реакция по изменению всех сигналов )
с правой стороны в от знака равенства может быть провод или регистр,
а вот с левой только регистр.
Пример:
reg [3:0] c;
always @(a or b or d)
begin
c = <выражение использующее входные сигналы a,b,d>;
end
"=" это блокирующее присвоение
блокирующие присвоения используются для описания комбинаторной логики в поведенческих блоках.
Для симулятора это означает, что выражение вычисляется, его результат присваивается регистру приемнику и он тут же, немедленно, может быть использован в последующих выражениях.
ваыва
Пример:
wire [3:0] a, b, c, d, e;
reg [3:0] f, g, h, j;
always @(a or b or c or d or e)
begin
f = a + b;
g = f & c;
h = g | d;
j = h - e;
end
====================================
CASE
====================================
Определение:
case (selector)
option1: <statement>;
option2: <statement>;
default: <if nothing else statement>; //по желанию, но желательно
endcase
Пример:
А вот и простой пример:
wire [1:0] option;
wire [7:0] a, b, c, d;
reg [7:0] e;
always @(a or b or c or d or option) begin
case (option)
0: e = a;
1: e = b;
2: e = c;
3: e = d;
endcase
end
====================================
FOR
====================================
На языке Verilog цикл скорее описывает сколько экземпляров логических функций должно быть реализовано аппаратно. Чтобы синтез прошел успешно, циклы должны иметь заданное фиксированное число итераций – иначе синтезатор просто не сможет ничего сделать.
module find_high_bit(input wire [7:0]in_data, output reg [2:0]high_bit, output reg valid);
integer i;
always @(in_data)
begin
//определим, есть ли в шине единицы
valid = |in_data;
//присвоим хоть что нибудь
high_bit = 0;
for(i=0; i<8; i=i+1)
begin
if(in_data[i])
begin
// запомним номер бита с единицей в шине
high_bit = i;
end
end
end
endmodule
====================================
СИНХРОННАЯ ЛОГИКА
====================================
Блокирующее и не блокирующее присваивание.
Это триггер. D-Триггер (flipflop) – это специальный логический элемент, способный запоминать.
Давайте посмотрим простой пример. Вот графическое представление восьмибитного регистра, запоминающего входные данные по фронту тактовой частоты:
Его представление в Verilog может быть такое:
module DFF8 (clock, data, q);
input clock;
input [7:0] data;
output [7:0] q;
reg [7:0] q;
always @(posedge clock) begin
q <= data;
end
endmodule
Здесь блок always чувствителен к фронту тактовой частоты: posedge clock. Еще, вместо posedge – положительного фронта тактовой частоты можно использовать negedge – отрицательный фронт. При использовании negedge запоминание в регистр происходит по перепаду сигнала clock с «1» в «0». Сам факт запоминания входных данных data в регистре q здесь описан оператором "<=". Это так называемое «неблокирующее присвоение».
на вход in такого регистра может быть подано, например, значение выражения какой нибудь комбинаторной функции, вычисленной в другом always блоке.
====================================
СИНХРОНЫЙ СБРОС
====================================
Очень часто модули имеют дополнительные сигнал сброса. Они используются чтобы установить триггера схемы в некоторое исходное состояние. Сигналы сброса могут быть разных типов: синхронные и ассинхронные,
Вот пример реализации синхронного сброса регистра с активным положительным уровнем (active-high):
Вот так эту схему можно описать на языке Verilog:
module DFFSR (reset, clock, data, q);
input reset;
input clock;
input [7:0] data;
output [7:0] q;
reg [7:0] q;
always @(posedge clock) begin
if (reset) begin
q <= 0;
end else begin
q <= data;
end
end
endmodule
====================================
АСИНХРОНЫЙ СБРОС
====================================
Ассинхронный сброс c активным сигналом единица описывается следующим образом. Вот схема:
module DFFAR (reset, clock, data, q);
input reset;
input clock;
input [7:0] data;
output [7:0] q;
reg [7:0] q;
always @(posedge clock or posedge reset) begin
if (reset) begin
q <= 0;
end else begin
q <= data;
end
end
endmodule
Обратите внимание, что сигнал reset теперь попал в список чувствительности для always блока.
Вообще-то для ассинхронных сбросов желательно всегда придерживаться вот такого стиля описания синхронной логики, как в приведенном выше примере. Сперва пишите "if(reset)" и все присвоения связанные с начальной инициализацией регистров. Потом пишите "else" и всю остальную логику. Дело в том, что синтезатор пытается в нашем коде выделить знакомые ему конструкции. Если написать иначе, хотя и логически правильно, то остается риск, что синтезатор чего-то не поймет и сгенерирует схему иначе, не используя все возможности триггера, не используя его ассинхронный сброс. В результате схема может стать больше (для ее реализации нужно больше логических элементов) и "медленнее" (работает стабильно на меньшей частоте).
счетчик по модулю 6
module mod_counter(
input wire reset,
input wire clock,
input wire [3:0]in,
input wire load,
output reg [3:0]cnt
);
parameter MODULE = 6;
always @(posedge clock or posedge reset)
begin
if(reset)
cnt <= 4'b0000;
else
begin
if(load)
cnt <= in;
else
if(cnt+1==MODULE)
cnt <= 4'b0000;
else
cnt <= cnt + 1'b1;
end
end
endmodule
Комментарии
Отправить комментарий