Verilog briefly (напоминалка)

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 sumoutput c_outinput ainput binput 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 or d) <statements>
( реакция по изменению сигналов a, b  и d )
или :
always @* <statements>
( реакция по изменению всех сигналов )

с правой стороны в от знака равенства может быть  провод или регистр,
а вот с левой только регистр.
 Пример:

reg [3:0] c;
always @(a or 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 or or 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 or or 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 в регистре здесь описан оператором "<=".  Это так называемое «неблокирующее присвоение».

на вход 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 clockbegin
  if (resetbegin
    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 resetbegin
  if (resetbegin
    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











Комментарии

Популярные сообщения из этого блога

iNAV work around

How to build coil for Minelab Explorer. DIY

Вычисление азимута и расстояния между двумя точками