designates my notes. / designates important.
A welcome follow up to FPGA 101. It includes just the right amount of simple code snippets that are explained line by line. Also included are a few larger, more realistic examples (UART) that are equally well described.
Completing Eduvance the VHDL tutorial I watched as I read FPGA 101 proved to be a nice supplement for both books.
Here is another tutorial series I worked through as I read this book. This is a basic beginner tutorial that eases you into VHDL. It uses a homebrew development board, but I did everything in simulation since I don’t have any board yet.
The more advanced Bruce Land/Cornell FPGA/Verilog course, ECE5760 DE2/115 lectures 2011, available on youtube was also more approachable as I read this book. It is quite a bit more advanced and uses Verilog instead of VHDL.
“Never drive a clock input with a signal that was generated by FPGA fabric (LUTs or Flops)!"
“Best Design Practices calls for us to design from the ’top-down’ and code from the ‘bottom-up’"
VHDL, as with so many other languages, is difficult to teach as each part of the language requires the understanding of some other part of the language to make sense. This makes the traditional “linear” approach very difficult as it requires the reader to read the book from start to finish in order to have a sufficient grasp of the language to become useful. This book is experimenting with a different approach – it covers various aspects of the material multiple times! Each “loop” builds upon the previous and introduces new material along with practical application.
Read from start to finish to understand? You don’t say…
2 I use “he” instead of the awkward “he/she” or the more encompassing “he/she/he–she/she–he/it” This is not out of sexism, but out of long tradition, in virtually every language, of using the third person masculine for when the gender of the reader is unknown (as opposed to indeterminate).
Another stumble on a rocky start. PERCEIVED sexism is ruining just about everything. This kind of display, even in its attempt at humor, simply gives more credence to the insanity of “gender inclusion”. If you are reading this, please, simply do not mention this nonsense and it will go away. To keep picking at it like a scab on makes the scar worse.
[structural VHDL is typically used] at the top level of a design where large lower-level modules are stitched together, when a specialized piece of Intellectual Property (IP) is provided often as a “black box"4, or when specific architectural features such as block memory, high-performance communication cores, or other specialized silicon resources, are required.
RTL stands for Register Transfer Level and refers to a coding style that defines how signals flow between hardware registers and the logical operations that occur between the registers. This is one step more abstracted than structural coding as the independent logic elements don’t need to be instantiated but can rather be inferred5 from equations.
The next level more abstracted is called “Behavioral Coding” This adds an additional layer of abstraction in that software-type constructs such as “for loops” “if statements” and “case statements”
’’’ 1.“Divide and Conquer” - break the design into manageable pieces a. Divide by functionality b. Complex modules can be further broken into smaller pieces7 until basic functionality is reached c. Write a single sentence describing what each module at each level of hierarchy does. If you can’t do this, you don’t have a clear enough idea of what the module should do. Think about it some more
Draw a top-level block diagram for each hierarchical level in the design a. Draw lower levels of the design hierarchy for more complex blocks
List the signals which move from module to module a. For the same level of hierarchy b. Through vertical levels of hierarchy c. Don’t just list signal names, but include types and units8 d. Choose meaningful signal names – be descriptive, complete, and unambiguous
Don’t start coding until the above steps are complete ’’’
try a name such as upperQuadrantTap12 (which uses a Java style naming convention) or upper_quadrant_tap_12 (which is more of a C/C++ naming convention and more commonly used in industry).
The C/C++ naming conventions are typically used with VHDL as the mix of upper and lowercases is generally lost as VHDL converts everything to lowercase during processing.
Every module must have one and only one entity.
The architecture is where the description of the circuit occurs. This is further broken down into two regions. The “what is to be used” (definitions) area resides between the “architecture” statement and the “begin” statement. This portion defines the various signals, constants, functions, procedures, and other modules used in this module.
signal input_from_laser_gyro: std_logic_vector(15 downto 0) := (others=>’0’);
Notice the use of the aggregate (others => ‘0’) to initialize this signal on power-up.
An optional initialization which includes the required symbol “:=” and a value of the proper type
although VHDL is a case insensitive language, when enumerated types are enclosed by single quotes, the case does become important!
As a designer, you will most likely use your own enumerated types for naming various states within a state machine.
Character - one of 256 characters defined in the ISO 8859-1 character set. Characters, like Booleans, are generally used more often in test benches than in synthesizable designs.
Boolean - comprised of the values “true” and “false” Although synthesizable, Booleans more commonly see application in test benches. Typically Booleans are used to temporarily store the results of complex comparisons.
integers are usually given a subrange. Limiting the range of an integer
predefined floating type is that of the REAL.
Floating types are not intrinsically synthesizable, that is, they are limited to simulation only. That said, the IEEE does have a proposed synthesizable floating-point standard which does work, however, the implementation performance is not especially efficient. There are a number of techniques for managing non-integer representations including fixed-point techniques.
Physical types. The last scalar type – that of the physical type – is provided to give context to real-world quantities. The only predefined physical type is that of time; however, numerous libraries are available to define other physical types such as distance, voltage, and current.
Users can also define custom physical types.
Similar to floating types, physical types are not synthesizable, but rather intended for simulation. Using physical types, board-level and system-level simulations are possible (including modeling of analog-to-digital converters, transistors, and other devices).
The most commonly used physical type is that of time which is almost always used in test benches to generate clocks and other stimuli.
Composite types define collections. These collections could be an ordered grouping of the same type of scalar object – arrays, or of different types of scalar objects and/or other composite types – records.
A commonly used array of std_logic is to define a byte: “array(7 downto 0) of std_logic” Of course the std_logic_vector type is a shorthand for this and is equivalent to “std_logic_vector(7 downto 0)”.
Binary logical operators (and, nand, or, nor, xor, xnor)
Relational operators (1⁄4, /1⁄4, <, <1⁄4, >, >1⁄4)
Shifting operators (sll, srl, sla, sra, rol, ror)
Addition operators (þ, À, &)
Unary sign operators (þ, À)
Multiplying operators (), /, mod, rem)
Other operators (not, abs, )))
Line 6: rotRightResult <= slvValue(2 downto 0) & slvValue(7 downto 3);
Output signals may be left disconnected (using the keyword “open” or tied to a signal which is not driven otherwise a “multi-source” error will occur (a short).
When we instantiate a submodule, we are coding in the “structural” style, that is, we are describing how various modules connect.
Both post-Map and post-Place-and-Route simulations are generally not performed for two reasons: first, they are very time-consuming for anything larger than a small or medium design. Second, another tool – the Static Timing Analyzer – is available. This tool analyzes all of the paths through the FPGA against the timing constraints provided to the implementation tools and reports unconstrained paths (paths that are not covered by a timing constraint) and failing paths (paths that take too long to meet timing).
The UUT [unit under test] or DUT [device under test] represents the top level of the design that you intend to synthesize.
wait
wait for <time>
wait until <event>
wait on <signal list>
If it makes sense to NOT have a reset for a process, then do so.
If a reset must be used, it should use positive logic (‘1’ = reset, ‘0’ = operational).
If a reset must be used, make it synchronous to the established clock.
1. Generics can be of any legal type or subtype.
2. Generics are used in the same way as constants are within a module.
3. Generic values can change from instantiation to instantiation, but are
constant within a module.
4. Top-level Generics can usually be set via synthesis tool options (GUI) or
command line arguments so that source code doesn't need to be modified.
5. Generate statements come in two flavors: conditional and looping.
6. Generate statements can wrap one or more concurrent statements – this
includes component instantiations, assignments, conditional assignments, and
even processes.
7. Generic and Generate statements are often, but not always, used together to
make code more flexible for reuse.
Procedures are blocks of codes which take zero or more “arguments” and perform a designated task. These tasks may change zero or more of the passed “arguments” A procedure does not “return” a value as a function does, but rather relies on modification of one or more of the passed arguments.
Functions are blocks of codes which take zero or more “arguments” and perform a designated task. While these tasks may change zero or more of the “arguments” they must return exactly one value of the designated type.
Whatever types, constants, or variables that are defined in this function or procedure are only available within that function or procedure. Information can only be made available from within the function or package by means of the arguments or the return value in the case of functions.
There is a special category of functions and procedures known as “pure” and “impure” Functions are “pure” by default, that is, if you neglect to specify that the function or procedure is pure or not, VHDL assumes that the function or procedure is pure. Pure, in this context, means that all the information that flows into the function arrives via the arguments, and all the information that flows out of the function are returned via the return statement and/or the arguments.
Conversely, an “impure” function or procedure either modifies a value not listed in the parameter list, or uses a value not listed in the parameter list.
As a general rule, functions are used to modify a particular piece of data. For example, computing the next value of a gray code sequence can easily be computed with a function.
Procedures are used when several values must be changed or when no values are returned. Examples of use might be a procedure for initializing a large set of data, or writing specific messages to the simulation console.
‘left – returns the leftmost value in the range
‘right – returns the rightmost value in the range
‘high – returns the largest value in the range (regardless of order)
‘low – returns the smallest value in the range (regardless of order)
‘range – returns the range of the array
‘reverse_range – the reversed range of an array
It is important to note that not every synthesis tool will implement multiplication and division for every possible argument. For example, some synthesis tools may only support multiplication by some power-of-two as this is easily done by wire shifting and requires no logic. Typical tricks for doing multiplication by a non-power-of-two constant would be to break the single multiplication into a series of shift-and-adds. So, multiplying by a constant “5” is the same as multiplying by 4 (left shift by 2), then adding itself.
the trivial case of dividing by a power-of-two can be implemented simply by right-shifting a signal. More complex division, such as fixed-point and floating-point math is available in the IEEE fractional packages (IEEE 1076.3-2006). Not all aspects of these packages may be efficiently implemented in synthesis.