First, assign is structural, not procedural. What does that mean? Well, structural code is just what it sounds like: it's anything that specifies structure. Basically that means wire connections to modules or gate primitives such as "and" and "or" gates. The "assign" keyword is structural because it essentially defines what a wire (or more generally, a net) is structurally. Here's an example:
assign x = a;
This means that x takes on whatever value a has. The right hand does not have to be just another symbol; it can be a logical expression, something like:
assign x = (a & b) ^ c;
That's the easy part. The confusing thing is always know exactly when and where you can use things in verilog.
Here are the rules for assign:
The symbol on the left hand of the assign must be a net (as opposed to a variable). That means a wire, wor, wand, tri0, tri1, etc.
The symbol on the right hand, however, can be either a net or a variable. Variable types include reg, real, time, etc.
You can only use assign once for any net. You cannot, for example, have the following:
assign x = a;
assign x = b;
Remember, assign specifies structure, so this doesn't make sense. You're not specifying a changing value for x over time or program execution as you might in procedural code. Which brings me to my next point...
You cannot use assign in procedural blocks. For example:
always @(*) begin
if(a)
assign x = b;
end
That makes no sense. Procedural code is something that is run or executed like a program. You cannot specify the structure of something procedurally. That's just crazy-talk.
So, here, in the context of a module, is a valid use of assign:
module foo(output x, input a, input b);
assign x = a | b;
endmodule
[EDIT: The above code is apparently syntactically valid, but it does NOT do what you want. It is also deprecated and not all compilers will warn you.]
The functionality of this module is actually to be an OR gate. Alternatively, we could have achieved the same functionality with:
module foo(output reg x, input a, input b);
always @(a,b) begin
x = a | b;
end
endmodule
Notice that there, x is declared as a reg and we do not use assign. That's because this is procedural code, not structural code.
Okay, that's it! :D