Моделирование смешанных схем на system verilog
Содержание
- 1 Еще ускоряемся
- 2 Combinational Element Design Example
- 3 What is the always block used for ?
- 4 4.2. Combinational circuit and sequential circuit¶
- 5 Continuous Assignment
- 6 Блокирующее присваивание
- 7 4.6. Guidelines for using ‘always’ block¶
- 8 Числа
- 9 4.7. If-else statement¶
- 10 10.5. User define types¶
- 11 Procedural Assignment
- 12 Example #6: 4×16 Decoder
- 13 Неблокирующее присваивание
- 14 10.3. ‘logic’ data type¶
- 15 Системные задания и функции (Display and Write Tasks)
- 16 4.3. Concurrent statements and sequential statements¶
- 17 4.10. Loop using ‘if’ statement¶
- 18 Nets and Variables
Еще ускоряемся
К сожалению, современные системы уже настолько сложны, что данного ускорения бывает недостаточно, в этом случае приходится прибегать к распараллеливанию. Многопоточных Verilog симуляторов, насколько я знаю, пока не изобрели, поэтому придется в рукопашную.
В SystemVerilog введен новый механизм для обращения к внешним программным модулям – Direct Programming Interface (DPI). Т.к. этот механизм проще, по сравнению с другими двумя, будем использовать его.
В начале модуля, где мы хотим вызвать внешнюю функцию нужно вставить строчку import.
import «DPI-C» function int some_funct(input string file_name, input int in, output real out);
Далее можно в Verilog ее использовать обычным образом, например, так:
Как правильно скомпилировать и где лежат библиотеки описано в документации на симулятор.
Далее приведу пример программы, работающей в несколько потоков
К сожалению, современные системы уже настолько сложны, что и этого ускорения бывает недостаточно. В этом случае приходится прибегать к использованию OpenCL для расчетов на видеокарте (не сложнее DPI), расчетов на кластере или в облаке. Во всех этих случаях серьезным ограничением является транспортная составляющая, т.е. время передачи данных на расчетное устройство и с него. Оптимальной задачей, в этом случае являются такая, где нужно много считать, при этом имеется, относительно этого расчета, малое количество данных, как исходных, так и результата. Тоже самое относится и к представленной программе, но в меньшей степени. Если это условие не выполняется, то зачастую, считать на одном лишь процессоре, оказывается быстрее.
Необходимо учесть, что ничего из представленных методов не работает, когда в серверной нет питания, впрочем, его только что подали, youtube опять заработал. На этой радостной ноте, спешу закончить мой рассказ, работа ждет.
Combinational Element Design Example
An block can also be used in the design of combinational blocks. For example the following digital circuit represents a combination of three different logic gates that provide a certain output at signal o.
The code shown below is a with four input ports and a single output port called o. The block is triggered whenever any of the signals in the sensitivity list changes in value. Output signal is declared as type in the module port list because it is used in a procedural block. All signals used in a procedural block should be declared as type .
See that the signal o becomes 1 whenever the combinational expression on the RHS becomes true. Similarly o becomes 0 when RHS is false.
Simulation Output
Click here for a slideshow with simulation example !
What is the always block used for ?
An block can be used to realize combinational or sequential elements. A sequential element like flip flop becomes active when it is provided with a clock and reset. Similarly, a combinational block becomes active when one of its input values change. These hardware blocks are all working concurrently independent of each other. The connection between each is what determines the flow of data. To model this behavior, an block is made as a continuous process that gets triggered and performs some action when a signal within the sensitivity list becomes active.
In the following example, all statements within the always block get executed at every positive edge of the signal clk.
4.2. Combinational circuit and sequential circuit¶
Digital design can be broadly categorized in two ways i.e. combinational designs and sequential designs. It is very important to understand the differences between these two designs and see the relation between these designs with various elements of Verilog.
- Combinational designs : Combinational designs are the designs in which the output of the system depends on present value of the inputs only. Since, the outputs depends on current inputs only, therefore ‘no memory’ is required for these designs. Further, memories are nothing but the ‘flip flops’ in the digital designs, therefore there is no need of ‘flip flops’ in combination designs. In the other words, only ‘logic gates (i.e. and, not and xor etc.)’ are required to implement the combinational designs.
- Sequential designs : Sequential designs are the designs in which the output depends on current inputs and previous states of the system. Since output depends on previous states, therefore ‘memories’ are required for these systems. Hence, in the sequential designs the ‘flip flops’ are needed along with the logic gates.
Continuous Assignment
This is used to assign values onto scalar and vector nets and happens whenever there is a change in the RHS. It provides a way to model combinational logic without specifying an interconnection of gates and makes it easier to drive the net with logical expressions.
Whenever b or c changes its value, then the whole expression in RHS will be evaluated and a will be updated with the new value.
Net declaration assignment
This allows us to place a continuous assignment on the same statement that declares the net. Note that because a net can be declared only once, only one declaration assignment is possible for a net.
Блокирующее присваивание
Блокирующее присваивание, заклеймленное некорыми как «медленное», в действительности во многих случаях синтезируется в совершенно ту же схему, что и неблокирующее. Так, например фрагменты:
Verilog Code:
- always @(posedge clk) begin
- x = x + 1;
- y = y + 1;
- end
и
Verilog Code:
- always @(posedge clk) begin
- x <= x + 1;
- y <= y + 1;
- end
дадут один и тот же результат. Оба выражения выполнятся одновременно, в обоих случаях «время выполнения» будет равно времени записи в соответствующий регистр значения на его входе. Пока выражения не зависят друг от друга, никакой разницы между блокирующими и неблокирующими присваиваниями нет.
В то же время, следующая запись являет собой что-то новое:
Verilog Code:
- always @(posedge clk) begin
- x = x + 1;
- y = x;
- end
Здесь x увеличится на 1, а y примет значение x + 1. Чтобы записать это выражение неблокирующими присваиваниями, потребовалась бы такая запись:
Verilog Code:
- always @(posedge clk) begin
- x <= x + 1;
- y <= x + 1;
- end
Цепочку блокирующих присваиваний можно рассматривать как одно большое выражение. Еще пример:
Verilog Code:
- y <= 3*((input_value >> 4) + center_offset
или
Verilog Code:
- y = input_value >> 4;
- y = y + center_offset;
- y = 3 * y;
Эти две записи эквивалентны. Но вторую запись нельзя понимать как последовательную цепочку вычислений. Это верно лишь в том смысле, что всё выражение действительно выстраивается в схему, в которой сначала отрезаются 4 младших разряда, результат и второй операнд идут на вход сумматора, а выход сумматора отдается умножителю на три. Так это представляется в электрической схеме и человек для удобства нарисует эту схему слева направо и читать он ее будет последовательно. Но в получившейся схеме все это выражение выполняется непрерывно, так же как и в предыдущей записи с неблокирующим присваиванием. Запись результата в регистр, как и следовало ожидать, происходит по фронту тактового импульса.
Заключительный пример, использующий оба вида присваиваний, заодно напоминающий о неродстве оператора for с одноименными операторами в алгоритмических языках программирования:
Verilog Code:
- // Эквивалентная запись:
- // history <= history; history <= history; history <= history; history <= current;
- always @(posedge clk) begin
- for (i = 1; i < 4; i = i + 1) historyi <= historyi-1;
- history <= current;
- end
- // Эквивалентная запись:
- // avg <= (history+history+history+history)/4;
- always @(posedge clk) begin
- sum = ;
- for (i = ; i < 4; i = i + 1) sum = sum + historyi;
- avg = sum4;
- end
Этот пример выдает скользящее среднее с запаздыванием на два такта.
4.6. Guidelines for using ‘always’ block¶
The general purpose ‘always’ block of Verilog can be misused very easily. And the misuse of this block will result in different ‘simulation’ and ‘synthesis’ results. In this section, the general guidelines are provided for using the ‘always’ block in different conditions.
Further, we can use the specilialized ‘always’ blocks of SystemVerilog to avoid the ambiguities in synthesis and simulation results, which are discussed in .
Note
Note that, the ‘always’ block is used for ‘synthesis (i.e. with sensitive list)’ as well as ‘simulation (i.e. with and without sensitive list)’, which have different set of semantic rules. If we do not follow the below guidelines in the designs, then simulation and synthesis tools will infer different set of rules, which will result in differences in synthesis and simulation results.
Further, SystemVerilog has specialized ‘always blocks’ for different types of designs (see ), which can catch the errors when the designs are not created according to below rules.
4.6.1. ‘always’ block for ‘combinational designs’
Follow the below rules for combinational designs,
- Do not use the ‘posedge’ and ‘negedge’ in sensitive list.
- Sensitive list should contain all the signals which are read inside the block.
- No variable should be updated outside the ‘always’ block.
- Use blocking assignment (i.e. = ) for assigning values.
- All the variables should be updated for all the possible input conditions i.e. if-else and case statements should include all the possible conditions; and all the variables must be updated inside all the conditions.
4.6.2. ‘always’ block for ‘latched designs’
Follow the below rules for latched designs,
- Do not use the ‘posedge’ and ‘negedge’ in sensitive list.
- Sensitive list should contain all the signals which are read inside the block.
- No variable should be updated outside the ‘always’ block.
- Use blocking assignment (i.e. = ) for assigning values.
- At least one the variables should not be updated for some of the possible input conditions.
Числа
Постоянные числа могут быть определены в десятичном (d or D), шестнадцатеричном (h or H), восьмеричном (o or O) или в двоичном (b or B) формате.
Они могут произвольно начинаться со знака + или – и могут быть написаны в одном из следующих форматов.
- <size>'<base><number>: самая полная форма для записи числа.
- <base><number>: эта форма записи использует разрядность по умолчанию и является машинно-зависимой, но, по крайней мере, равняется 32 битам.
- <number>: эта форма записи использует разрядность по умолчанию для десятичного формата.
Наиболее простой формат 3 — беззнаковое десятичное число, содержащее, в любом порядке, последовательность цифр от 0 до 9. Хотя пользователь, возможно, не определил размерность написанного числа, Verilog сам вычисляет разрядность числа, примененного в выражении. Обычно в выражениях разрядность примененных в операторах чисел одинакова. И для использования выбирают требуемое число битов, начиная с наименьшего значащего бита.
Формат 1 определяет постоянный размер для разрядности и записывается следующим образом:
При этом неизвестные и высокоимпедансные значения можно использовать во всех числах, кроме десятичных. В каждом случае применение символов x или z представляет данное число битов, имеющих состояние x — неизвестное значение или z— высокий импеданс. При записи чисел, кроме z, может быть использован символ ?. Его применяют в операторе case для того, чтобы улучшить читаемость текста.
Если мы имеем два числа, и одно из них имеет меньшую разрядность, чем другое, то в таком случае для меньшего числа производится заполнение старших разрядов нулями так, чтобы оба числа имели одинаковую разрядность. Если же в числе с большей разрядностью старшие разряды заполнены значениями x или z, то и число с меньшей разрядностью тоже будет заполнено значениями x или z.
Символ подчеркивания может вставляться в числа с любым основанием для того, чтобы улучшить читаемость кода. Но, конечно, символ подчеркивания не может быть первым символом в записи числа. Приведем примеры двоичных чисел: 12’b 0x0x_1101_0zx1 и 12’b 0x0x11010zx1. Как мы видим, первое число читать значительно легче. А это пример неправильной записи: 8’b_0001_1010.
Примеры записи чисел, в которых не определена разрядность:
- 792 — десятичное число;
- ‘h 7d9 — шестнадцатеричное число;
- ‘o 7746 — восьмеричное число.
В примере 10 показаны формы записи чисел, в которых определена разрядность.
В примере 11 показаны формы записи чисел со знаком и отрицательных чисел. А в примере 12 показаны формы записи чисел со знаком «?».
Вещественные числа могут быть записаны в десятичном или научном формате. Если число записано в десятичном формате, оно должно иметь, по крайней мере, одну цифру после десятичной точки (пример 13).
Преобразование вещественного числа в целое число
Язык Verilog преобразовывает вещественные числа в целые, округляя вещественное число к самому близкому целому числу, а не отбрасывая десятичную часть. Например, вещественные числа 35,7 и 35,5 и округляются до значения 36, а 35,2 становится 35. Неявное преобразование имеет место, когда вы назначаете вещественное число как целое число (real to an integer).
4.7. If-else statement¶
In this section, a 4×1 multiplexed is designed using If-else statement. We already see the working of ‘if’ statement in the . In lines 11-24 of , ‘else if’ and ‘else’ are added to ‘if’ statement. Note that, If-else block can contain multiple ‘else if’ statements between one ‘if’ and one ‘else’ statement. Further, ‘begin — end’ is added in line 12-15 of , which is used to define multiple statements inside ‘if’, ‘else if’ or ‘else’ block. shows the waveform generated by Modelsim for .
Note that, we are generating the exact designs as the VHDL tutorials, therefore line 22-23 are used. Also, we can remove the line 22-23, and change line 20 with ‘else’, which will also work correctly.
Fig. 4.4 Multiplexer using if statement,
Listing 4.3 Multiplexer using if statement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// ifEx.v module ifEx( input wire1 s, input wire i0, i1, i2, i3, output reg y ); always @(s) begin if (s==2'b00) begin //begin-end is required for more than one statements y = i0; // more statements end else if (s==2'b01) y = i1; else if (s==2'b10) y = i2; else if (s==2'b11) y = i3; else y = y; // no change end endmodule |
10.5. User define types¶
Unlike VHDL, the Verilog does not have the userdefined variables, which is a very handy tool in VHDL. Hence, we need to define states-types and states as ‘localparam’ and ‘reg’ respectively, as shown in . SystemVerilog adds two more functionalities, i.e. ‘enum’ and ‘typedef’, which can be used to define the ‘user-defined (i.e. typedef) enumerated (i.e. enum) type’. In this section, we will learn these two keywords with an example.
10.5.1. ‘typedef’
The keyword ‘typedef’ is similar to typedef-keyword in C. The ‘typedef’ is used to create the ‘user-defined datatype’, as shown in .
Listing 10.7 ‘typedef’ example
typedef logic 31 new_bus; // create a new variable 'new_bus' of size 32 new_bus data_bus, address_bus; // two variables of new data type 'new_bus'
10.5.2. ‘enum’
The ‘enum’ is used to define the ‘abstract variable’ which can have a specific set of values, as shown below. Note that if the type is not defined in ‘enum’ then the type will be set as ‘integer’ by default, which may use extra memory unnecessarily. For example in , the variable ‘num_word1’ has only 3 possible values, but it size is 32 bit (by default); therefore it is wise to define the type of enum as well, as done for the variable ‘new_word2’ in the listing.
Listing 10.8 ‘enum’ example
// default type is integer i.e. new_word1 will have the size of 32 bit enum {one, two, three} num_word1; // variable num_word1 can have only three types of values num_word1 = one; // assign one to num_word1 // size of new_word2 is 2-bits enum logic 1 {seven, eight, nine} new_word2 new_word2 = seven
Procedural Assignment
Procedural assignments occur within procedures such as always, initial, task and functions and are used to place values onto variables. The variable will hold the value until the next assignment to the same variable.
The value will be placed onto the variable when the simulation executes this statement at some point during simulation time. This can be controlled and modified the way we want by the use of control flow statements such as if-else-if, case statement and looping mechanisms.
Variable declaration assignment
An initial value can be placed onto a variable at the time of its declaration as shown next. The assignment does not have a duration and holds the value until the next assignment to the same variable happens. Note that variable declaration assignments to an array are not allowed.
If the variable is initialized during declaration and at time 0 in an initial block as shown below, the order of evaluation is not guaranteed, and hence can have either 8’h05 or 8’hee.
Procedural blocks and assignments will be covered in more detail in a later section.
Example #6: 4×16 Decoder
Testbench
Simulation Log
ncsim> run en=0 in=0x0 out=0x0 en=0 in=0x1 out=0x0 en=0 in=0x2 out=0x0 en=0 in=0x3 out=0x0 en=0 in=0x4 out=0x0 en=0 in=0x5 out=0x0 en=0 in=0x6 out=0x0 en=0 in=0x7 out=0x0 en=0 in=0x8 out=0x0 en=0 in=0x9 out=0x0 en=0 in=0xa out=0x0 en=0 in=0xb out=0x0 en=0 in=0xc out=0x0 en=0 in=0xd out=0x0 en=0 in=0xe out=0x0 en=0 in=0xf out=0x0 en=1 in=0x0 out=0x1 en=1 in=0x1 out=0x2 en=1 in=0x2 out=0x4 en=1 in=0x3 out=0x8 en=1 in=0x4 out=0x10 en=1 in=0x5 out=0x20 en=1 in=0x6 out=0x40 en=1 in=0x7 out=0x80 en=1 in=0x8 out=0x100 en=1 in=0x9 out=0x200 en=1 in=0xa out=0x400 en=1 in=0xb out=0x800 en=1 in=0xc out=0x1000 en=1 in=0xd out=0x2000 en=1 in=0xe out=0x4000 en=1 in=0xf out=0x8000 ncsim: *W,RNQUIE: Simulation is complete.
Неблокирующее присваивание
Неблокирующее присваивание обозначает, что ко входу регистра в левой части присваивания подключается выход комбинаторной схемы, описываемой в правой части выражения. Собственно момент записи определяется списком чувствительности в блоке always, обычно это фронт тактирующего сигнала. Следует знать, что все операторы неблокирующего присваивания внутри одного блока always выполняются одновременно, а условия, определяющие произойдут присваивания или нет, определяются заранее. К моменту присваивания, обычно это фронт тактирующего сигнала, все используемые в выражениях сигналы должны иметь установившиеся значения. В противном случае результат выполнения операции может быть непредсказуемым.
Пример:
Verilog Code:
- reg reg_A;
- reg reg_B;
- wire swap_en;
- always @(posedge clk) begin
- if (swap_en) begin
- reg_A <= reg_B;
- reg_B <= reg_A;
- end
- end
Человеку непосвященному скорее всего покажется, что по фронту сигнала clk, если swap_en равен «1», регистры reg_A и reg_B примут значение, которое reg_B имел до свершения события. В действительности же эта запись соединяет выход регистра reg_A со входом reg_B, а выход reg_B со входом reg_A. Таким образом, если в момент положительного перепада clk сигнал swap_en установлен в «1», в каждый из регистров записывается предварительно установившееся на его входе значение. Для reg_A это значение reg_B, а для reg_B — это значение reg_A. Два регистра обменялись значениями одновременно!
Пример 2
Verilog Code:
- input strobe;
- reg strobe_sampled;
- reg7 count;
- always @(posedge clk) begin
- strobe_sampled <= strobe;
- if (strobe & ~strobe_sampled) begin
- // событие: положительный перепад на входе «strobe»
- count <= count + 1;
- end
- end
По фронту clk происходит запись текущего значения strobe в регистр strobe_sampled. Параллельно происходит проверка, а не единице ли равно текущее значение strobe и не ноль ли при этом значение strobe_sampled. Схема, синтезируемая из условия if использует выход регистра strobe_sampled. То есть, условие внутри if можно понимать как «strobe равно единице и предыдущее значение strobe равно нулю».
При этом не будет лишним повторить, что эта запись на самом деле не так проста, как кажется. Например, условие внутри if — это выход комбинаторной схемы, которая не связана с тактирующим сигналом и поэтому может быть описана извне:
Verilog Code:
- wire strobe_posedge = strobe & ~strobe_sampled;
Но это еще не все. Новичок скорее всего прочитает этот код примерно так: «если обнаружен положительный перепад сигнала strobe, взять содержимое count, увеличить его на 1 и записать обратно в count». В действительности же следует читать это как: «в регистр count записывается значение выражения, которое к моменту обнаружения положительного перепада сигнала strobe имеет установившееся значение count + 1». Вариант записи, иллюстрирующий такое прочтение:
Verilog Code:
- wire strobe_posedge = strobe & ~strobe_sampled;
- wire 7 count_incr = count + 1;
- always @(posedge clk) begin
- strobe_sampled <= strobe;
- if (strobe_posedge)
- count <= count_incr;
- end
В чем же разница? Казалось бы, как ни читай, суть от этого не меняется. Но понимание внутренней структуры происходящего необходимо для описания более сложных систем. Понимание того, как происходит синтез схемы, позволит избежать некоторых досадных ошибок. Вот простейший пример:
Verilog Code:
- always @(posedge clk) begin
- count <= count + 1;
- if (count == 10) count <= ;
- end
Этот счетчик имеет период счета равный 11. Выражение count == 10 выполняется на один такт позже после того, как в регистр count было записано значение 10. Один из способов исправить положение — употребить в if то же выражение, что и в правой части присваивания:
Verilog Code:
- if (count + 1 == 10) count <= ;
Иногда удобно выносить выражения типа count + 1 из блоков always, это позволяет уменьшить вероятность ошибок в случае их многократного использования.
10.3. ‘logic’ data type¶
In previous chapters, we saw that the ‘reg’ data type (i.e. variable type) can not used outside the ‘always’ block, whereas the ‘wire’ data type (i.e. net type) can not be used inside the ‘always’ block. This problem can be resolved by using the new data type provided by the SystemVerilog i.e. ‘logic’.
Note
Please note the following point regarding ‘logic’,
- ‘logic’ and ‘reg’ can be used interchangeably.
- Multiple drivers can not be assigned to ‘logic’ data type i.e. values to a variable can not be assigned through two different places.
- Multiple-driver logic need to be implemented using net type e.g. ‘wire’, ‘wand’ and ‘wor’, which has the capability of resolving the multiple drivers. Since, we did not use the ‘wire’ to drive the multiple-driver logic in this tutorial, therefore ‘wire’ can also be replace by ‘logic’.
- is reimplemented in , where both ‘wire’ and ‘reg’ are replaced with ‘logic’ keyword.
Системные задания и функции (Display and Write Tasks)
Стандартные системные задания и функции и их краткие описания приведены в таблице 17.
Таблица 17. Стандартные системные задания и функции
Вывод на монитор
Основное системное задание — это $display (табл. 18). С его помощью можно просмотреть переменные, строки, выражения. Оно чаще всего употребляется на практике.
Таблица 18. Формат команды Display
Пример использования:
где p1, p2, p3,…, pn могут быть текстовыми строками, переменными или выражениями. Формат системного задания $display аналогичен printf в языке Cи. По умолчанию системное задание $display добавляет новую строку при выводе текста на монитор. $display без параметров используется для перехода на новую строку.
В отличие от $display, системное задание $write при выводе данных на монитор не выполняет переход на новую строку.
Инициализация памяти
Представим себе, что мы применяем в модели память. Как было уже сказано, память описывается как массив регистров. Вот синтаксис для описания массива регистров:
Как же записать данные при инициализации проекта в такую память? Системная функция $readmemb как раз и позволяет читать бинарные данные из файла и загружать эти данные в моделируемую память. Синтаксис этой функции:
где <file_name> — имя и путь к файлу, содержащему двоичные данные, параметр <reg_name> — это 2D-массив регистров, в котором хранятся данные, и, как опции, две цифры, которые указывают начальный и конечный адрес данных.
Файл инициализации может содержать только двоичные данные, пробелы и комментарии. Функцию $readmemb обычно вызывают при инициализации памяти в блоке initial. Функция $readmemh аналогична функции $readmemb, но работает с шестнадцатеричными данными.
Необходимо отметить, что если ранее программа XST, поставляемая фирмой Xilinx, не имела возможности выполнять инициализацию памяти таким образом, но теперь такая возможность уже есть. Пример, показывающий чтение двоичных данных из файла:
Кроме бинарных или шестнадцатеричных данных, пробелов и комментариев, файлы инициализации могут также содержать адресные метки:
В примере 27 показана форма записи данных в файле инициализации памяти.
Продолжение следует
4.3. Concurrent statements and sequential statements¶
In , we saw that the concurrent statements execute in parallel, i.e. the order of the statement does not matter. Whereas shows the example of ‘sequential statements’ where the statements execute one by one. Following are the relationship between ‘statements’ and ‘design-type’,
- Please note that ‘sequential statements’ and ‘sequential designs’ are two different things. Do not mix these together.
- Combinational designs can be implemented using both ‘sequential statements’ and ‘concurrent statements’.
- Sequential designs can be implemented using ‘sequential statements’ only.
- Sequential statements can be defined inside ‘always’ block only. Further, these blocks executes concurrently e.g. if we have more than one always block then these block will execute in parallel, but statements inside each block will execute sequentially.
- Sequential designs are implemented using various constructs e.g. ‘if’, ‘case’ and ‘for’ etc., which are discussed in this chapter.
- Conditional operator (?:) can be used for combinational designs.
4.10. Loop using ‘if’ statement¶
In , a loop is created using ‘if’ statement, which counts the number upto input ‘x’.
Explanation
Warning
Sensitivity list is still not correct in the e.g. we do not put the ‘x’ in the sensitive list at Line 20 which is used inside the ‘always’ block. Further, the ‘clk’ is unnecessarily used at Line 33.
Although the results are correct, but such practice leads to undetectable errors in large designs. We will see the correct style of coding in .
Listing 4.6 Loop using ‘if’ statement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// ifLoop.v (-- This code is for simulation purpose only) // ideally positive or negative clock edge must be used; which will be discussed later. module ifLoop #( parameter N = 3, M = 2 //not used ) ( input wire clk, input wireN: x, output wireN: z ); localparam continueState = 1'b0, stopState = 1'b1; reg currentState; reg N: count = ; always @(clk, currentState, count) begin if(count<=x) currentState = continueState; else currentState = stopState; end // simulation and synthesis difference in verilog: // if count is added to sensitivity list i.e. always @(clk, currentState, count) // then always block must create an infinite loop (see exaplation) // but this simulator will work fine for this case // such error can not be detected in verilog. always @(clk, currentState) begin if(currentState==continueState) count = count+1; else count = ; end assign z = count; endmodule |
Fig. 4.7 Loop using ‘if’ statement, with N = 1
Fig. 4.8 Loop using ‘if’ statement, with N = 3
Nets and Variables
Nets and variables are the two main groups of data types which represent different hardware structures and differ in the way they are assigned and retain values.
Nets
Nets are used to connect between hardware entities like logic gates and hence do not store any value on its own. In the image shown below, a net called net_11 is used to connect between the output of the AND gate to the first input of the flip-flop called data_0. In a similar way, the two inputs of the AND gate are connected to nets net_45 and net_67.
There are different types of nets each with different characteristics, but the most popular and widely used net in digital designs is of type . A is a Verilog data-type used to connect elements and to connect nets that are driven by a single gate or continuous assignment. The is similar to the electrical wire that is used to connect two components on a breadboard.
When there is a requirement for mulitple nets, they can be bunched together to form a single . In the image shown below, we have a 4-bit wire that can send 4 separate values on each one of the wires. Such entities with a width more than 1 are called vectors as we shall see in the next article.
It is illegal to redeclare a name already declared by a net, parameter or variable as shown in the code below.
Variables
A variable on the other hand is an abstraction of a data storage element and can hold values. A flip-flop is a good example of a storage element.
Verilog data-type can be used to model hardware registers since it can hold values between assignments. Note that a need not always represent a flip-flop because it can also be used to represent combinational logic.
In the image shown on the left, we have a flip-flop that can store 1 bit and the flip-flop on the right can store 4-bits.