The language is irrelevant. the tools handle both equally fine.
Verilog is easier to 'step-in'
- there is less keyboard pounding involved
- it's more productive ( not my words. year after year it is proven again in 'coding contests' between vhdl and verilog guru's . they get 30 minutes to desing something. the veriloggers are alway fully ready, fully debugged and the vhdl'ers are still fixing punctuation and lexical errors in their source )
Now, irrepective of the language used, the mindset is TOTALLY different from working on a CPU. A CPU works sequential. in FPG aeverything happens simultaneously under control of a clock. the 'program' in the fpga determines what is ALLOWED to happen. ( on a cpu it's more 'what must happen')
an important stepstone in beginning work with synthesis languages ( whetehr in cpld /fpga or ASIC work ) is UNDERSTANDING the synthesis process.
This is where 99% of the people beginning this stuff go catastrophically wrong. there are very few books that explain this process and the basic concept is lost. this leads to ugly ,kludged code, that often has uncovered scenarios and doesn't exactly do what you expect it would do. you then need to patch in more 'conditions' to cover these holes. if you understand the synthesizer you won't run into these problems.
The languages have evolved use the latest versions as they solve annoyances that often get frustrating very quickly.
Verilog2005 (sometimes called SystemVerilog although they are not 100% the same) is now the de-facto verilog standard. VHDL sits at release 204 i believe and 2010 is almost (very lately) finished but only some hi end tools support that one .
How does a logic synthesizer work ?
it essentially needs to translate a list of instructions and conditions ( if/then/else , mthematical and boolean operations etc ) into a block of logic. the way we describe theis block is essentially 'sequential'. it is a bunch of lines of code , each line containing some instructions. so the synthesizer reads these line by line and builds code.. but this s contradictory to the concept of digital logic ! everything has to happen at once. so how do we translate this 'list' into parallel form ?
the solution is simple :
we begin at the INPUTS, read the first line and build a little cloud that connects inputs to what is required by the line of code.
the next line of code as 'glued on' at the end of the previous build step.
once we hit the last line of code we have reached the OUTPUTS of the block. at every step the synthesizer can cut wires going to outputs and 'inject' a new block. it can also splice existing signals.
so we grow our logic from INPUT to OUTPUT.
an example ( in pseudo laguage so it is easy to read)
if x = 1 then
z = a and b
else
z = a or b
end if
so this describes a block that can switch between an and and an or operation under control of a signal.
so the synthesizer will begin building :
if x=1 then : take input pin'X' and drive a mulitplexer 'select' signal ( if/then/else constructs are built using multiplexers).
z = a and b : output of the multiplexer goes to 'Z' . Apply signals A and B to an AND gate. Ouput of the ANd gate goes to the multiplexer input for
'X=1'
else : this is the other multiplex case
z = a or b : take a and b and attach to an or gate. output of the or gate goes to mulitplexer input for 'x=0'
A mulitplexer is a simple boolean equation. a mulitplexer has a control line (S) , two inputs (x and y) and an ouput (z). the equation is Z = (X.Sn) + (Y.S)
since our code generates more boolean stuff we can now substitute terms into a massive equation, squash it, reduce it and done. we have logic.
when code becomes more complex you will need more constructs
if (enable=1) then
if updown =1 then
count = count +1
else
count = count -1
end if
else
if reset = 1 then
count = 0
else
if preset = 1 then
count = inputvalue
end if
end if
there. the bove code is a simple counter that can count up and down, has a reset and a perest input. reset makes count zero , preset loads an input value. but.. can you figure out what will happen in the following cases :
i make reset high and i make preset high ... (this one is fairly obvious)
i make reset low and preset low ... (this one is bit harder.. it is actually undefined as there is no full definition for this pathway...
this can lead to very strange situations and logic not doing what you expect it to do . and this is a common pitfall. your block of logic does not work right under all possible conditions because you forgot a pathway ... you need to go over the entire tree and figure out hat happens when... reading a large block becomes tedious to understand.
if you use the scheduling technique this problem will not occur.
Scheduled code is code written in such a way that there are no ambiguous or forgotten conditions possible by using the synthesis mechanism.
both VHDL and Verilog have a clause in the language definition that states the following : Logic shall be implemented in the ORDER it was WRITTEN.
what does this mean ? this goes back to the way the synthesizer builds logic. it read a line , builds a little cloud of logic , splices input sgnals and cuts the prior generated output to inject the new block inbetween.
so, the LAST line of code in a block sits CLOSER to the output than the first line of code.
using this mechnism we can rewrite our counter as follows :
if updown =1 then
count = count +1
else
count = count -1
end if
if enable =0 then count = count
if reset =1 then count = 0
if preset =1 then count = input value
the lowest line in this list has the HIGHEST priority. we don't care what has happened earlier. as the lowest line sits closest to the output it takes control
so , depending on 'updown' we count up or down.
if enable is zero then count remains count. this line sits closer to the output. so it doesntmatter what updown just wanted to do. if this line is active then count will not move !
if reset =1 thencount = 0. i dont care that enable wanted me to stay where i was.. theis line says : if reset is low : counter becomes zero. screw what happened before !
and lastly if preset is 1 then i load the input value.
there are no hidden pathways. something will happen , no matter what. the priorirty of 'what' happens is purely defined by the order of the instructions.
if someone tells you reset needs priority over preset you simply swap those two lines.
for simple things this may not be that obvious but if you start writing large complex blocks this way of coding saves a lot of time and it eliminates problems
for example an i/o block that talks to a bidirectional bus that needs tri-stating.
output = Z ( high impedant)
if (write) then
case (address)
0: output = somevalue
1: output = someothervalue
9 : output = anothervalue
default : output = 0xdeadbeef
end if
there we go. by default the 'output' pin is high impedant. only if 'write' is logic high will i apply some code that looks at an address and switches through some data.
if they now tell me i need a few additional modes : in reset all outpus need to be low ,and in writetest mode a special pattern (010101010) needs to be there
i simply add two lines at the end
output = Z ( high impedant)
if (write) then
case (address)
0: output = somevalue
1: output = someothervalue
9 : output = anothervalue
default : output = 0xdeadbeef
end if
if testmode then output = &b01010101
if reset then output = 0
done. this reads very easily . there is no mishmas of if then else elseif and other curly wurly bits.
output is high impedant, except if write is active then we look at address and throw some stuff on there, except if testmode is active then we throw 1010101 on there ,except if reset is active then we throw all zeros on there.
no hidden pathways, no surprizes. very aesy to read and understand , very easy to modify ( priority changes is a matter of moving some lines up or down ) and there is a very good chance it will work first time as you intended it to work.