Hi,
Timing loops are created when your code is written.
Example code with a loop.
assign y0 = n0 & s0;
assign n0 = y0 & r1;
The error is created when the logic cone for y0 contains the signal (n0).
And the logic cone for n0 contains the signal (y0).
When you write your logic you should always consider the logic cone that you are creating for every signal. This will improve your finding timing problems early as well, this will come with experience.
Yes, it is a problem, because always block is not clocked. As you may see, it has all the signals in its sensitivity list @(*) as opposed to @(posedge clk).
When you have a signal that is "self referenced" you have a combinatorial (logic/timing) loop. Try to imagine how would behave an actual digital circuit that gets it's inputs from its outputs. It'll go in some run away craziness.
It is OK to self reference if the value of a signal depends on the previous value of itself.