[ Team LiB ] 7.2 Procedural Assignments Procedural assignments update values of reg, integer, real, or time variables. The value placed on a variable will remain unchanged until another procedural assignment updates the variable with a different value. These are unlike continuous assignments discussed in Chapter 6 , Dataflow Modeling, where one assignment statement can cause the value of the right-hand-side expression to be continuously placed onto the left-hand-side net. The syntax for the simplest form of procedural assignment is shown below. assignment ::= variable_lvalue = [ delay_or_event_control ] expression The left-hand side of a procedural assignment <lvalue> can be one of the following: • A reg, integer, real, or time register variable or a memory element • A bit select of these variables (e.g., addr[0]) • A part select of these variables (e.g., addr[31:16]) • A concatenation of any of the above The right-hand side can be any expression that evaluates to a value. In behavioral modeling, all operators listed in Table 6-1 on page 96 can be used in behavioral expressions. There are two types of procedural assignment statements: blocking and nonblocking. 7.2.1 Blocking Assignments Blocking assignment statements are executed in the order they are specified in a sequential block. A blocking assignment will not block execution of statements that follow in a parallel block. Both parallel and sequential blocks are discussed in Section 7.7, Sequential and Parallel Blocks. The = operator is used to specify blocking assignments. Example 7-6 Blocking Statements reg x, y, z; reg [15:0] reg_a, reg_b; integer count; //All behavioral statements must be inside an initial or always block initial begin x = 0; y = 1; z = 1; //Scalar assignments count = 0; //Assignment to integer variables reg_a = 16'b0; reg_b = reg_a; //initialize vectors #15 reg_a[2] = 1'b1; //Bit select assignment with delay #10 reg_b[15:13] = {x, y, z} //Assign result of concatenation to // part select of a vector count = count + 1; //Assignment to an integer (increment) end In Example 7-6 , the statement y = 1 is executed only after x = 0 is executed. The behavior in a particular block is sequential in a begin-end block if blocking statements are used, because the statements can execute only in sequence. The statement count = count + 1 is executed last. The simulation times at which the statements are executed are as follows: • All statements x = 0 through reg_b = reg_a are executed at time 0 • Statement reg_a[2] = 0 at time = 15 • Statement reg_b[15:13] = {x, y, z} at time = 25 • Statement count = count + 1 at time = 25 • Since there is a delay of 15 and 10 in the preceding statements, count = count + 1 will be executed at time = 25 units Note that for procedural assignments to registers, if the right-hand side has more bits than the register variable, the right-hand side is truncated to match the width of the register variable. The least significant bits are selected and the most significant bits are discarded. If the right-hand side has fewer bits, zeros are filled in the most significant bits of the register variable. 7.2.2 Nonblocking Assignments Nonblocking assignments allow scheduling of assignments without blocking execution of the statements that follow in a sequential block. A <= operator is used to specify nonblocking assignments. Note that this operator has the same symbol as a relational operator, less_than_equal_to. The operator <= is interpreted as a relational operator in an expression and as an assignment operator in the context of a nonblocking assignment. To illustrate the behavior of nonblocking statements and its difference from blocking statements, let us consider Example 7-7 , where we convert some blocking assignments to nonblocking assignments, and observe the behavior. Example 7-7 Nonblocking Assignments reg x, y, z; reg [15:0] reg_a, reg_b; integer count; //All behavioral statements must be inside an initial or always block initial begin x = 0; y = 1; z = 1; //Scalar assignments count = 0; //Assignment to integer variables reg_a = 16'b0; reg_b = reg_a; //Initialize vectors reg_a[2] <= #15 1'b1; //Bit select assignment with delay reg_b[15:13] <= #10 {x, y, z}; //Assign result of concatenation //to part select of a vector count <= count + 1; //Assignment to an integer (increment) end In this example, the statements x = 0 through reg_b = reg_a are executed sequentially at time 0. Then the three nonblocking assignments are processed at the same simulation time. 1. reg_a[2] = 0 is scheduled to execute after 15 units (i.e., time = 15) 2. reg_b[15:13] = {x, y, z} is scheduled to execute after 10 time units (i.e., time = 10) 3. count = count + 1 is scheduled to be executed without any delay (i.e., time = 0) Thus, the simulator schedules a nonblocking assignment statement to execute and continues to the next statement in the block without waiting for the nonblocking statement to complete execution. Typically, nonblocking assignment statements are executed last in the time step in which they are scheduled, that is, after all the blocking assignments in that time step are executed. In the example above, we mixed blocking and nonblocking assignments to illustrate their behavior. However, it is recommended that blocking and nonblocking assignments not be mixed in the same always block. Application of nonblocking assignments Having described the behavior of nonblocking assignments, it is important to understand why they are used in digital design. They are used as a method to model several concurrent data transfers that take place after a common event. Consider the following example where three concurrent data transfers take place at the positive edge of clock. always @(posedge clock) begin reg1 <= #1 in1; reg2 <= @(negedge clock) in2 ^ in3; reg3 <= #1 reg1; //The old value of reg1 end At each positive edge of clock, the following sequence takes place for the nonblocking assignments. 1. A read operation is performed on each right-hand-side variable, in1, in2, in3, and reg1, at the positive edge of clock. The right-hand-side expressions are evaluated, and the results are stored internally in the simulator. 2. The write operations to the left-hand-side variables are scheduled to be executed at the time specified by the intra-assignment delay in each assignment, that is, schedule "write" to reg1 after 1 time unit, to reg2 at the next negative edge of clock, and to reg3 after 1 time unit. 3. The write operations are executed at the scheduled time steps. The order in which the write operations are executed is not important because the internally stored right-hand-side expression values are used to assign to the left-hand-side values. For example, note that reg3 is assigned the old value of reg1 that was stored after the read operation, even if the write operation wrote a new value to reg1 before the write operation to reg3 was executed. Thus, the final values of reg1, reg2, and reg3 are not dependent on the order in which the assignments are processed. To understand the read and write operations further, consider Example 7-8 , which is intended to swap the values of registers a and b at each positive edge of clock, using two concurrent always blocks. Example 7-8 Nonblocking Statements to Eliminate Race Conditions //Illustration 1: Two concurrent always blocks with blocking //statements always @(posedge clock) a = b; always @(posedge clock) b = a; //Illustration 2: Two concurrent always blocks with nonblocking //statements always @(posedge clock) a <= b; always @(posedge clock) b <= a; In Example 7-8 , in Illustration 1, there is a race condition when blocking statements are used. Either a = b would be executed before b = a, or vice versa, depending on the simulator implementation. Thus, values of registers a and b will not be swapped. Instead, both registers will get the same value (previous value of a or b), based on the Verilog simulator implementation. However, nonblocking statements used in Illustration 2 eliminate the race condition. At the positive edge of clock, the values of all right-hand-side variables are "read," and the right-hand-side expressions are evaluated and stored in temporary variables. During the write operation, the values stored in the temporary variables are assigned to the left-hand- side variables. Separating the read and write operations ensures that the values of registers a and b are swapped correctly, regardless of the order in which the write operations are performed. Example 7-9 shows how nonblocking assignments shown in Illustration 2 could be emulated using blocking assignments. Example 7-9 Implementing Nonblocking Assignments using Blocking Assignments //Emulate the behavior of nonblocking assignments by //using temporary variables and blocking assignments always @(posedge clock) begin //Read operation //store values of right-hand-side expressions in temporary variables temp_a = a; temp_b = b; //Write operation //Assign values of temporary variables to left-hand-side variables a = temp_b; b = temp_a; end For digital design, use of nonblocking assignments in place of blocking assignments is highly recommended in places where concurrent data transfers take place after a common event. In such cases, blocking assignments can potentially cause race conditions because the final result depends on the order in which the assignments are evaluated. Nonblocking assignments can be used effectively to model concurrent data transfers because the final result is not dependent on the order in which the assignments are evaluated. Typical applications of nonblocking assignments include pipeline modeling and modeling of several mutually exclusive data transfers. On the downside, nonblocking assignments can potentially cause a degradation in the simulator performance and increase in memory usage. [ Team LiB ] . 0 • Statement reg_a [2] = 0 at time = 15 • Statement reg_b[15:13] = {x, y, z} at time = 25 • Statement count = count + 1 at time = 25 • Since there is a. vectors #15 reg_a [2] = 1'b1; //Bit select assignment with delay #10 reg_b[15:13] = {x, y, z} //Assign result of concatenation to // part select of a