10.4. Specialized ‘always’ blocks¶
In , we saw that a sequential design contains two parts i.e. ‘combination logic’ and ‘sequential logic’. The general purpose ‘always’ block is not intuitive enough to understand the type of logic implemented by it. Also, the synthesis tool has to spend the time to infer the type of logic the design intended to implement. Further, if we forgot to define all the outputs inside all the states of a combination logic, then a latch will be inferred and there is no way to detect this type of errors in Verilog. To remove these problems, three different types of ‘always’ blocks are introduced in SystemVerilog, i.e. ‘always_ff’, ‘always_comb’ and ‘always_latch’, which are discussed below,
10.4.1. ‘always_comb’
The ‘always_comb’ block represents that the block should be implemented by using ‘combinational logic’; and there is no need to define the sensitivity list for this block, as it will be done by the software itself. In this way SystemVerilog ensures that all the software tools will infer the same sensitivity list. The example of the ‘always_comb’ block is shown in .
Listing 10.2 ‘always_comb’ example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// always_comb_ex.sv module always_comb_ex ( input logic a, b, output logic y, z ); always_comb begin if (a > b) begin y = 1; z = ; end else if (a < b) begin y = ; z = 1; end else begin y = 1; z = 1; end end endmodule |
Further, if the design can not be implemented by ‘pure combinational’ logic, then the compiler will generate the error as shown in . In this listing, we commented the Line 18 and Lines 21-24. In this case, a latch will be inferred (see comments), therefore error will be reported as shown in Lines 8-9. Also, the information about the latch will be displayed by the software as shown in Lines 10-11.
Listing 10.3 Error for incomplete list of outputs in ‘always_comb’ block
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 |
// always_comb_error_ex.sv module always_comb_error_ex ( input logic a, b, output logic y, z ); // Error (10166): SystemVerilog RTL Coding error at always_comb_error_ex.sv(13): always_comb // construct does not infer purely combinational logic // Info (10041): Inferred latch for "z" at always_comb_error_ex.sv(17) // Info (10041): Inferred latch for "y" at always_comb_error_ex.sv(17) always_comb begin if (a > b) begin y = 1; z = ; end else if (a < b) begin // y = 0; // missing output will create latch z = 1; end // else begin // missing 'else' block will create latch // y = 1; // z = 1; // end end endmodule |
10.4.2. ‘always_latch’
In , we saw that the implementation of the logic need a ‘latch’, therefore we can replace the ‘always_comb’ with ‘always_latch’ as done in Line of and the code will work fine. Similar to the ‘always_comb’, the ‘always_latch’ does not need the sensitive list.
Listing 10.4 ‘always_latch’ example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// always_latch_ex.sv module always_latch_ex ( input logic a, b, output logic y, z ); always_latch begin if (a > b) begin y = 1; z = ; end else if (a < b) begin // y = 0; // missing output will create latch z = 1; end // else begin // missing 'else' block will create latch // y = 1; // z = 1; // end end endmodule |
Similarly, the was implemented using pure combination logic. If we change the ‘always_comb’ with ‘always_logic’ in that listing, then error will be reported as no ‘latch’ is required for this design as shown in Lines 8-9 of .
Listing 10.5 Error for latch logic as the design can be implemented using pure combination logic
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 |
// always_latch_error_ex.sv module always_latch_error_ex ( input logic a, b, output logic y, z ); // Error (10165): SystemVerilog RTL Coding error at always_latch_error_ex.sv(11): // always_latch construct does not infer latched logic always_latch begin if (a > b) begin y = 1; z = ; end else if (a < b) begin y = ; // missing output will create latch z = 1; end else begin // missing 'else' block will create latch y = 1; z = 1; end end endmodule |
пример
Простой пример двух шлепки следует:
модуль высший уровень(Часы,сброс настроек); ввод Часы; ввод сброс настроек; рег flop1; рег флоп2; всегда @ (поза сброс настроек или поза Часы) если (сброс настроек) начать flop1 <= ; флоп2 <= 1; конец еще начать flop1 <= флоп2; флоп2 <= flop1; конецконечный модуль
В Оператор в Verilog — это еще один аспект того, что он является языком описания оборудования, в отличие от обычного процедурного языка. Это называется «неблокирующим» назначением. Его действие не регистрируется до тех пор, пока не будет выполнен блок always. Это означает, что порядок присвоений не имеет значения и даст один и тот же результат: flop1 и flop2 будут менять местами значения каждые часы.
Другой оператор присваивания называется блокирующим назначением. Когда присваивание используется в целях логики, целевая переменная обновляется немедленно. Если бы в приведенном выше примере использовались операторы блокирующий оператор вместо , flop1 и flop2 не поменялись местами. Вместо этого, как и в традиционном программировании, компилятор понимает, что нужно просто установить flop1 равным flop2 (и впоследствии игнорировать избыточную логику, чтобы установить flop2 равным flop1).
Пример счетчик Схема следующая:
модуль Div20x (первый, clk, cet, белый гриб, считать, tc);// НАЗВАНИЕ 'Счетчик деления на 20 с включениями'// включить CEP - только часы// enable CET - это включение часов и// включает вывод TC// счетчик на языке Verilogпараметр размер = 5;параметр длина = 20;ввод первый; // Эти входы / выходы представляютввод clk; // подключения к модулю.ввод cet;ввод белый гриб;вывод размер-1 считать;вывод tc;рег размер-1 считать; // Назначенные сигналы // внутри всегда // (или начальный) блок // должно быть типа regпровод tc; // Остальные сигналы имеют тип провода// Приведенный ниже оператор always является параллельным// оператор выполнения, который// выполняет в любое время сигналы// сначала или clk переход от низкого к высокомувсегда @ (поза clk или поза первый) если (первый) // Это вызывает сброс cntr считать <= {размер{1'b0}}; еще если (cet && белый гриб) // Включает оба значения true начать если (считать == длина-1) считать <= {размер{1'b0}}; еще считать <= считать + 1'b1; конец// значение tc присваивается непрерывно// значение выраженияназначать tc = (cet && (считать == длина-1));конечный модуль
Пример задержек:
...рег а, б, c, d;провод е;...всегда @(б или е) начать а = б & е; б = а | б; #5 c = б; d = #6 c ^ е; конец
В всегда Вышеупомянутое предложение иллюстрирует другой тип метода использования, то есть он выполняется всякий раз, когда любой из объектов в списке ( б или е) изменения. Когда одно из этих изменений, а немедленно присваивается новое значение, и из-за назначения блокировки, б впоследствии присваивается новое значение (с учетом нового значения а). После задержки в 5 единиц времени, c присвоено значение б и ценность с ^ е спрятан в невидимом магазине. Затем, еще через 6 единиц времени, d присваивается значение, которое было спрятано.
Сигналы, которые управляются изнутри процесса (начальный или всегда блокируемый), должны быть типа рег. Сигналы, поступающие извне процесса, должны быть типа провод. Ключевое слово рег не обязательно подразумевает аппаратный регистр.
Solutions For Passing Multiple Signals Across Clock Domain Crossing (CDC)
So how do we deal with synchronizing multiple signals? There are at least several solutions with different levels of complexity:
- Multi-bit signal consolidation
- Multi-cycle path (MCP) formulation without feedback
- Multi-cycle path (MCP) formulation with feedback acknowledge
- Dual-Clock Asynchronous FIFO
- Two-Deep FIFO
Multi-cycle path (MCP) formulation is a particularly interesting and widely applicable solution. It refers to sending unsynchronized data from the source clock domain to the destination clock domain, paired with a synchronized control (e.g. a load enable) signal. The data and control signals are sent simultaneously from the source clock domain. The data signals do not go through any synchronization, but go straight into a multi-bit flip-flop in the destination clock domain. The control signal is synchronized through a two-flip-flop synchronizer, then used to load the unsynchronized data into the flip-flops in the destination clock domain. This allows the data signals to settle (while the control signal is being synchronized), and captured together on a single clock edge. We will get into two variations of this technique in later sections.
Multi-bit signal consolidation
Consolidating multiple bits across clock domain crossing (CDC) into one is more of a best practice, than a technique. It’s always good to reduce as much as possible the number of signals that need to cross a clock domain crossing (CDC). However, this can be applied directly to the problem of sequencing two signals into the destination clock domain. A single signal can be synchronized across the clock domain crossing (CDC), and the two sequenced signals can be recreated in the destination clock domain once the synchronizing signal is received.
Multi-cycle path (MCP) formulation without feedback
The multi-cycle path (MCP) synchronizer is comprised of several components:
- Logic that converts a synchronization event from source clock domain to a toggle to pass across the clock domain crossing (CDC)
- Logic that converts the toggle into a load pulse in the destination domain
- Flip-flops to capture the unsynchronized data bits
One key idea in this design is that the synchronization event (a pulse) is converted into a single toggle (either low to high, or high to low) before being synchronized into the destination clock domain. Each toggle represents one event. You need to be careful when resetting the synchronizer such that no unintended events are generated (i.e. if the source domain is reset on its own, and the toggle signal goes from high to low due to reset).
Source clock domain event to toggle generator
The following circuit resides in the source clock domain, and converts an event that needs to traverse the clock domain crossing (CDC) into a toggle, which cannot be missed due to sampling in the destination clock domain.
Destination clock domain toggle to load pulse generator
Next, we need a circuit in the destination clock domain to convert the toggle back into a pulse to capture the multi-bit signal.
Putting it together
Finally, putting the entire synchronizer circuit together, we get the following.
Notice the multi-bit data signal passes straight from source (clock) flip-flop to destination (clock) flip-flop to avoid problems with synchronizing multiple bits. A single control signal is synchronized to allow time for the multi-bit data to settle from possible metastable state. The load pulse from the source clock domain first gets converted into a toggle. The toggle is synchronized across the clock domain crossing (CDC), then gets converted back to a load pulse in the destination clock domain. Finally that load pulse is used to load the multi-bit data signal into flip-flops in the destination clock domain.
Rate of Synchronization
Initially you may think that the the toggle synchronizer eliminates the problem of a missing pulse when crossing from a fast clock to a slow clock domain. However, there is a limitation of the rate of how often data can be synchronized across the synchronizer. If you look at the complete circuit, the input data has to be held until the the synchronization pulse loads the data in the destination clock domain. The whole process takes at least two destination clocks. Therefore to use this circuit, you must be certain that the input data only needs to be synchronized not more than once every three destination clock cycles. If you are unsure, then a more advanced synchronization circuit like the synchronizer with feedback acknowledgement or Dual-Clock Asynchronous FIFO should be used.
Железное противостояние: VHDL против Verilog
Introduction to Verilog
Verilog is a type of Hardware Description Language (HDL). Verilog is one of the two languages used by education and business to design FPGAs and ASICs. If you are unfamilliar with how FPGAs and ASICs work you should read this page for an introduction to FPGAs and ASICs. Verilog and VHDL are the two most popular HDLs used. Compared to traditional software languages such as Java or C, Verilog works very differently. Let’s get started by looking at a simple example.
First we will create a Verilog file that describes an And Gate. As a refresher, a simple And Gate has two inputs and one output. The output is equal to 1 only when both of the inputs are equal to 1. Below is a picture of the And Gate that we will be describing with Verilog.
An And Gate
Let’s get to it! One fundamental unit of Verilog is called a wire. For now let’s assume that a wire can only be a 0 or a 1. Here is some basic wire logic:
wire and_temp; assign and_temp = input_1 & input_2;
We are creating a wire called and_temp on the first line of code. On the second line of the code, we are taking the wire that we created and we are assigning the wire. To assign it, we are using the Boolean AND function which in Verilog is the Ampersand (&). If you were to describe the code shown above, you might say, «The signal and_temp gets input_1 AND-ed with input_2.»
Input_1 and Input_2 are inputs to this piece of Verilog Code. Let’s show the complete list of inputs and outputs. This is done in the module definition. Module is a reserved keyword in Verilog which shows the creation of a block of code with defined inputs and outputs.
module example_and_gate ( input_1, input_2, and_result); input input_1; input input_2; output and_result;
This is your basic module. It defines our module called example_and_gate and 3 signals, 2 inputs and 1 output. Let’s put everything together to finish the file. The only thing we are missing is the assignment of the output and_result. One other note, // in Verilog is used for a comment.
/////////////////////////////////////////////////////////////////////////////// // File Downloaded from http://www.nandland.com /////////////////////////////////////////////////////////////////////////////// module example_and_gate ( input_1, input_2, and_result); input input_1; input input_2; output and_result; wire and_temp; assign and_temp = input_1 & input_2; assign and_result = and_temp; endmodule // example_and_gate
Congratulations! You have created your first Verilog file.
Does it seem like you had to write a lot of code just to create a stupid and gate? First of all, and gates aren’t stupid. Secondly, you are correct, HDLs take a lot of code to do relatively simple tasks. You can take some comfort in the fact that Verilog is at least less verbose than VHDL. Get used to the fact that doing something that was very easy in software will take you significantly longer in an HDL such as Verilog or VHDL. But just ask some software guy to try to generate an image to a VGA monitor that displays Conway’s Game of Life and watch their head spin in amazement! By the way, that video is created with an FPGA. You will be able to do that soon enough!
Next we will discuss another fundamental Verilog keyword: ALWAYS
DC Simulation
For the DC simulation, we will sweep the input of the inverter, 0 to 5 volts.
To do this, first open up the schematic that is set up to simulate the inverter.
Then, select Tools -> Analog Environment, to open the Cadence Analog
Design Environment window.
Verify that the simulator that you are using is «spectreS». If not, change
the simulator to spectreS by selecting Setup -> Simulator/Directory/Host …
Now, select Analyses -> Choose …
Select the DC box under the analysis list. Also, select «Component Parameter»
and «Select Component». In the Virtuoso schematic window, select the voltage
source that is connected to the input of the inverter. A list of parameters of
the voltage source will appear. Choose «dc» and make sure in the analysis setup
window, that the Sweep Range has «Start-Stop» selected and 0 and 5 are in the
«Start» and «Stop» parameters, respectively. Also, make sure that the Sweep Type
reads «Automatic».
Now, you are ready to simulate. Select the output and input of the inverter
to plot and run the simulation. The DC Response should look something like the
waveform below.
Notice how the input and output cross in the linear (transition) region of
the inverter. We will want to calculate this crossing value so that it can be
used as a bias to the input of the inverter for future AC simulation. This can
be accomplished either by eyeing it or more effectively by using the calculator.
Open the calculator by selecting the calculator icon. Make sure that
«Evaluate Buffer» is unchecked. Select wave, and then click on the output
waveform in the Waveform Window. Now, select «Special Functions» and choose
«cross» in the Calculator window. When the «Threshold Crossing» window appears,
select OK for the default values. Select «Evaulate Buffer» in the Calculator
Window, and the cross value will be given. This should be roughly vdd/2 (vdd/2
is for an ideal inverter). Round off to the nearest hundreth and remember this
value for AC simulation.
1.5. Load the design on FPGA¶
Follow the below, steps to load the design on FPGA,
-
Connect the FPGA to computer and turn it on.
-
Full compilation process generates the .sof/.pof files, which can be loaded on the FPGA board. To load the design on FPGA board, go to Tools–>Programmer. And a programmer window will pop up.
-
In the programmer window (see ), look for two things i.e. position ‘1’ should display ‘USB-BLASTER’ and position ‘6’ should display the ‘.sof’ file. If any of this mission then follow below steps,
-
Finally click on the ‘start’ button in and check the operation of ‘half adder’ using switches SW0 and SW1; output will be displayed on green LEDs i.e. LEDG0 and LEDG1.
1.1. Introduction¶
In this tutorial, full adder is designed with the help of half adders. Here we will learn following methods to create/implement the digital designs using Altera-Quartus software,
- Digital design using ‘block schematics’,
- Digital design using ‘Verilog codes’,
- Manual pin assignment for implementation,
- Pin assignments using ‘.csv’ file,
- Loading the design on FPGA.
- Converting the ‘Verilog design’ to ‘Symbols’
- Converting the ‘Block schematic’ to ‘Verilog code’ and ‘Symbols’.
If you do not have the FPGA-board, then skip the last part i.e. ‘loading the design on FPGA’. Simulation of the designs using ‘Modelsim’ is discussed in .
Запись чисел в Verilog
Числа или иначе константы могут определяться в десятичном, шестнадцатеричном, восьмеричном или двоичном форматах. В языке Verilog предусмотрены две формы для записи чисел. Первая форма представляет собой простое десятичное число как последовательность цифр от 0 до 9 и опционально может предваряться символами плюса или минуса. Вторая форма представления чисел имеет следующий формат:
1 |
<size> <base_format> <number> |
Поле size содержит десятичные цифры и указывает разрядность константы в битах. Это поле опционально и может опускаться в случаях если разрядность константы заранее определена (Допустим производится присвоение константного значения переменной, разрядность которой задана при ее объявлении). Если разрядность не определена, то разрядность принимается по умолчанию равной 32 (32 в большинстве систем синтеза, в некоторых разрядность по умолчанию может отличаться).
Второе поле base_format содержит букву, определяющую формат представления числа (десятичный, двоичный …). Эта буква предваряется символом одиночной кавычки (‘). Формат представления определяется следующими буквами: d – десятичный; h – шестнадцатиричный; o – восьмеричный и b – двоичный. Допускается вводить эти буквы как прописными, так и строчными.
Последнее поле number содержит цифры допустимые для выбранного формата:
от 0 до 9 для десятичного формата;
от 0 до 7 для восьмеричного;
0 и 1 для двоичного;
от 0 до 9 и буквы a, b, c, d, e, f для шестнадцатеричного.
Буквенные обозначения могут вводится как строчными, так и прописными буквами.
Числа имеющие знаковое представление предваряются опциональными символами плюса и минуса перед полем size. Эти символы перед полем number недопустимы. Запись -8’h5A эквивалентна записи -(8’h5A), а запись 8’h-5A вызовет синтаксическую ошибку.
Помимо указанных цифр в поле numbers могут присутствовать буквы x, X, z, Z и символ (?) (В поле size они недопустимы). Буквы x и X обозначают неизвестное (неопределенное) состояние, т.е. состояние соответствующих битов неизвестно. Буквы z и Z обозначают состояние высокого импеданса – z-состояние или отсутствие источника (драйвера). Символ ? эквивалентен символу z и предназначен для лучшей читабельности кода в случаях когда это состояние безразлично (don’t-care).
Существует возможность сокращать запись числа: например 8’b1 будет эквивалентно записи 8’b11111111, но запись 8’b01 будет эквивалентна записи 8’b00000001. В силу того, что такие сокращенные записи могут мешать читабельности кода, употреблять их без особой надобноси не следует, пожалуй допустимыми можно считать записи 8’b0, 8’b1, 8’bx, можно записать и 16’hx – это эквивалентно 16’hxxxx. В остальных случаях лучше делать подробную запись числа.
Для улучшения читабельности допускаются еще два приема: допускается вставлять пробелы и символы табуляции между полями записи, например 8’hB6 можно записать как 8 ‘h B6. Вставлять пробелы внутри поля number не допускается, но можно вставлять символ подчеркивания (_), для разбиения числа на некоторые группы. Примеры 83465 можно записать как 83_465; 8’b01100111 можно записать как 8’b0110_0111. Символ подчеркивания в поле size или перед полем number недопустим.
1.3. Digital design using ‘block schematics’¶
Digitals design can be create using two methods i.e. using ‘block-schematics’ and with ‘programming language e.g. VHDL or Verilog’ etc. Both have their own advantages in the design-process, as we will observe in the later chapters of the tutorial.
In this section, we will create a half_adder using block-schematics method, as shown below,
For this, click on File–>New–>Block diagram/Schematics files, as shown in Fig. 1.5; and a blank file will be created.
Fig. 1.5 Create new block schematics
Double click (anywhere) in the blank file, and a window will pop-up; select the ‘and’ gate from this window as shown in Fig. 1.6. Similarly, select the ‘xor’ gate.
Fig. 1.6 Select ‘and’ gate
Next, right click on the ‘xor’ gate and then click on ‘Generate Pins for Symbol Ports’, as shown in Fig. 1.7.
Fig. 1.7 Add ports
Now, connect the input ports of ‘xor’ gate with ‘and’ gate (using mouse); then Next, right click on the ‘and’ gate and then click on ‘Generate Pins for Symbol Ports’. Finally rename the input and output ports (i.e. x, y, sum and carry) as shown in Fig. 1.8.
Fig. 1.8 Make connections
Finally, save the design with name ‘half_adder_sch.bdf’. It’s better to save the design in the separate folder, so that we can distinguish the user-defined and system-generated files, as shown in Fig. 1.9 where Verilog codes are saved inside the ‘VerilogCodes’ folders, which is inside the main project directory.
Fig. 1.9 Save project in separate directory i.e. VerilogCodes here
Since the project name is ‘full_adder’, whereas the half adder’s design name is ‘half_adder_sch.bdf’ (i.e. not same as the project name), therefore we need to set this design as top level entity for compiling the project. For this, go to project navigator and right click on the ‘half_adder_sch.bdf’ and set it as top level entity, as shown in Fig. 1.10.
Fig. 1.10 Select top level entity for the project
Now, we can analyze the file as shown in Fig. 1.11. If all the connections are correct that analysis option will not show any error.
Note that, ‘start compilation’ option (above the Analyse option in the figure) is used when we want to generate the .sof/.pof file, to load the design on the FPGA, whereas analyze option is used to generate the RTL view only. We will use ‘compilation’ option in next section.