You still have problems left, because there are two places, that can execute simultaneously, that both try to write to cnt_reg, the block that executes if (reg_we != 4'b 0000) and the block that executes if (cfg_reg == 32'h 0000_0001). Simulation will let you do that, synthesis won't. You need to make those two blocks mutually exclusive. You will have got an error message about the clash of assignments, it would have made things a lot easier if you'd included it rather than just handwaving 'so maybe it's a case of "oh... you're not allowed to do that" sort of thing'.
We've just had a discussion on this in another thread. Multiple assignments within a single always_* block are not only allowed, but such behavior is explicitly defined in the specification, namely, the last assignment in the code flow always wins, and so there is never a clash nor any ambiguity. This is very convenient in many cases for state machines, when you can assign "default" values in the beginning of the block, and later down the flow only reassign those signals which are supposed to have non-default values.
My own experience has been that if you pile a load of defaults at the top an always there's no problem, the tools can figure out what you mean - in fact I typically do just that for the outputs from state machines. If your conflicting drivers are buried away behind a couple of conditionals that could both be true all hell breaks loose, well OK, you get an error message. Imagine it as "How would I build this physically out of multiplexers without using priority encoders" and you find you can't. If there's anything in there that looks like a priority encoder (e.g. nested ifs) then it sails through. it's only when it can't work out "who wins" on two simultaneous enables that it breaks, and although the standards might say that the lexically latest wins, some of the tools don't seem to have got that message.
Edit: Out of curiosity I went away and tested this out in Yosys on as simple a test case as I could.
module Test (
input wire clk,
input wire enableA,
input wire sourceA,
input wire enableB,
input wire sourceB
);
reg destination;
always @(posedge clk)
begin
if (enableA) destination <= sourceA;
if (enableB) destination <= sourceB;
end
endmodule
And I find to my surprise that it
doesn't barf on it, and it superficially appears acts according to standards. I have definitely had problems with this in the past, but with exactly
which synthesis engine I'm not now sure. I thought I had encountered it in Yosys, so either Yosys has changed or it was another tool, but either way my technical objection fails.
Presumably to do this Yosys has to synthesise a priority encoder. Checking exactly what it
does synthesise is a job for another time, and when I've got a bit I'll come back to it and take a look.
So, I have to remove my objection to clashing sources on
technical grounds, but I'm going to retain my objection on the grounds that code that relies on implicit subtleties is bad compared to code that explicitly states what will happen. That is
always @(posedge clk)
begin
if (enableA) destination <= sourceA;
if (enableB) destination <= sourceB;
end
and
always @(posedge clk)
begin
if (enableB) destination <= sourceB;
else
if (enableA) destination <= sourceA;
end
ought to both do exactly the same thing, but the latter makes the coder's intention entirely clear, the former does not
even though it will result in exactly the same behaviour.2nd edit:
Here's what gets synthesised by Yosys without any optimisation, two cascaded multiplexers: