Welcome to Verilog Fundamentals. In this video, you will learn how to use the assignment statements and also about the wide range of operators in Verilog and how they work. The fundamental statement in Verilog is the assignment statement. All assignment statements outside of an always or initial block are concurrent. They happen at the same time, they're not sequential. Like VHDL, Verilog is not a programming language like C where everything happens sequentially. This confuses students sometimes and they used to doing a lot of programming, they think the results are going to be the same. They're not, these are data concurrent languages. In order to get good results, you need to think of the hardware you want to make before you write the Verilog statement. The assignment statement itself makes hardware. The output of the operation on the right hand side of the symbol is continuously assigned to the variable on the left hand side as in this statement that we have here where we're going to assign A to B and C and create an and gate. The variable on the left hand side must be a net like a wire, it can't be a reg went outside of the always block. So let's look at an example where we're using continuous assignment statements. Assume that you want to create this circuit, how would this be coded in Verilog? Think about this for a minute, it's not too hard. We can probably assign y to an expression with operators in one line in order to create the circuit. So the answer then is to assign y equal to a or b in parentheses and the nought of c given by the tilde that implements the nought. Note that if we leave out the parents, what we get instead is something that looks like this, like the circuit that you see here. We're assigning y equal to a or b and nought c, without the parents because of the default order of evaluation, we're going to get this circuit instead. This may be a completely different result than what we expected if we wanted the first circuit. So we're trying to make this, if we're not careful with the way we write the code, making sure that we put things in order, then what we're going to to get is this instead. So you need to be aware that combinational logic descriptions, need to be well ordered. So outside of an always or initial block, the assignment statements are all continuous and concurrent but inside of a procedural block, assignment statements are not necessarily concurrent. For example, if you have a procedural or a blocking assignment, which is just the equals symbol by itself, those assignments are done sequentially in the order of the statements are written. The second assignment is not started until the preceding one is complete. For example here, we have an always block that has positive edge of a clock and its sensitivity list. So won't make any changes except at the positive edge of clock. As you might imagine, we're trying to create flip-flops or registers here. So in the first line what we have is that Q1 gets Q2 and D gets Q1. And the way this will work is, is that initially, Q1 is evaluated, it's assigned to Q2. And so the value of this being assigned to Q2 is the old value of Q1, and then in the second statement, Q1 is going to get D, okay. So this creates that a shift register where you have D goes into Q1 and then Q1 goes into Q2 and so on, which is probably what you expected. If you reverse the order though, in the second line here, what happens is, is that Q1 is going to get the value of D. And that evaluation is going to complete and then in the second statement, Q2 is going to get the new value of Q1, and so it's going to be equal to D as well. And so the synthesizer is going to either generate a single flip-flop where D is the input and Q2 is the output, or it might create parallel flip-flops where you have both Q1 and Q2 outputs, but D is driving both of those. And so now you get a completely different result because you change the order. So what we see is that the order statements matters in always blocks when using blocking assignments. There's a second type of assignment statement and procedures are always or initial blocks known as the nonblocking assignment. Note that there's no hyphen in nonblocking. A nonblocking assignment which has the symbol less than or equal to an equal sign together, so kind of points to the left hand side. When you have these assignments and they follow each other in the code, they're all started in parallel, so they are concurrent. The right hand side of the nonblocking assignment is evaluated starting from the completion of the last blocking assignment or if none, the start of the procedure. The transfer to the left hand side is made according to delays that are defined. An intra-assignment delay in a nonblocking statement will not delay the start of any subsequent statement whether it'd be blocking or nonblocking. So let's see what happens when we use nonblocking statements instead. So look at the previous example now with the nonblocking assignment. Using nonblocking statements, the intent of the previous example to create a shift register is preserved no matter what the order. So here we have an always block and we see that Q1 gets Q2, then D gets Q1, we get a shift register. But if we reverse the order of these things, we still get a shift register, you get exactly the same circuit. If you want to create a parallel flip-flop, like what we saw before that we created unintentionally with the blocking statements, then you can still do that. You just are more explicit about it and say, Q1 gets D, Q2 gets D, and then you know you're creating parallel flip-flops just as you intended. So normally, you wouldn't write code this way where you're repeating variable names and so on. But, we're just showing kind of what would happen in each case so that you can understand that nonblocking statements happen in parallel. And that the order doesn't matter in which they're presented. So many new Verilog learners get confused about the use of blocking or nonblocking statements. So therefore, we've created some guidelines for the use of blocking and nonblocking assignment statements. One generally should not mix nonblocking and blocking statements in the same procedure. The nonblocking statements best mimic what physical flip-flops do, use it for always blocks with a posedge clock,negedge clock, those kinds of procedures which describe sequential circuits. The blocking assignment best corresponds to what C-code would do, you can use it for combinational procedures, to create combinational logic within or outside of always blocks. Sometimes it's best to split all of your logic design into combinational logic and sequential logic and then within an always block, create all your sequential logic using nonblocking statements. And then have another always block, we create our combinational logic using blocking statements. If we combine assignment statements with operators on variables, then we get a wide variety of possible circuit descriptions. Verilog provides us with a number of operators, half of them are shown here in this list, which is in the order of precedence. One can use the square brackets to select portions of a bus or sections of an array. We've already seen the importance of using parentheses and the use of the tilde to implement an inverter. If the logical symbol is placed in front of a vector variable, it creates a gate that operates on all inputs yielding one output. This is called a reduction operator. Signs can be changed using a unary plus or minus symbols, concatenation is something that's commonly done. It's very easy to do with in Verilog. You just have to put the two vectors inside of the curly braces and then the result is going to be the combination of those two vectors. It's also really easy to do replication of vectors. We just need to put the number of times you want to replicate it inside these braces as shown. And then you're going to get a vector that's going to be that number of times longer than the initial one, the vector is going to be repeated. We can do multiply, divide, modulus kinds of operations within Verilog, but you have to be careful because divide and modulus may not be supported for synthesis. And when you do a multiply, you may get some results that are not expected, you have to be careful with that. Pay attention to the input variables whether they're unsigned or signed, and so on because that will generate different kinds of logic within the system. If you have the plus or minus signs in between variables, then you're going to get an add, or a subtraction operation that's going to be implemented in hardware. So here's the rest of the Verilog operators in another chart. The shifting operators have a similar effect to those in C programming. Here, it says that if you have a variable that gets shifted left 2, that effectively multiplies it by 4, and so on. If it shifted the other way, then it divides it by some factor of 2 and that's sometimes handy to use and it implements very efficiently inside of logic. Comparison tests use the same syntax as C, so you see all the different kinds of comparison tests here. Then you have the bit-wise logical operators, so and, XOR, not OR, and so on. These single symbols when they're placed in between a set of variables are going to do bit-wise operations, which particularly, the variables are vectors. So if you had two 4-bit vectors and you're going to operate on those, one bit pair at a time is going to be operated on yielding a 4-bit vector output. Lastly, we have the conditional statement. It usually synthesizes to a mux and it's a very efficient way to represent them. So let's make use of some of the operators in order to describe some circuits. Suppose you want to make a multiplexer, let's look at several different ways to code this in Verilog. You could use data flow modeling by use of a continuous assignment statement using the conditional operator. This gives a very efficient solution as the circuit is described in one line. Any change on the right hand side will produce an event generating an output on the left hand side. The implementation produced by the synthesizer will look a lot like a circuit shown here with an or gate, two and gates and an inverter. Knowing that, we could create a structural model by using gate primitives. This is a little more involved but makes the underlying structure clear. The default data type for the inputs and the outputs is type wire and so here, we just instantiate a couple and gates and or gate and a not gate in order to create this circuit. You could use behavioral modeling by use of an always block. Modules can contain one or more always blocks, the reg behaves like a memory. We need to use it here because wires can't be assigned to a variable in an always block. So you can't use wires as the output like in real life, wires don't have memory. Here, in the if then else statement, the red will maintain the value until otherwise assigned. So, it remembers and affect the result of this if then else, the list of signals after the and symbol here is called the sensitivity list. Any change on these signals make the block executes and simulation. The synthesis output should match the simulation out to it. So once again, what we're going to end up with after the synthesizer runs is a circuit that looks just like this one. Lastly, you could make a multiplexer using a User-Defined Primitive or UDP. Here, the behavior of the output is defined by truth table that includes the don't care symbols indicated by a question mark. This allows you to specify the behavior more precisely. This model is a little different from the others in that when a and b match, select is ignored and the out will be a logical level instead of an x like it is in the other models. You'll be able to see this in simulation. This behavior is a little more optimistic and likely more robust. So this gives you some motivation why you might want to define your own user primitives instead of using the given operators and statements within Verilog. So in this video, you have learned how to use assignment statements, when and where to use blocking or nonblocking assignments, the wide range of operators in Verilog and how they work, and four different ways to make a multiplexer. In the next video, we will learn more about data types and higher level statements and design processes.