oh, and something very important : learn the principle of scheduling in logic synthesis (part of the IEEE standard ). it will help you tremendously to make write very clear , first time right logic.
Googling for "principle of scheduling in logic synthesis" - 0 hits.
Any chance of tossing me a link?
you will find almost no links to this. it is mentioned in the IEEE standard for verilog in paragraph 5.4.1 under Determinism
" The standard guarantees a certain scheduling order -snip-
Statements within a begin-end block shall be exceuted in the order in which they appear ... -snip- ....
in no case shall the statements be executed in any order other than that in which they appear. "
" non blocking statements shal be executed in the order they appear "
here is an example of this mechanism in action
always @(posedge clock)
if (reset) count <=0;
else
if (enable) begin
if preset count <=data_in;
else
if(updown) count <=count +1;
else count <= count -1;
end
end
simple up down counter with parallel load and reset.
reset works always
preset and count only work when enabled. preset has priority over counting operation
using the principle of scheduling this thing becomes much simpler to write and maintain
always @(posedge clock)
if (enable) begin
if(updown) count <=count +1;
else count <= count -1;
if preset count <=data_in;
end
if (reset) count <=0;
end
the block inside the if(enable) only runs if enable is true.
checking reset always runs. as this is the last operation in the list it has the most 'power'
i don't care what was done by all previous operations. if reset is active count goes to zero.
same for the preset operation. i dont care that the counter has been modified 'before' . if preset is active the counter gets loaded.
here is why this works the way it works : it has to do with HOW a synthesizer builds logic. it doesnt matter if you are using VHDL or Verilog they ALL work like that as it is part of the standard. any synthesizer out there needs to do it this way to be compliant to the standard.
we humans write a sequential list of operations and the synth needs to 'parallelise this'. how is this done ? by creating mux-chains.
lets take this step by step.
The synthesizer starts reading my code. grabs a clock, creates a bunch of flipflops for 'count' and ties the clock to those flipflops. Now it hits the if (enable) statement. an if construction is basically a multiplexer. ((AX) + (BXn))
if enable is high then A goes through , if enable is low then B goes through.
So the outputs of the flipflops go to B (enable low) the input A is left open.
Note : the standard says that for any multiplexer input where there is NO specific clause given the current output will be applied. this means you never have undefined operations ! you only need to specify the 'changes'. anything you did not define does not happen and the registers don't change state. a nice side effect is that you never get that dreaded latch inferral or undefined pathways in state machines.
Now, we hit another multiplexer controlled by updown. so we tie input A of the first multiplexer to a new one , under control of UPDOWN. input A gets count-1 , input B gets count+1.
this pathway is now closed.
The next line says if (reset). the synthesizer wil now do the following : it will take EVERYTHING it has already generated and INSERT a multiplexer between that block and the register. so that whole block if enable if updown.. get shifted to the left and a multiplexer is inserted under control of reset.
If reset is active then 0 is sent through to the registers, if not then whatever was in effect goes through (and that is all the code that was generated up till now )
that is how synthesizers work and build logic multiplexers are very simple boolean operations and chains of multiplexers can be minimised very compact resulting in the fastest possible logic.
using this principle i could have written this code even differently
always @(posedge clock)
if(updown) count <=count +1; // this is the default operation
else count <= count -1;
if (preset) count <=data_in; // unless preset is active then this line overrides.
if (enable == 0) count <=count; // unless enable is false then we do not alter 'count'
if (reset) count <=0; // unless reset is active then we make count zero
end
the advantage of using this principle is that you squash complex if then else constructs to a few lines.
reading such code and understanding its exact behavior is also very simple. the last line has most 'weight'
if someone gives me this :
always @(posedge clock)
if (shift) value <= value <<1;
else value <= value >>1;
if (sample) value <=data_in; // unless preset is active then this line overrides.
if (enable == 0) value <= value; // unless enable is false then we do not alter 'count'
if (reset) value <=0; // unless reset is active then we make count zero
end
reading and understanding is now easy. :
depending on 'shift' we shift left or right.
unless sample is active then we latch in new parallel data.
unless enable is zero, in which case value does not change.
unless reset is active in which case we clear.
If i now tell you that preset needs priority over reset. you simply swap the position of two lines.
There is now certainly a bunch of people that will growl on this as it is not 'proper' VHDL or Verilog. and they are right and wrong at the same time.
This is verilog for SYNTHESIS. meaning we will make actual logic with this.
you will rarely encounter this kind of code in the wild because of two reasons :
1 - most code is written by code monkeys that follow textbooks . textbook written by people who don't understand how a synthesizer works and never read the IEEE standards.
2 - most people code for FPGA and have no idea how their 'ramblings' impact actual mapping. just take a larger fpga if it doesn't fit.
Go to the industry (and i mean the semiconductor business where asics are made ) and you will see this kind of code.
- it promotes first time right
- it avoids undefined pathways
- it synthesizes the combinatorial cloud to the smallest equations possible.
- easy to understand easy to maintain easy to alter.