The Verilog reg is the object that can store a value and a drive strength. It may be used for designing both combinational and sequential logic. The main thing is that reg is a storage object. This is the main difference with a wire, which is actually is a wire that connects points. And these points may be determined as either explicit or implicit objects of the reg data type.
The data of type reg can only be driven in initial and always blocks. So, if we need to specify either sequential logic (flip-flops) or complicated combinational logic (which would be hard to fit in continuous assignments) - we use reg.
reg [N-1:0] ctrl;
reg ctrl_p_ff;
genvar i;
generate
if (N > 2) begin // 4/5, 8/9, 16/17, ...
always @(*) ctrl[0] = ~(r[N-3] & ~(sel_comb_i[N-2] & ~r[N-2]));
for (i=1; i<N-2; i=i+1) begin
always @(*) ctrl[i] = ~(r[0] & ~(sel_comb_i[N-2-i] & ctrl[i-1]));
end
always @(*) ctrl[N-2] = ~(ctrl_i_net & ~(sel_comb_i[0] & ctrl[N-3]));
always @(*) ctrl[N-1] = ctrl[N-2];
end
else if (N == 2) begin // 4/5, 8/9
always @(*) ctrl[0] = ~(ctrl_i_net & ~(sel_comb_i[0] & ~r[0]));
always @(*) ctrl[N-1] = ctrl[N-2];
end
else if (N == 1) begin // ?????? 4/5
always @(*) ctrl[0] = ~ctrl_i_net;
end
endgenerate
always @(posedge clock)
ctrl_p_ff <= ~ctrl[N-1];