2013年2月12日 星期二

Introduction to Verilog for FPGAs


Introduction to Verilog for FPGAs




Systems Design Using FPGAs

Unit 3: Introduction to Verilog for FPGAs

Traditionally schematic capture was the means to create a digital design. However as designs became larger and more complex this design method became difficult to debug, revise, edit, and maintain. These are the reasons why the Electronic Design Automation (EDA) vendors started to introduce design tools based on hardware description languages (HDLs). Although HDLs are the main method to create the distinct design blocks of a system it is still common to see schematics used to represent the higher levels of a design to connect the lower level design blocks.
There are two main Hardware Design Languages (HDLs) used today, Veriliog and VHDL. People will no knowledge of either language should find Verilog the easiest of the two to grasp. Also those people with previous experience in the 'C' programming language should find some useful similarities to Verilog. For these reasons Verilog will be the language of choice to demonstrate digital design methods in this module.
It is useful to stress here that HDLs are not programming languages but are as the name describes a hardware description language. That is HDL 'code' describes the the functionality of a circuit.
Verilog was originally designed as a digital simulation language and many of the constructs do not synthesise. That is they cannot be used to produce a netlist. Only the synthesisable elements of the language will be introduced here.

Unit Contents


3.1 Introduction

This section introduces some terminology used in HDLs.

3.1.1 Abstraction Level

There are three levels of abstraction that can be used to describe digital designs.
  • Gate Level
  • Data Flow
  • Behavioural or Algorithmic Level
N.B. These can be compared to machine code, assembly, and 'C' when programming a micro-controller.
Gate level of abstraction describes the circuit purely in terms of gates. This approach works well for simple circuits where the number of gates is small because the designer can instantiate and connect each gate individually. Also this level is very intuitive to the designer because of the direct correspondence between the circuit diagram and the Verilog description.
If the design is very large then a higher level of abstraction should be used above gate level. Data Flow allows the designer to design a circuit in terms of the data flow between registers and how a design processes data rather than the implementation of individual gates. The design tools would then convert the description to a gate level circuit.
This level of abstraction allows designers to describe the circuit's functionality in an algorithmic way. That is the designer describes the behaviour of the circuit without any consideration to how the circuit transfers to hardware.
Note that the term RTL (Register Transfer Level) is commonly used for a combination of Data Flow and Behavioural modeling.

3.1.2 Types of Coding

Verilog HDL allows circuits to be described in two ways, structuraly (how they are built), or in terms of behaviour, (what they do). It is important to remember that when you desribe your circuits behaviour rather than its structure then the systhesis tool has to translate the description into logic. The synthesis tool although very good at its job may not always translate the description into the logic you intended, and also may not produce the most efficient circuit possible. For these reasons it is useful to have an idea of how your description will be translated into logic. Unit 5 covers some typical Verilog circuits and how they translate into hardware.

The two types of coding styles in Verilog:
  • Structural or Continuous - used for primitive descriptions or data flow without storage

 and(q, a, b);          //structural assignment using Verilog primitive gate       
 assign q = a & b | c;    //continuous assignment
  • Behavioural or Procedural - can be used to describe circuits with storage, and / or combinational logic. This type of coding will be covered in more detail later.

 always@(posedge clock)                                  
 begin
  count <= count + 1;
  if(a)
   z <= b;
    end

3.2 Module Components

The main component of a Verilog design block is a module. A simple module construction is shown below and illustrates the three levels of abstraction:

module my_new_module(a, b, z);                         

 input a, b;
 output z;
 wire d, q;
 reg z;

 //Gate Level
 and(q, a, b);

 //Data Flow
 assign d = a & b;

 //Behavioural
 reg z;
 always@(*)
 begin
  if(a)
   z <= b;
    end

endmodule

3.2.1 Interface Specification


module my_new_module(clock, a, b, z);                         

 input clock, a, b;
 output z;
 wire d, q;
 reg z;

 //Gate Level
 and(q, a, b);

 //Data Flow
 assign d = a & b;

 //Behavioural
 reg z;
 always@(posedge clock)
 begin
  if(a)
   z <= b;
    end

endmodule

The interface section requires two keywords module and endmodule as highlighted above. The name of the module follows the module statement. All the interface signals (ports) to and from the module follow the module name and are enclosed in parenthesis. If this is the top-level module then these signals will correspond to the relevant FPGA device pins, otherwise they will be internal signals to the design module and will interface to a higher level module.

3.2.2 Declaration


module my_new_module(clock, a, b, z);                         

 input clock, a, b;
 output z;
 wire d, q;
 reg z;

 //Gate Level
 and(q, a, b);

 //Data Flow
 assign d = a & b;

 //Behavioural
 reg z;
 always@(posedge clock)
 begin
  if(a)
   z <= b;
    end

endmodule
The declaration section of the module specifies the port directions, and any internal signals. Port directions can be input, output, or inout. Also any internal declarations are made here such as variables, wires, and nets.

3.2.3 Implementation


module my_new_module(clock, a, b, z);                         

 input clock, a, b;
 output z;
 wire d, q;
 reg z;

 //Gate Level
 and(q, a, b);

 //Data Flow
 assign d = a & b;

 //Behavioural
 reg z;
 always@(posedge clock)
 begin
  if(a)
   z <= b;
    end

endmodule
This section of the module describes the functionality of the circuit using either individual or combinations of gate level, data flow, and behavioural modeling.

3.2.4 Comments

Comment are specified in two ways. One uses a double forward slash // and the other a combination of /* and */. The usage of these is shown below:

assign a = b;   //this is a single line comment                

/* This is a block
comment
that can continue on more than one line */


3.3 Hierarchy

Verilog allows more complex modules to be created using simpler modules. This is called instantiation and has several benefits:
  • Allows a hierarchical structure to be created and so makes the overall design more understandable and readable
  • Allows re-use of modules
  • If a bug is found in a lower level module that is used (instantiated) throughout the design then only the one module needs to be modified and this change will be reflected throughout the whole design
  • Allows a large complex design to be divided (partitioned) into lower level modules that can then be distributed between a team of designers
  • Allows quick individual testing of these simpler lower level modules
Therefore a typical Verilog design consists of one top-level module and one or many lower-level modules. The top-level module contains a list of ports that connect to the outside world (pins on the device) and interconnections to the lower level modules.
Instantiation of a module is illustrated below and shows two methods to create an instance of a separate module:

module Test(in1, in2, in3, in4, sel, out);                         

 input in1, in2, in3, in4, sel;
 output out;
 wire out1, out2;
 
 
 //signals reference by order
 mux2to1 m1(in1, in2, sel, out1);
 
 //connections by name - order doesn't matter
 mux2to1 m2(.q(out2), .a(in4), .select(sel), .b(in3));

 assign out = out1 & out2;

endmodule


module mux2to1(a, b, select, q);                         

 input a, b, select;
 output q;
 
 reg q;

 always@(*)
 begin
  if(select)
   q <= a;
     else
   q <= b;
 end

endmodule

The lower level module is called mux2to1 and is instantiated twice in the module Test. The syntax is:
module_name instance_name_1 (port_connection_list);
In the first instance of the mux the port connection list is referenced by order. That is, the order of the signals from the instantiation (m1) must match the order of the module definition (mux2to1).
In the second instance the order of the port connection list doesn't matter because the signals are referenced in the instance. For example .a(in4) connects the 'a' signal in the definition to the 'in4' signal in the instantiation.
The above example compiled and viewed (RTL Viewer) using Altera Quartus II software is shown in Fig 1.

Figure 1 Altera RTL Viewer of Mux Instantiation Example

Figure 1 Altera RTL Viewer of Mux Instantiation Example


3.4 Built-in Logic Primitives

Verilog includes primitive logic gates as part of it's language. These are described below.

3.4.1 Basic Gates

Basic gates have a single output and any number of inputs. They consist of AND, NAND, OR, NOR, XOR, XNOR
The syntax is:- gate_name instance_name(output, input1, input2,......inputn);
Example:- and and1(out, in1, in2, in3);

3.4.2 Buffers

These primitives implement buffers and invertors. They have one input and any number of outputs. They consist of buf, and not.
The syntax is:- gate_name instance_name(output1, output2,.....outputn, input);
Example:- not not1(out1, out2, in);

3.4.3 Tri-state Buffers

These primitives implement 3-state buffers and invertors. They have one input and any number of outputs. Their output can be logic 1, logic 0, or high impedance depending on the input and the control signals. They consist of bufif0 and notif0 for an active low control signal, and bufif1 and notif1 for an active high control signal.
The syntax is:- gate_name instance_name(output1, output2,.....outputn, input);
Example:- notif1 notif1_1(out, in, control);
if control is low, out is high impedance, if control is high then out is the inverse of in.

3.5 Data Types

This section introduces the various data types available in Verilog.

3.5.1 Value Set

Verilog supports four values:
Value LevelCondition
0
Logic zero or False Condition
1
Logic 1 or True Condition
x
Unknown Logic Value
z
High Impedance or Floating State

3.5.2 Wires

Wires are used as connections between hardware elements and are treated just as wires in real circuits. The wire can be read or assigned to but does not store it's value. It must be driven by a continuous assignment or connected to the output of a gate or module. Other types of wires are wand, wor, and tri.
Wires are one bit values by default but can be declared as vectors as shown below.

wire a;   //declare single wire
wire a, c;  //declare two wires
wire d = 1'b0; //wire is fixed to logic value 0           
wire [7:0] A; //vector of 8 wires

Note:
Quartus does not require you to define one bit wires so the code Test1 will be ok and the wire Pulse1s will be defined automatically.


module Test1(CLK, Count);

    input CLK;
    output [7:0] Count;

    CLKDIV clkdiv( CLK, Pulse1s);
    COUNTER counter(CLK, Pulse1s, Count);          

end module
However if we require a multi-bit wire then we MUST declare it as shown in Test2.

module Test2(CLK, Decode);

    input CLK;
    output [7:0] Decode;

    wire [7:0] Count;

    CLKDIV clkdiv( CLK, Pulse1s);
    COUNTER counter(CLK, Pulse1s, Count);
    Decoder    decoder(CLK, Count, Decode);        

end module

3.5.3 Registers

Registers represent data storage elements and retain their value until another value is placed on them. They do not necessarily produce a physical register built from edge triggered flipflops in real circuits. In Verilog it is simply a variable that can hold it's value.

reg a;    //declare single 1-bit variable              
reg a, c;   //declare two 1-bit variables
reg [7:0] A, B; //two 8-bit variables

3.5.4 Input, Output, and Inout

These keywords declare the port direction of a module. The input and inout ports are of type wire. An output can be of type wire, wand, wor, tri, or reg. Examples of port declarations are given below.

 module port_example(a, b, c, d);            
  input a;  
  output b, c;
  output d;
  reg d;   //the output above also declared as a reg     

3.5.5 Integers

Integers are general purpose variables used for purposes such as counting. It is possible to use reg as a general purpose variable but a reg stores values as unsigned whereas integers store values as signed. If an integer holds numbers that are not defined at compile time their size will default to 32-bits. If they hold constants the synthesiser will adjust them to the minimum width needed at compile time.

3.5.6 Parameters

The keyword parameter allows constants to be defined in a module. Parameters cannot be used as variables. The most simplest use of a parameteris to give a constant value a meaningful description as shown below.

parameter bit_width 8;    //meaning full name for a constant

reg [bit_width-1:0] output_bus;
Parameters also have a much more powerful and useful feature that is they can be overridden individually at compile time by higher level modules. This is demonstrated in the following example code.

module Test(clk, out1, out2, out3);                         

 input clk;
 output out1, out2, out3;
 
 defparam t3.n = 4;

 timer      t1(clk, out1); 
 timer #(3) t2(clk, out2);
    timer      t3(clk, out3); 
 
endmodule


module timer(clkin, clkout);                         

 input clkin;
 output clkout;

 parameter n = 2; 

 reg [n:0] count;
 
 always@(posedge clkin)
 begin
  count  <= count + 1'b1;
 end

 assign clkout = count[n];
endmodule
Firstly a timer module timer is declared with a default parameter n set as 2. This causes the output of the module to act as a divide by 4 counter.
In the top-level module Test, three instances of timer are created. The first instance creates a timer with the default setting (divide by 4). The second instance overrides the default setting to 3 ( #(3) ) giving a divide by 8. And the third instance uses the defparam keyword to overwrite the default setting to 4, giving a divide by 16 timer.

3.6 Operators

Operators in Verilog are similar to operators found in other languages.
Operators can be used in both continuous and procedural assignments

3.6.1 Arithmetic

The list of arithmetic operators supported by Verilog are listed below:
  • + (addition)
  • - (subtraction)
  • * (multiplication)
  • / (division)
  • % (modulus)

3.6.2 Relational

Relational operators compare two operands and return a single bit. These operators synthesise into comparators.
  • < (less than)
  • <= (less than or equal to)
  • > (greater than)
  • >= (greater than or equal to)
  • == (equal to)
  • != (not equal to)

3.6.3 Bit-wise

Bit-wise operators carry out a bit-by-bit comparison of two operands.
  • ~ (bit-wise NOT)
  • & (bit-wise AND)
  • | (bit-wise OR)
  • ^ (bit-wise XOR)
  • ~^ (bit-wise XNOR)

3.6.4 Logical

Logical operators return a single bit. They can work on expressions, integers, and groups of bits. If any of the bits in the operand are non-zero then it is treated as a logic 1. Logic operators are typically used with if..else statements.
  • ! (Logical NOT)
  • && (Logical AND)
  • || (Logical OR)

3.6.5 Reduction

Reduction operators operate on all the bits of a vector. For instance if we have a vector [2:0] a. Then z = &a returns 1 if all the bits of 'a' are 1.
  • & (reduction AND)
  • | (reduction OR)
  • ~& (reduction NAND)
  • ~| (reduction NOR)
  • ^ (reduction XOR)
  • ~^ (reduction XNOR)

3.6.6 Shift

Shift operators shift the first operand by the number of bits specified in the second operand. Empty positions are filled with zeros.
  • << (shift left)
  • >> (shift right)

3.6.7 Concatenation

The concatenation operator combines two or more operands to form a larger vector.
{ } (concatenation)
Example if a = 0010 and b = 110010 then z = {a,b} gives z = '0010110010'

3.6.8 Replication

The replication operator makes multiple copies of an item.
{n{item}} (n times replication of item)
Example if a = 0010 then y = {2{a}} gives y = '00100010'

3.6.9 Conditional

The conditional operator returns one of two expressions based on a condition. This operator synthesises to a multiplexer.
(condition) ? result1 if true : result2 if false
Example z = (x) ? a : b returns a if x is true or returns b if x is false


3.7 Operands

3.7.1 Numbers

Unless defined by the designer the size of a Verilog number is 32-bits wide. The format of a Verilog number is size'base value. If no size is specified then the number is assumed to be 32-bit. All numbers are padded to the left with zeros if necessary. The available base formats are, b - binary, o - octal, d - decimal, and h - hexadecimal. Some examples follow.

476             //32 bit decimal
8'd255          //8 bit decimal
1'b0            //single bit binary
4'b0110         //4 bit binary
8'b1100         //8 bit binary  00001100                 
4'hb            //4 bit hex number 1011
'h9c            //32 bit hex

3.7.2 Bit Selects

Bit selects and part bus selects can be achieved in Verilog. The syntax is:
variable_name [index]
variable name[msb:lsb]
The following code snippet illustrates how this is done.

reg [7:0] a,b;     //declare the registers

        //single bit select
c = a[6] & b[3];    //AND bit 7 of 'a' with bit 4 of 'b'

        //part bit selects
sum = a[7:5] + b[2:0];   //add the 3 msb of 'a' to 3 lsb of 'b'


3.8 Compiler Directives

A compiler directive is used to control the compilation of a Verilog file. The grave accent mark, `, (usually to the left of the number 1 key) denotes a compiler directive. A directive is effective from the point at which it is declared to the point at which another directive overrides it, even across file boundaries. Compiler directives may appear anywhere in the source description, but it is recommended that they appear outside a module declaration. Some common directives are introduced below.
`include - This compiler directive lets you insert the entire contents of a source file into another file during Verilog compilation. The compilation proceeds as though the contents of the included source file appear in place of the `include command. You can use the `include compiler directive to include global or commonly-used definitions and tasks, without encapsulating repeated code within module boundaries.
`define - This compiler directive lets you create macros for text substitution and create macros to trigger the `ifdef compiler directive. You can use text macros both inside and outside module definitions
`undef - The `undef compiler directive lets you remove definitions of text macros created by the `define compiler directive. This use the `undef compiler directive to undefine a text macro that you use in more than one file
`ifdef...`else...`endif - Optionally includes lines of source code during compilation. The `ifdef directive checks that a macro has been defined, and if so, compiles the code that follows. If the macro has not been defined, the compiler compiles the code (if any) following the optional `else directive. You can control what code is compiled by choosing whether to define the text macro with `define. The `endif directive marks the end of the conditional code


3.9 Procedural Coding

3.9.1 always Procedural Assignments

Procedural assignments are always made in always blocks. Only reg and integer values can be assigned (placed on the left hand side of the = sign). The right hand side of the assignment is an expression that can use any of the operators described in section 3.6. The always block can be used to describe both combinational and sequential designs. If there is more than one line of code in the always block then this must be enclosed withinbegin....end keywords. These points are illustrated by the two code snippets below:

  //combinational design
  always @(*)
  begin
   statement1;                                      
   statement2;
  end

  //sequential design
  always @(posedge clock)
  begin
   statement1;                                      
   statement2;
  end

 

The always statement controls when the circuit described within the always block will be triggered. The terms included in parathesise following thealways& statement is called the sensitivity list. It is these terms that control the triggering of the circuit.
There are two types of sensitivity lists, level and edge. Level sensitivity lists allow us to describe combinational circuits. Edge sensitivity lists allow us to describe flip flop circuits.
if we are describing a combinational circuit then all the inputs to the circuit should be included in the sensitivity list. Consider an AndOrInvert circuit and the corresponding code shown in Fig 3.

Figure 3 AndOrInvert Circuit

Figure 3 AndOrInvert Circuit


  //combinational design
  always @(a, b, c, d)
  begin
   y = ~((a & b) | (c & d));
  end

As shown, all the circuit inputs, a, b, c, and d, are included in the sensitivity list.
Please note however; Verilog-2001 offers additional syntax for the sensitivity list which is:
always@(*)
This creates an implicit sensitivity list that contains all the signals whose values are read in the statements of the always block.


An edge triggered circuit can either be positive edge of negative edge.
  • positive edge trigger - always@(posedge CLK)
  • negative edge trigger - always@(negedge CLK)
Note: To ensure a design is fully synchronous do not mix the two edge triggered options.
Figure 4 shows a circuit using a flipflop and the corresponding code.

Figure 4 - Mux Circuit with FlipFlop Output



  
  always @(posedge CLK)
  begin
   if(s)
    y <= a;
   else
    y <= b;
  end


3.9.2 Blocking and Non-blocking Assignments

Two types of assignments can be made in a procedural block, blocking (=) and non-blocking (<=). With blocking assignments each one is 'processed' sequentially in the order they are written. with non-blocking assignments the operations are carried out in parallel. The two types of assignment are illustrated below. Fig 3 and 4 show the circuits created when changing the order of the blocking statements. Fig 5 and 6 show how changing the order of non-blocking makes no difference to the synthesised circuit.

Figure 3 Use of blocking assignments to create a shift register


  //blocking assignments
  always @(posedge clock)
  begin
   C = B; //this assignment is done first
   B = A;  //this assignment is done second
  end
Figure 3 Use of blocking assignments to create a shift register

Figure 4 Use of blocking assignments to create a parallel flip flop


 always@(posedge clk)
 begin
  B = A;
  C = B;
 end
Figure 4 Use of blocking assignments to create a parallel flip flop

Figure 5 Use of non-blocking assignments to create a shift register


 always@(posedge clk)
 begin
  C <= B;   //These two lines are 
  B <= A;   //executed in parallel
 end
Figure 5 Use of non-blocking assignments to create a shift register

Figure 6 Again the use of non-blocking assignments to create a shift register


 always@(posedge clk)
 begin
  B <= A;   //These two lines are 
  C <= B;   //executed in parallel
 end

Figure 6 Again the use of non-blocking assignments to create a shift register

3.9.3 begin...end

As previously mentioned begin....end statements are used to group several statements together in places such as always blocks and ifcase, andfor statements. If only one statement follows these statements then the begin...end can be omitted.

3.9.4 for Loops

This is the same as a for loop in C, where it is used to repeatedly execute a statement/s. Do not forget however that this is not a processor and the statements are not sequentially executed on the hardware, it is a hardware description of a circuit. It is a neat concise method of describing your circuit. The following example code snippet and generated hardware circuit shown in Fig 7 should re-enforce this.

Figure 7 for Loop code example and generated hardware circuit


 module Test(a, b, c);

  input [7:0] a,b;
  output [7:0] c;

  reg [7:0] c;

  integer index = 8'd0;
  parameter low_range = 8'd0;
    parameter high_range = 8'd7;
  parameter step = 8'd1;

  always@(*)
  begin
   for (index = 0; index < high_range+1; index = index + 1)
   begin
    c[index] = a[index] & b[index];
   end
  end
 endmodule
Figure 7 for Loop code example and generated hardware circuit

3.9.5 if...else if...else

The if...else statements execute a statement or a block of statements following the if depending on the result of an expression. The if can be used on its own, with an else or using an else if with a second expression. The syntax of these three methods are shown below:

//a pure if expression
if(expression)
begin
 statement1
 statement2
end

//if...else if expression
if(expression1)
begin
 statement1
 statement2
end
else if(expresssion2)
begin
 statement3
 statement4
end

//if...else expression
if(expression)
begin
 statement1
 statement2
end
else
begin
 statement3
 statement4
end

3.9.6 case Statements

The case statement allows a multi-path branch to be executed depending on the expression and the list of case choices. A default alternative can be included that will be executed if none of the other alternatives are matched. The syntax and code snippet is illustrated below:

syntax:
 case (expression)
  alternative1:  statement1;
  alternative2:  statement2;
  alternative3:  statement3;
  default:  default_statement;
 endcase    

code snippet:
 case(alu_control)
  2'b00:   y = a+b;
  2'b01:   y = a-d;
  2'b10:   y = a&b;
  default:  y = 0;
 endcase

    


Updated 29/07/2008 KS

Site Search

 
  
Powered by Google
Site Map

沒有留言:

張貼留言

WOKWI DHT22 & LED , Node-Red + SQLite database

 WOKWI DHT22 & LED , Node-Red + SQLite database Node-Red程式 [{"id":"6f0240353e534bbd","type":"comment&...