✔ Đào Tạo Kỹ Sư Thiết Kế Vi Mạch cùng Semicon

Verilog


Verilog Basics
Part-I


../images/main/bullet_green_ball.gif Introduction

 
Every new learner's dream is to understand Verilog in one day, at least enough to use it. The next few pages are my attempt to make this dream a reality. There will be some theory and examples followed by some exercises. This tutorial will not teach you how to program; it is designed for those with some programming experience. Even though Verilog executes different code blocks concurrently as opposed to the sequ
ential execution of most programming languages, there are still many parallels. Some background in digital design is also helpful.
          
Life before Verilog was a life full of schematics. Every design, regardless of complexity, was designed through schematics. They were difficult to verify and error-prone, resulting in long, tedious development cycles of design, verification... design, verification... design, verification...

When Verilog arrived, we suddenly had a different way of thinking about logic circuits. The Verilog design cycle is more like a traditional programming one, and it is what this tutorial will walk you through. Here's how it goes:
  •     Specifications (specs)
  •     High level design
  •     Low level (micro) design
  •     RTL coding
  •     Verification
  •     Synthesis.       
First on the list is specifications - what are the restrictions and requirements we will place on our design? What are we trying to build? For this tutorial, we'll be building a two agent arbiter: a device that selects among two agents competing for mastership. Here are some specs we might write up.       
  •     Two agent arbiter.
  •     Active high asynchronous reset.
  •     Fixed priority, with agent 0 having priority over agent 1
  •     Grant will be asserted as long as request is asserted.
Once we have the specs, we can draw the block diagram, which is basically an abstraction of the data flow through a system (what goes into or comes out of the black boxes?). Since the example that we have taken is a simple one, we can have a block diagram as shown below. We don't worry about what's inside the magical black boxes just yet.

../images/main/bulllet_4dots_orange.gif Block diagram of arbiter

 

Now, if we were designing this machine without Verilog, the standard procedure would dictate that we draw a state machine. From there, we'd make a truth table with state transitions for each flip-flop. And after that we'd draw Karnaugh maps, and from K-maps we could get the optimized circuit. This method works just fine for small designs, but with large designs this flow becomes complicated and error prone. This is where Verilog comes in and shows us another way.

../images/main/bulllet_4dots_orange.gif Low level design

To see how Verilog helps us design our arbiter, let's go on to our state machine - now we're getting into the low-level design and peeling away the cover of the previous diagram's black box to see how our inputs affect the machine.




Each of the circles represents a state that the machine can be in. Each state corresponds to an output. The arrows between the states are state transitions, labeled by the event that causes the transition. For instance, the leftmost orange arrow means that if the machine is in state GNT0 (outputting the signal that corresponds to GNT0) and receives an input of !req_0, the machine moves to state IDLE and outputs the signal that corresponds to that. This state machine describes all the logic of the system that you'll need. The next step is to put it all in Verilog.

 ../images/main/bullet_green_ball.gif Modules
  We'll need to backtrack a bit to do this. If you look at the arbiter block in the first picture, we can see that it has got a name ("arbiter") and input/output ports (req_0, req_1, gnt_0, and gnt_1).
           
Since Verilog is a HDL (hardware description language - one used for the conceptual design of integrated circuits), it also needs to have these things. In Verilog, we call our "black boxes" module. This is a reserved word within the program used to refer to things with inputs, outputs, and internal logic workings; they're the rough equivalents of functions with returns in other programming languages.


../images/main/bulllet_4dots_orange.gif Code of module "arbiter"

If you look closely at the arbiter block we see that there are arrow marks, (incoming for inputs and outgoing for outputs). In Verilog, after we have declared the module name and port names, we can define the direction of each port. (version note: In Verilog 2001 we can define ports and port directions at the same time) The code for this is shown below.
             

  1  module arbiter (
  2  // Two slashes make a comment line.
  3  clock      , // clock
  4  reset      , // Active high, syn reset
  5  req_0      , // Request 0
  6  req_1      , // Request 1
  7  gnt_0      , // Grant 0
  8  gnt_1        // Grant 1
  9  );
 10  //-------------Input Ports-----------------------------
 11  // Note : all commands are semicolon-delimited
 12  input           clock               ;
 13  input           reset               ;
 14  input           req_0               ;
 15  input           req_1               ;
 16  //-------------Output Ports----------------------------
 17  output        gnt_0                 ;
 18  output        gnt_1                 ;
 
Here we have only two types of ports, input and output. In real life, we can have bi-directional ports as well. Verilog allows us to define bi-directional ports as "inout."
          
Bi-Directional Ports Example -

inout read_enable; // port named read_enable is bi-directional
           
How do you define vector signals (signals composed of sequences of more than one bit)? Verilog provides a simple way to define these as well.
           
Vector Signals Example -
    
inout [7:0] address; //port "address" is bidirectional
           
Note the [7:0] means we're using the little-endian convention - you start with 0 at the rightmost bit to begin the vector, then move to the left. If we had done [0:7], we would be using the big-endian convention and moving from left to right. Endianness is a purely arbitrary way of deciding which way your data will "read," but does differ between systems, so using the right endianness consistently is important. As an analogy, think of some languages (English) that are written left-to-right (big-endian) versus others (Arabic) written right-to-left (little-endian). Knowing which way the language flows is crucial to being able to read it, but the direction of flow itself was arbitrarily set years back.
           
Summary

  •     We learnt how a block/module is defined in Verilog.
  •     We learnt how to define ports and port directions.
  •     We learnt how to declare vector/scalar ports.

../images/main/bullet_green_ball.gif Data Type



 What do data types have to do with hardware? Nothing, actually. People just wanted to write one more language that had data types in it. It's completely gratuitous; there's no point.
           
But wait... hardware does have two kinds of drivers.
           
(Drivers? What are those?)
           
A driver is a data type which can drive a load. Basically, in a physical circuit, a driver would be anything that electrons can move through/into.           

  •     Driver that can store a value (example: flip-flop).
  •     Driver that can not store value, but connects two points (example: wire).
The first type of driver is called a reg in Verilog (short for "register"). The second data type is called a wire (for... well, "wire"). You can refer to tidbits section to understand it better.

There are lots of other data types - for instance, registers can be signed, unsigned, floating point... as a newbie, don't worry about them right now.
           
Examples :
           
wire and_gate_output; // "and_gate_output" is a wire that only outputs

reg d_flip_flop_output; // "d_flip_flop_output" is a register; it stores and outputs a value
reg [7:0] address_bus; // "address_bus" is a little-endian 8-bit register

Summary

  •     Wire data type is used for connecting two points.
  •     Reg data type is used for storing values.
  •     May god bless the rest of data types. You'll see them someday.



../images/main/bullet_green_ball.gif Operators

 Operators, thankfully, are the same things here as they are in other programming languages. They take two values and compare (or otherwise operate on) them to yield a third result - common examples are addition, equals, logical-and... To make life easier for us, nearly all operators (at least the ones in the list below) are exactly the same as their counterparts in the C programming language.



Example -
  •     a = b + c ; // That was very easy
  •     a = 1 << 5; // Hum let me think, ok shift '1' left by 5 positions.
  •     a = !b ; // Well does it invert b???
  •     a = ~b ; // How many times do you want to assign to 'a', it could cause multiple-drivers.
       
Summary           
  •     Let's attend the C language training again, they're (almost) just like the C ones.



Verilog Basics
Part-II


../images/main/bullet_green_ball.gif Control Statements

Wait, what's this? if, else, repeat, while, for, case - it's Verilog that looks exactly like C (and probably whatever other language you're used to program in)! Even though the functionality appears to be the same as in C, Verilog is an HDL, so the descriptions should translate to hardware. This means you've got to be careful when using control statements (otherwise your designs might not be implementable in hardware). 
 
../images/main/bulllet_4dots_orange.gif If-else

If-else statements check a condition to decide whether or not to execute a portion of code. If a condition is satisfied, the code is executed. Else, it runs this other portion of code.
 
  1 // begin and end act like curly braces in C/C++.
  2 if (enable == 1'b1) begin
  3   data = 10; // Decimal assigned
  4   address = 16'hDEAD; // Hexadecimal
  5   wr_enable = 1'b1; // Binary 
  6 end else begin
  7   data = 32'b0;
  8   wr_enable = 1'b0;
  9   address = address + 1; 
10 end

One could use any operator in the condition checking, as in the case of C language. If needed we can have nested if else statements; statements without else are also ok, but they have their own problem, when modeling combinational logic, in case they result in a Latch (this is not always true).

../images/main/bulllet_4dots_orange.gif Case

Case statements are used where we have one variable which needs to be checked for multiple values. like an address decoder, where the input is an address and it needs to be checked for all the values that it can take. Instead of using multiple nested if-else statements, one for each value we're looking for, we use a single case statement: this is similar to switch statements in languages like C++.
           
Case statements begin with the reserved word case and end with the reserved word endcase (Verilog does not use brackets to delimit blocks of code). The cases, followed with a colon and the statements you wish executed, are listed within these two delimiters. It's also a good idea to have a default case. Just like with a finite state machine (FSM), if the Verilog machine enters into a non-covered statement, the machine hangs. Defaulting the statement with a return to idle keeps us safe.
           
 1 case(address)
 2   0 : $display ("It is 11:40PM");
 3   1 : $display ("I am feeling sleepy");
 4   2 : $display  ("Let me skip this tutorial");
 5   default : $display  ("Need to complete");
 6 endcase


Looks like the address value was 3 and so I am still writing this tutorial.

Note: One thing that is common to if-else and case statement is that, if you don't cover all the cases (don't have 'else' in If-else or 'default' in Case), and you are trying to write a combinational statement, the synthesis tool will infer Latch.

../images/main/bulllet_4dots_orange.gif While

A while statement executes the code within it repeatedly if the condition it is assigned to check returns true. While loops are not normally used for models in real life, but they are used in test benches. As with other statement blocks, they are delimited by begin and end.
           
 1 while (free_time) begin
 2  $display ("Continue with webpage development");
 3 end


As long as free_time variable is set, code within the begin and end will be executed. i.e print "Continue with web development". Let's looks at a stranger example, which uses most of Verilog constructs. Well, you heard it right. Verilog has fewer reserved words than VHDL, and in this few, we use even lesser for actual coding. So good of Verilog... so right.

  1 module counter (clk,rst,enable,count);
  2 input clk, rst, enable;
  3 output [3:0] count;
  4 reg [3:0] count;
  5                      
  6 always @ (posedge clk or posedge rst)
  7 if (rst) begin
  8   count <= 0;
  9 end else begin : COUNT
10   while (enable) begin
11     count <= count + 1;
12     disable COUNT;
13   end
14 end
15 
16 endmodule

The example above uses most of the constructs of Verilog. You'll notice a new block called always - this illustrates one of the key features of Verilog. Most software languages, as we mentioned before, execute sequentially - that is, statement by statement. Verilog programs, on the other hand, often have many statements executing in parallel. All blocks marked always will run - simultaneously - when one or more of the conditions listed within it is fulfilled.
           
In the example above, the always block will run when either rst or clk reaches a positive edge - that is, when their value has risen from 0 to 1. You can have two or more always blocks in a program going at the same time (not shown here, but commonly used).      

We can disable a block of code, by using the reserve word disable. In the above example, after each counter increment, the COUNT block of code (not shown here) is disabled.

../images/main/bulllet_4dots_orange.gif For loop

For loops in Verilog are almost exactly like for loops in C or C++. The only difference is that the ++ and -- operators are not supported in Verilog. Instead of writing i++ as you would in C, you need to write out its full operational equivalent, i = i + 1.

 1      for (i = 0; i < 16; i = i +1) begin
 2             $display ("Current value of i is %d", i);
 3       end


This code will print the numbers from 0 to 15 in order. Be careful when using for loops for register transfer logic (RTL) and make sure your code is actually sanely implementable in hardware... and that your loop is not infinite.

../images/main/bulllet_4dots_orange.gif Repeat

Repeat is similar to the for loop we just covered. Instead of explicitly specifying a variable and incrementing it when we declare the for loop, we tell the program how many times to run through the code, and no variables are incremented (unless we want them to be, like in this example).
           
 1 repeat (16) begin
 2   $display ("Current value of i is %d", i);
 3   i = i + 1;
 4 end


The output is exactly the same as in the previous for-loop program example. It is relatively rare to use a repeat (or for-loop) in actual hardware implementation.

../images/main/bulllet_4dots_orange.gif
Summary
  •     While, if-else, case(switch) statements are the same as in C language.
  •     If-else and case statements require all the cases to be covered for combinational logic.
  •     For-loop is the same as in C, but no ++ and -- operators.
  •     Repeat is the same as the for-loop but without the incrementing variable.

 ../images/main/bullet_green_ball.gif Variable Assignment

In digital there are two types of elements, combinational and sequential. Of course we know this. But the question is "How do we model this in Verilog ?". Well Verilog provides two ways to model the combinational logic and only one way to model sequential logic.
  •     Combinational elements can be modeled using assign and always statements.
  •     Sequential elements can be modeled using only always statement.
  •     There is a third block, which is used in test benches only: it is called Initial statement.
 ../images/main/bulllet_4dots_orange.gif Initial Blocks

An initial block, as the name suggests, is executed only once when simulation starts. This is useful in writing test benches. If we have multiple initial blocks, then all of them are executed at the beginning of simulation.
           
Example
      
 1 initial begin
 2             clk = 0;
 3             reset = 0;
 4             req_0 = 0;
 5             req_1 = 0;
 6 end


In the above example, at the beginning of simulation, (i.e. when time = 0), all the variables inside the begin and end block are driven zero.

Go on to the next page for the discussion of assign and always statements.


Verilog Basics
Part-III


../images/main/bullet_green_ball.gif Always Blocks

As the name suggests, an always block executes always, unlike initial blocks which execute only once (at the beginning of simulation). A second difference is that an always block should have a sensitive list or a delay associated with it.

The sensitive list is the one which tells the always block when to execute the block of code, as shown in the figure below. The @ symbol after reserved word ' always', indicates that the block will be triggered "at" the condition in parenthesis after symbol @.

One important note about always block: it can not drive wire data type, but can drive reg and integer data types.

 1 always  @ (a or b or sel)
 2 begin
 3   y = 0;
 4   if (sel == 0) begin
 5     y = a;
 6   end else begin
 7     y = b;
 8   end
 9 end


The above example is a 2:1 mux, with input a and b; sel is the select input and y is the mux output. In any combinational logic, output changes whenever input changes. This theory when applied to always blocks means that the code inside always blocks needs to be executed whenever the input variables (or output controlling variables) change. These variables are the ones included in the sensitive list, namely a, b and sel.
           
There are two types of sensitive list: level sensitive (for combinational circuits) and edge sensitive (for flip-flops). The code below is the same 2:1 Mux but the output y is now a flip-flop output.

 1 always  @ (posedge clk )
 2 if (reset == 0) begin
 3   y <= 0;
 4 end else if (sel == 0) begin
 5   y <= a;
 6 end else begin
 7   y <= b;
 8 end


We normally have to reset flip-flops, thus every time the clock makes the transition from 0 to 1 (posedge), we check if reset is asserted (synchronous reset), then we go on with normal logic. If we look closely we see that in the case of combinational logic we had "=" for assignment, and for the sequential block we had the "<=" operator. Well, "=" is blocking assignment and "<=" is nonblocking assignment. "=" executes code sequentially inside a begin / end, whereas nonblocking "<=" executes in parallel.

We can have an always block without sensitive list, in this case we need to have a delay as shown in the code below.

 1 always  begin
 2    #5  clk = ~clk;
 3 end


#5 in front of the statement delays its execution by 5 time units.

 ../images/main/bulllet_4dots_orange.gif Assign Statement

An assign statement is used for modeling only combinational logic and it is executed continuously. So the assign statement is called 'continuous assignment statement' as there is no sensitive list.

 1 assign out = (enable) ? data : 1'bz;

The above example is a tri-state buffer. When enable is 1, data is driven to out, else out is pulled to high-impedance. We can have nested conditional operators to construct mux, decoders and encoders.
           
 1 assign out = data;

You could download file one_day13.v here

This example is a simple buffer.


../images/main/bullet_green_ball.gif Task and Function

When repeating the same old things again and again, Verilog, like any other programming language, provides means to address repeated used code, these are called Tasks and Functions. I wish I had something similar for webpages, just call it to print this programming language stuff again and again.

Code below is used for calculating even parity.   

  1 function parity;
  2 input [31:0] data;
  3 integer i;
  4 begin
  5   parity = 0;
  6   for (i = 0; i < 32; i = i + 1) begin
  7     parity = parity ^ data[i];
  8   end
  9 end
10 endfunction

Functions and tasks have the same syntax; one difference is that tasks can have delays, whereas functions can not have any delay. This means that function can be used for modeling combinational logic.

A second difference is that functions can return a value, whereas tasks can not.

 

 Verilog Basics
Part-III

 ../images/main/bullet_green_ball.gif Test Benches

Ok, we have code written according to the design document, now what?

Well we need to test it to see if it works according to specs. Most of the time, it's the same we use to do in digital labs in college days: drive the inputs, match the outputs with expected values. Let's look at the arbiter testbench.

  1 module arbiter (
  2 clock,
  3 reset,
  4 req_0,
  5 req_1,
  6 gnt_0,
  7 gnt_1
  8 );
  9 

10 input clock, reset, req_0, req_1;
11 output gnt_0, gnt_1;
12 
13 reg gnt_0, gnt_1;
14 
15 always @ (posedge clock or posedge reset)
16 if (reset) begin
17  gnt_0 <= 0;
18  gnt_1 <= 0;
19 end else if (req_0) begin
20   gnt_0 <= 1;
21   gnt_1 <= 0;
22 end else if (req_1) begin
23   gnt_0 <= 0;
24   gnt_1 <= 1;
25 end
26 
27 endmodule
28 // Testbench Code Goes here
29 module arbiter_tb;
30 
31 reg clock, reset, req0,req1;
32 wire gnt0,gnt1;
33 
34 initial begin
35   $monitor ("req0=%b,req1=%b,gnt0=%b,gnt1=%b", req0,req1,gnt0,gnt1);
36   clock = 0;
37   reset = 0;
38   req0 = 0;
39   req1 = 0;
40    #5  reset = 1;
41    #15  reset = 0;
42    #10  req0 = 1;
43    #10  req0 = 0;
44    #10  req1 = 1;
45    #10  req1 = 0;
46    #10  {req0,req1} = 2'b11;
47    #10  {req0,req1} = 2'b00;
48    #10  $finish;
49 end
50 
51 always begin
52   #5  clock =  ! clock;
53 end
54 
55 arbiter U0 (
56 .clock (clock),
57 .reset (reset),
58 .req_0 (req0),
59 .req_1 (req1),
60 .gnt_0 (gnt0),
61 .gnt_1 (gnt1)
62 );
63 
64 endmodule

It looks like we have declared all the arbiter inputs as reg and outputs as wire; well, that's true. We are doing this as test bench needs to drive inputs and needs to monitor outputs.

After we have declared all needed variables, we initialize all the inputs to known state: we do that in the initial block. After initialization, we assert/de-assert reset, req0, req1 in the sequence we want to test the arbiter. Clock is generated with an always block.
           
After we are done with the testing, we need to stop the simulator. Well, we use $finish to terminate simulation. $monitor is used to monitor the changes in the signal list and print them in the format we want.


 req0=0,req1=0,gnt0=x,gnt1=x
 req0=0,req1=0,gnt0=0,gnt1=0
 req0=1,req1=0,gnt0=0,gnt1=0
 req0=1,req1=0,gnt0=1,gnt1=0
 req0=0,req1=0,gnt0=1,gnt1=0
 req0=0,req1=1,gnt0=1,gnt1=0
 req0=0,req1=1,gnt0=0,gnt1=1
 req0=0,req1=0,gnt0=0,gnt1=1
 req0=1,req1=1,gnt0=0,gnt1=1
 req0=1,req1=1,gnt0=1,gnt1=0
 req0=0,req1=0,gnt0=1,gnt1=0


I have used Icarus Verilog simulator to generate the above output.



Nguồn asic-world

Không có nhận xét nào:

Đăng nhận xét