Programar en VHDL
Programar en VHDL
INDEX
The main design unit of the system is the so called: entity. The entity provides system’s interface
specification and is generally comprised of two elements:
•= Parameters of the system as seen from the outside, such as bus width, delays, clock frequency,
etc. (Are declared as generic).
•= Connections which are transferring information to an from the system (system’s inputs and
outputs). (Are declared as port).
Every stand alone system, entity, most have a description, a functionality, and this is the
architecture. The architecture are always related to an entity. Since different types of architecture can
perform the same function, a system (an entity) can be specified with different architectures.
Each system’s architecture can be described either in terms of its functionality (behavior) or
structural, which require different kinds of information about the system. The synthesis tools work with
both them.
The behavioral description can be mixed with the structural one. The synthesis tool first of all needs
to transform the behavioral part in a fully structural one. The structural design is referred to concrete
physical components, and are easier to synthesize but more difficult to specify.
The Behavioral description specifies what the system is expected to do. Is a description of output’s
response to input’s changes.
The Structural description specifies what components should be used and how they should be
connected.
Another important concept is the Package. It is an external source of description when something is
undefined in the standard language. A library is a simple example of a package use. There are two
clauses, which serve to declare a library: library and use.
The STANDARD package has the declaration of all-standard types, operators and objects. It is
used by default in any VHDL compiler and it is not needed to use the library and use clause for this
package.
library Std;
use Std.Standard.all
The TEXTIO package has the declaration of types and object relating to reading and writing texts as
defined by VHDL standard. The operations are not synthesizeable and can be used for simulation and
modeling only.
library Std;
use Std.TextIO.all;
The STD_LOGIC_1164 defined by IEEE Standard 1164 are extension to the standard VHDL
language. Multiple value logic, resolution functions and extended operators are the most important
declarations here.
library IEEE;
use IEEE.Std_Logic_1164.all;
The IEEE 1029 WAVES specify waveforms and vector Exchange Specification. Define data format for
testing and timing verification.
Apart from this main packages many vendors use to add their own packages that usually bears the
vendor’s name.
-------------------------------------------------------------------------------------
-- Example of a system --
-------------------------------------------------------------------------------------
library IEEE;
use IEEE.Std_Logic_1164.all;
entity entity_name is
generic (
BusWidth : integer :=8;
MaxDelay : time :=20 ns;
LoopIteration : integer:=3
);
port (
CS, RST : in std_logic;
DATA1 : inout std_logic_vector (15 downto 0);
DATA2 : inout std_logic _vector (BusWith-1 downto 0);
ADDR : out std_logic _vector (19 downto 0)
);
signals ( X, Y : std_logic;
InternalBus : std_logic_vector
);
begin
….
1.3 Comments
Comments can be included in the VHDL code for documenting the description. Comments in VHDL
start with two hyphens (--) and terminate at the end of the line. They can be located almost everywhere,
but may not start in the middle of an identifier or reserved word.
-- any comments
2.1.1 Generic
Are specifying only in entities inside the generic clause, always before port declarations. Generics
provide constant values for different parameters, as seen from the outside of the system. For example,
they can control size of a model, in particular the width of buses a size of such parameterized modules
as n-bit adders and n-bit comparators. They can be used for timing parameters such as delays, setup
times, hold times, switching times and any other timing parameters used in electronics. They can also be
used as loop counters, etc.
generic (
generic_name : generic_type := optional_value;
……
generic_name : generic_type := optional_value
);
2.1.2 Constants
The main use of constant is to apply “legible names” with explicitly defined types instead of literal
values, which are called “hard-coded”. This way the code becomes more readable an easier to maintain
because changing a constant in one place will affect all of its instances in the entire architecture. The
constant is generally used the same way as generic does. Because a constant is not visible from the
outside it cannot declare size of vectors declared as ports in entities. This restriction, however, can be
overcome if the constant is declared in a package used by an entity.
The constants are declaring only one per each line. If two or more constants are of the same type
and have the same value, they can be specifying in one declaration.
Basically the main difference between generics and constants is that generics can be used
dynamically while constants are purely static. This means that you can change the value of a generic
without any changes to the code. However, constants cannot be changed without changing the code.
This is important especially in case when the specification will be used as a component for a high-level
specification. Its time such a component is used, it may assigned new values if they are specified by
generics.
Any change in a generic value would affect all architectures and ports of its entity.
Any change in a constant value would affect the architecture where it is declared and if declare in a
package, in any design unit using the package; this include entities and architectures assigned to them.
Generic are used only in entity.
Constant are used in all declarations: package, entity, architecture, process, block, component.
2.1.4 Ports
If we use the IEEE standard is enough to declare the std_ulogic (std_ulogic_vector) in case of
use of unresolved functions or std_logic (std_logic_vector) when using resolved functions. This
means that possible conflicts in a bus can be resolved.
The signals can be classified as either internal or external.
•= External signals are the signals that connect the system to the outside word; they form the system
interface. (Declared only in entities).
•= Internal signals are not visible from the outside. Are parts of its internal architecture, providing
signals between internal circuits. (Declared only in architectures).
The ports are also signals but the keyword signal in port is not needed because each port by
definition is a signal. Internal signals do not require mode (in, out, inout, etc) declarations.
The visibility of each signal is determined by the place in which it is declared: Only higher level
definitions are seen by lower level.
•= A signal declared in a package is visible in all design units which use this package
use MyPack -- the next body (entity) will use the package MyPack
entity En1 is
…..
use MyPack -- the next body (architecture) will use the package MyPack
architecture Arch1 of En1 is
…..
•= A signal declared as a port in an entity is visible in all architectures assigned to this entity
architecture Arch2 of En2 is -- The architecture inside one entity will use all signals
….. signals that were before declared in the entity.
architecture Arch3 of En2 is
…..
•= A signal declared in the declarative part of an architecture is visible only inside this architecture
architecture Arch2 of En2 is -- declaration of the signal B inside the architecture Arch2
signal B -- signal B is not seen by the other architecture Arch3
…..
architecture Arch3 of En2 is -- declaration of the signal C inside the architecture Arch3
signal C -- signal C is not seen by the other architecture Arch2
…...
•= A signal declared in a block located within an architecture is visible only inside this block
Every piece of information inside a digital system is stored in the form of bit and bit_vector.
However, often it is inconvenient to use this form. For example is much better to represent a 32 bit bus
in an Hexadecimal way than in Binary. Similarly, many complex but regular data structures can be
represented by arrays and physical parameters.
Scalar type is a generic name that refers to all types whose objects have no elements or internal
structure. VHDL defines several scalar types. However, you may specify your proprietary types, either
range-based or enumerated. Usually, such a user-defined scalar type is declared as a subtype of a type
defined earlier, either as a standard type or other user-defined type.
•= Boolean
In VHDL Boolean values TRUE/FALSE are not equivalent to 1/0 logical values. Therefor, it may not
be assume that True is the same as ‘1’ and vice versa. They are completely different types.
•= Character
The set of values specified in the character types covers all characters defined by the eight-bit
character set ISO 8859-1 (known as Latin-1).
•= Integer
The range of integer values depends on the implementation but must include the range specified
above. In synthesis, integer type is usually limited to some subset of a complete type (e.g. 0 to 15 or 0
to 99) in order to reduce the number of storage resources.
•= Real
Real type, also called the floating-point type, is specified with a range, which is implementation
dependent. However, the specified range is the maximum that must be covered by all implementations.
Also all implementations must guarantee a minimum of six decimal digits of precision.
•= Bit
Bit type is an enumeration type that defines two standard logical values: ‘0’, ‘1’. This is the only type
that can be used for logical operations and unlike in gate-level designs.
An example of User Defined Enumeration Types is when we use a Finite State Machine (FSM)
representing a sequential design. Each state of the FSM is encoded with state variables (flip-flops),
which store information on the actual state.
IDLE
DECODE
Physical Types are unique in VHDL because they specify not only the object values but also units in
which the values are expressed. This allows such physical quantities as time, distance, current,
temperature, etc., to be specified with precision. VHDL standard defines only one physical type – time,
but other types can be defined at will. One example how to do this is the following.
There are two kinds of units in physical type declarations: the primary unit (at the header) and
secondary unit that are defined in terms of the primary unit.
Despite their descriptive power, the physical types are not synthesizeable.
Arrays are complex types, with a regular structure consisting of elements of the same type. The
unlimited range is specified as: range <>
There are two predefined VHDL arrays: bit_vector (with elements of the type bit) and string
(having elements of the type character).
They differ the way they define the lower limit: the first element of a bit_vector is indexed as 0 and
string starts with 1.
Notes that a single element of those arrays is specified in single quotes, but two or more elements,
called a slice, require double quotes.
Since the predefined arrays are one-dimensional, they are sometimes called vectors. Every element
in the array is ordered in one dimension. A user, however, may declare an array with an arbitrary
number of dimensions.
A typical application of an array is a memory device, which can be considered to be a two-
dimensional array of bits, grouped into words (vector of vectors).
3 2 1 0
0
1
2
3
1021
1022
1023
2.3.6 Records
Records, like arrays, belong to the complex types. They differ, however, in two features: elements of
a record can be different types and they are referenced by names instead of indexes. A reference to a
record element has the following form: record_name: element_name
These names are always written without parentheses.
The primary role of records is to group together different parameters of single object. Each such
object is referenced by a single name, which makes the code more compact and readable.
Not every record can be synthesized, but most synthesizers are able to cope with records of bit,
bit_vector, boolean and integer types.
Apart from very trivial systems, input signals must always be transformed in some way to generate
the desired output signals. This process can be visualized as: outputs <- transformations (inputs).
Since digital system signals are typically logical signals, the most often used class of VHDL
operators are logical operators as: and, or, nand, nor, xor and xor. The not operation is a one-operand
operation. The logical operations are defined for the types: Bit, Boolean and Bit_vector, so the operands
and result must be from one of these types.
In case of vectors a logical operation is performed on individual bits, i.e. the leftmost bit of the result
vector is computed on the leftmost bit of the operands and so on. This means that the operands and the
resulting vector must be of the same length.
The are use to compare two objects. They cover all kinds of comparisons:
equal to ( = )
not equal ( /= )
less than ( < )
less than or equal to ( <= )
greater than ( > )
greater than or equal to ( >= )
The compared objects must be of the same type and can be Boolean, bit, character, integer, real,
time string or bit_vector. However, the result is always Boolean.
The shift operators shift elements of one-dimensional arrays. This class of operations is restricted to
arrays, whose elements are of the types Bit or Boolean.
There are 6 shift operands: sll, srl, sla, sra, rol, ror.
sll -- shift left logical ( the new value inserted after shifting is the default logical value: ‘0’, if
signal is a bit. In case the signal was a boolean type then the default used was ‘false’)
start: ‘01101001’
step1: ‘11010010’ Register 0
step2: ‘10100100’
srl -- shift right logical ( the new value inserted after shifting is the default logical value: ‘0’, if
bit, or ‘false’, if Boolean)
sla -- shift left arithmetic ( the new value inserted after shifting is equal to the most right one.
In this case is the ‘1’)
sra -- shift right arithmetic ( the new value inserted after shifting is equal to the most left one.
In this case is the ‘0’)
rol -- rotate left logical ( the new value inserted after shifting is the one goes out )
start: ‘01101001’
step1: ‘11010010’ Register
step2: ‘10100101’
ror -- rotate right logical ( the new value inserted after shifting is the one goes out )
start: ‘01101001’
step1: ‘10110100’
step2: ‘01011010’ Register
Concatenation is a convenient way of creating new values of arrays of any types. This operation is
predefined for one-dimensional arrays.
Example:
Entity Example is
end Example;
begin
AggVec <= (Data2(3 to 5) & Data1(0 to 3) & BitOne); --- AggVec = 01010111
end Concatenation;
The assignment mechanism is simple: the target signal appears on the left, followed by the
assignment symbol (<=) and then the expression. Notes that this symbol (=>) exists also in VHDL but
has totally different use and meaning.
Examples:
x <= y <= z; -- It is not a “pipelined assignment” ( z to y and then to x), but the boolean result of
the comparison (y less or equal to z) is assigned to signal x.
a <= b or c; -- This is a typical assignment for a logical operation. (legal for boolean, bit and
bit_vector types).
k <= ‘1 ; -- This is a classical assignment. Place the value in apostrophes if it is a single bit
or a character value. Do not use apostrophes if boolean, integer, real or
enumeration types.
m <= “0101”; -- Assignment of a bit_vector or string value (depending on the type of m signal).
See that the value is in double quotes. The number of elements on the right and left
must match.
n <= m & k; -- Bit vectors can be merged with single bits to form a new vector. The number of
elements in the declared target signal must match the total number of bits merged.
In the real world nothing happens instantly, all systems have their own delay. In VHDL this fact is
modeled by the after clause added to any signal assignment statement. (The after clause cannot be
synthesized, is only use during simulation proposes).
There are two delays declaration used in VHDL: Inertial and Transport.
Inertial Delay is typical of most real-life systems and because of that, it is the default delay model in
VHDL. The after automatically assumes the inertial delay. A classical inertial process can be a led. If
applying power on to a led and then power off it, faster than the time (delay time) it needs to light, we will
not see any lighting. The same happens with the flip-flops, no signal will be output if the input pulse is
shorter than the time needed to read it. This kind of delay is also known as setup time.
The inertial and transport delay models are sufficient for describing any physical system in VHDL.
Their main similarities and differences are as follows:
•= Inertial delay is the default delay in VHDL and does not require any additional declaration.
Transport delay requires the transport keyword to be used.
•= Inertial delays do not propagate impulses shorter than the specific delays.
Transport delay propagates all changes of the input signal no matter how fast or how often
they may appears.
•= Inertial and transport delays are specified using the after clause, followed by the time value.
•= Both inertial and transport delay can be applied to signals of any type.
Process is a formal way of making such a list of sequential operations. It has a very structured
format, even if it represents behavior of only a minor part of a design.
Process is declared by the process keyword. You may assign a name to a process if you want. The
list of statements in a process starts from the keyword begin and terminate with end process.
name: process (sensitivity list)
declarations;
begin
sequential statments;
end process name;
Any process may have a sensitivity list. The process is resume any time one or more of the
parameters in the sensitivity list changes. If not parameters declared the process is always executing. In
declarations we define variables and constants.
There are various conditions that are used to stop and/or resume a process. In VHDL we use 3
types of wait statements:
wait on CLK;
wait on Enable, Data;
•= If several of these conditions are mixed, they form the fourth construct called complex.
The wait statement can be located anywhere inside the sequential statements. When the process
found a wait the execution stops and resumes (continues from the next statements after the wait) only
after the conditions are fulfilled. If the wait is the last statement of the process, it resumes starting from
the beginning.
Waiting on sensitivity list, i.e. waiting for a change of the signal value is probably the most often
used condition for resuming processes. The sensitivity_list has the same function as the wait on at the
end of a process.
Real systems have many subcircuits working at the same time. The architecture body has a
concurrent structure, which is sometimes hard to imagine because all statements (process and their
contents) are listed sequentially. Nevertheless, all processes in any architecture are executed
concurrently with each other. Whenever a signal on the sensitivity list of a process changes its value, the
process is activated. This happens regardless of whether the signal has been changed by the system’s
environments, by other processes or even by the same process (non-advisable).
Microcomputer
Signals cannot be declare inside processes. Any assignment to a signal takes effect only when
the process suspends. Until that happens, all signals keep their previous values. Only the last
assignment to a signal listed inside a process is effective.
Since signals can store only the last value assignment, they cannot be used for storing intermediate
or temporary data within a process. For that in VHDL an object called variable is used to store temporary
data. Variable are similar to signals. However because of their specific applications they can be used
only for description of algorithms inside processes and cannot have delay applied. Variables are
declared only inside processes.
An assignment to a variable is made with the ‘:=’ symbol. This assignment takes instant effect and
each variable can be assigned new values as many time as needed.
Some Processes are so simple that can be assigning inside the architecture in so called concurrent
signal assignment. These can be done because all assignments inside architecture are concurrent. A
concurrent signal assignment statement can appear inside architecture in parallel with some processes
and can be executed concurrently with other assignments, similarly to processes. If two or more
assignments appear in a process, they will be executed in the listed sequence. However, if they were
represented by concurrent signal assignment, they would be executed concurrently.
S
architecture Gates of Mux2to1 is
signal AOK, BOK, NS : bit, A AO K
begin
AOK <= A and S after 1 ns;
BOK <= B and NS after 1 ns; NS
NS <= not S after 1ns; Z
Z <= AOK or BOK after 1 ns;
end architecture Gates
B BO K
Concurrent signal assignment statements are sensitive to a change of any signal that appears to
the right from the assignment symbol. The order of the statements are not important, because all have
the same priority, all are concurrent. On the contrary from a process the assignment can be delayed with
the after clause, either with an inertial or transport delay model.
The changing environment conditions are represented in VHDL by four classes of conditional
statements:
•= Conditional execution of statements ( if…then… statements )
•= Conditional execution with alternatives ( if…then…else… and if…then…elseif… statements )
•= Multiple choice of statements ( case… statement )
•= Loops, allowing repeating the execution of some statements ( while…do… statement and
for…do… statements ).
ExProc:process
(Sensitivity_List)
while Cond3
...
loop
...
case Cond2 is others
end loop
end case
end if
...
end process
ExProc : process(Sensitivity_List )
begin
if Cond1
then
…
case Cond2 is
when Val1 => …
when Val2 => …
when others => for I in 1 to 4 loop
…
end loop;
end case;
else
while Cond3 loop
…
end loop;
…
end if;
…
end process ExProc;
Entity Control_Unit is
Port (
…
);
end ControlUnit;
Signal…<= …?
CASE CURRENT_STATE IS
END CASE;
END IF;
SYNCH: PROCESS
BEGIN
wait until CLOCK'event and CLOCK ='1';
CURRENT_STATE <= NEXT_STATE;
…
END FSM;
Some operations are performed only when listed conditions are met. Such operations are called
conditional. Syntaxes: if condition_met then execute_operations. If true go through then if false go
thru else. Let’s see in an example of a simple D-FF.
Now we give more flexibility adding a synchronous Reset and Preset to our Example of D-FF.
Syntaxes: if condition_met then execute_operations elseif condition_met then … elseif …
The if…then…elseif… construct is a multiple-choice statement that allows selecting only one
branch out of several, depending upon the encountered conditions. A more readable construction when
many decision have to be taken is the multiple-choice case statement.
architecture CaseBased of ProgrammmableGates is
ProgrGate : process (Mode, PrGIn1, PrGIn2) Mode1 Mode2 Mode3
begin
case Mode is
when “000” => PrGOut <= PrGIn1 and PrGIn2;
when “001” => PrGOut <= PrGIn1 or PrGIn2;
PrGIn1 Programmable PrGOut
when “010” => PrGOut <= PrGIn1 nand PrGIn2;
when “011” => PrGOut <= PrGIn1 nor PrGIn2; Gate
when “100” => PrGOut <= not PrGIn1; PrGIn2
when “101” => PrGOut <= not PrGIn2;
when others => PrGOut <= ‘0’;
end case;
end process ProgrGate;
end architecture CaseBased;
Important! In (case signal_name is) structure the signals used (Signals in the previous example are
Mode and PrGOut) must be static. This means that any parameter defined for them like bus width must
be declared directly or using Constant. Definition of kind Generic is not allowed. These constraints are
the same for the (with signal_name select) structure. To avoid this, use (if condition then).
This loop is iterated as long as the condition at the beginning is true. The control is passed to the
next statements after the loop finished. Syntaxes: while condition_true loop
Process
variable Count : integer := 0;
begin
wait until Clk = ‘1’;
while Level = ‘1’ loop
Count := Count + 1;
wait until Clk = ‘0’;
end loop;
end process;
The while loop repeats its contents an unlimited number of times as long as the condition specified
in the loop header is true. What happen if we want to repeat the operations only a selected number of
times. In this case we use a for loop.
A for loop does not contain an explicit boolean condition. Instead, a discrete counter is specified
with a range and as long as the counter falls within that range, the loop is iterated.
The counter range does not need to be specified in the classical for as from…to… It can also be
listed as a subtype or enumeration type. In such a case only the (subtypes is specified as the range of
the counter.
The while and for loop iterate and execute all their statements as long as the iteration condition is
met. Sometimes, however, it is decidable to skip some of the loop statements in the current iteration and
go directly to the next iteration of the same loop, or even to exit the loop and terminate the execution of
the loop.
The jump to next iteration and jump out of loop (exit) operations can be assigned a condition of
their execution. This condition are specified in a when clause which is following the statement.
Example: Counting bits with a value of ‘1’ in a bit vectors and exit if they are more than 4.
Perform a signal assignment when a condition is true. Since the if…then… conditional assignment
is a sequential statement that is restricted to processes only, you may have to use the conditional signal
assignment which can also be used inside architectures. It is a functional equivalent of the conditional
statement but is written in a different way to distinguish it from the conditional signal assignment.
The important difference between conditional signal assignment and conditional assignment is that
the first is restricted to signal assignments only and the last can be used for any kind of sequential
statements.
Example of the same Programmable Gate, this time using conditional signal assignment instead of using
the multiple-choice statement.
begin
Mode1 Mode2 Mode3
with Mode select
PrGOut <=
To conditionally assign to a signal one of several values, use the selected signal assignment
construct. It is a concurrent construct that cannot be used inside processes, where the case statement
should be used instead. The opposite is true as well: a case statement cannot be used outside
processes, were the selected signal assignment construct should be used.
Unlike the case statement, the selected signal assignment is restricted to signal assignments only and
cannot contain another statements.
As we saw already, when signals are assigned their new values inside processes, this assignment
is taking place only after the process is suspended. Moreover, if there is more then one assignment to a
signal, only the last one will take effect, because we are in presence of sequential statements.
Using the driver performs these functions. The VHDL compiler creates a driver for every signal that
is assigned a value inside a process. The rule is very simple: no matter how values are assigned to the
signal in a process, there is only one driver per signal per process. All operations are performed on the
driver, which is copied to the signal only when process suspends.
How VHDL store information about signal events outside processes, were the statements are
concurrent we will see later on “Signals with many drivers”.
Thanks to its driver a signal can have its past, current and future values. Drivers containing the
current signal value were shown on the previous box. However, drivers can also specify the projected
output waveform for a signal. Each driver can be assigned such a waveform consisting of a sequence of
one or more transaction. A transaction consists of a signal value and time. Time specifies the instant
when the driver will be assigned the new value specified by the transaction.
A waveform can be specified explicitly as a sequence of values and the associated delays related to
the same point in time. Waveforms can be considered as projected future of a signal. Since simulators
store transactions for each signal, they create in effect the history of signals.
Time [s] 0 1 2 3 4 5 6 7 8 9 10
Describing the future signals is important for creating so called test benches, where a VHDL
specification is tested in a VHDL environment and stimuli are represented by waveforms.
For example, having only the current vale would not allows us to check whether a signal has
changed recently, or to verify whether it was a rising or a falling edge. The signal history is represented
by signal attributes. The attributes are pieces of information attached to a signal and automatically
updated based on the signal’s history. The information includes the previous value of the signal
(‘last_value), the amount of time that has elapsed since the last change of the signal value
(‘last_event), etc. To get the current attribute you need to specify the signal’s name followed by an
apostrophe and the attribute’s name. Every time an assignation happens, means a ‘Transaction occurs.
Time [s] 0 1 2 3 4 5 6 7 8
SignalEx 0 0 0 0 0 1 0 0 1
SignalEx’Transaction False False True False False True True False True
SignalEx’Event False False False False False True True False True
SignalEx’Last_event Max time Max time Max time Max time Max time 0 0 1 0
SignalEx’Last_value 0 0 0 0 0 0 1 0 0
Attributes related to the history of signals are not the only attributes available. In fact, attributes are
quite often used in VHDL and the language defines 36 different attributes for different classes of objects:
scalar types, discrete types, array types, array signals and named entities. See Appendix ___.
Apart from the predefined attributes you may also define your attributes, extending the VHDL set
almost indefinitely.
Attributes have many applications – from edge detection (based on the ‘event attribute) to size-
independent specifications. Some of the most typical examples of the attribute applications are
presented in the following examples.
In order to verify whether a signal (usually CLK) has a rising or falling edge, it is necessary to
know that it just changed it value and the actual value is ‘1’ or ‘0’, respectively. The first condition can
be verified by the ‘event attribute and the entire condition looks as follows: rising edge first, then falling
edge.
To avoid hard-coded loop boundaries a range attribute can be used. No matter what is specified
for the DataArray one –dimensional array (including bit vectors), the loop will iterate exactly the same
number of times, as there are elements in the array. Such universal ranges facilitate easy code
maintenance. Note that the loop can iterate in the listed and in reverse order.
Sometimes you may need the index of the most significant bit in a bit_vector. Instead of writing it
as a “hard-coded” number, which is difficult to maintain in case of changes, it can be easily extracted
using the ‘left’ attribute. Similarly, the least significant (rightmost) bit index can be determined with the
‘right’ attribute.
ArrayType’ left
ArrayType’ rigth
Setup time is violated when the last signal event before active edge of the clock took place in less
than the “setup” time. In the example below the Setup violated boolean variable will become true
when the setup time on SomeSignal is violated. This is how errors are automatically reported in
VHDL.
When a signal has only one driver, its value is easy to determine. However, what happens when a
signal has multiple drivers? What signal values will such drivers generate?
Signal with multiple sources can be found in numerous applications. For example, the computer
data bus may receive data from the processor, memory, disk and I/O devices. Each of those device
drivers the bus and each bus signal line may have multiple drivers. Since VHDL is a language for
describing digital systems, such multiple drivers are handled in VHDL with easy.
A VHDL simulator cannot “know” in advance whether a signal with multiple drivers will never be
activated from two or more sources at the same time. Because of that the simulator must be prepared
for mixing the signals values. Such “mixing” of signals is called in VHDL resolving. The rules for mixing
signal values are specified as a table, which is called the “resolution function”. The table lists all
possible signal values in columns and rows and each cell contains information on what value will be
generated if the two values are mixed.
R G B
R R R/G R/B
G G/R G G/B
B B/R B/G B
4.5 Two-Value Logic is not enough for Resolution
As you have probably noticed on the previous example, the resolution function requires more than
the basic set of values – three basic colors needed an additional three to be solved. Similar situation
appears when considering how to represent multiple-source signal in VHDL. When we take a look at the
bit signal type, an obvious question arises: what will happen if we mix ‘0’ and ‘1’ (what is the resolved
value of ‘0’ and ‘1’?). The problem with the bit type is that is not possible to specify resolved signals with
only two values and there is not answer the above question.
This fact has very far-reaching consequences: if you are using only bit and bit_vector types, you are
NOT able to specify a microprocessor in VHDL.
Unresolved types (like the two mentioned) cannot be used for multiple-driver signals, which in turn
are necessary to specify data buses (which obviously exist in every microprocessor). To solve this
problem you must use some other logic data type, one with more than two values and a resolution
function defined for all signal values combinations.
The value of ’0’ and ‘1’ alone are not sufficient for resolving multiple-source signals. Even some
single-source signals often require more than two values for representing real-life digital objects:
•= Sometimes it is not important what value a signal has. This condition it is represented by the “don’t
care” value.
•= Tri-state buffers disconnect drivers from signal lines with a “high impedance” state, which is neither
‘0’ nor ‘1’.
•= Occasionally, a system can have an “unassigned” or “unknown” value, which is different from “don’t
care”.
NOTE: The letter ú’ inside the ulogic name indicates “unresolved types”. Such values cannot be
used with signals that have multiple drivers.
Example: Declarations.
The std_ulogic type is declare in the std_logic_1164 package and contains nine enumerated
values (MVL9). The std_ulogic_vector is a non-limited vector of std_ulogic elements.
TYPE std_ulogic is
( ‘U’ , -- Uninitialized
‘X’ , -- Forcing 0 or 1
‘0’ , -- Forcing 0
‘1’ , -- Forcing 1
‘Z’ , -- High impedance
‘W’ , -- Weak 0 or 1
‘L’ , -- Weak 0 ( for ECL open emitter)
‘H’ , -- Weak 1 (for open Drain or Collector)
‘-‘ , -- don’t care
);
The type std_ulogic and std_ulogic_vector are not defined in the VHDL standard and such
have to be used from an external package. This requires using library and the ‘use’ statements as
presented below. Usually they are specified before the entity header.
Library IEEE;
use IEEE.Std_Logic_1164.all;
All logical operators available for bit and bitvector types are available for std_ulogic and
std_ulogic_vector, respectively. This relates to and, nand, or, nor, xor and not operators. The
specification bellow shows what values can be expected from applying the AND operator to
std_ulogic values.
For greater convenience two additional functions are defined for signals of std_ulogic:
falling_edge and rising_edge. They check whether a falling edge or a rising edge, respectively, was
detected on the selected signal. The result is given as a boolean value. The D-type flip-flop in the
below representations is specified using both the rising_edge function and a description without it.
The Std_ulogic type supports all the values that may be needed to specify a typical digital system.
It is, however, unresolved, which makes it still useless in case of multiple-driven signals. Because of that
one more type is defined in Std_Logic_1164 package: std_logic. It connects the expressive power of
nine values of std_ulogic with resolution, giving a VHDL designer a really universal logical type.
Std_logic is nowadays a defacto industrial standard.
The only difference between std_logic and std_ulogic is that the former is a resolved version of the
latter. Thanks to that, all the operations and functions (including rising_edge and falling_edge) defined
for std_ulogic can be used with std_logic without any additional declarations.
There is a resolved version of std_ulogic_vector as well. Its name is quite logical: std_logic_vector
(resolved functions remove the ‘u’ from the name)
U X O 1 Z W L H -
U ‘U’ ‘U’ ‘U’ ‘U’ ‘U’ ‘U’ ‘U’ ‘U’ ‘U’
X ‘U’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’
0 ‘U’ ‘X’ ‘0’ ‘X’ ‘0’ ‘0’ ‘0’ ‘0’ ‘X’
1 ‘U’ ‘X’ ‘X’ ‘1’ ‘1’ ‘1’ ‘1’ ‘1’ ‘X’
Z ‘U’ ‘X’ ‘0’ ‘1’ ‘Z’ ‘W’ ‘L’ ‘H’ ‘X’
W ‘U’ ‘X’ ‘0’ ‘1’ ‘W’ ‘W’ ‘W’ ‘W’ ‘X’
L ‘U’ ‘X’ ‘0’ ‘1’ ‘L’ ‘W’ ‘L’ ‘W’ ‘X’
H ‘U’ ‘X’ ‘0’ ‘1’ ‘H’ ‘W’ ‘W’ ‘H’ ‘X’
- ‘U’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’ ‘X’
Behavioral VHDL description specifies systems in terms of their operations. The structural
description specifies what is the system made of, what do the subsystems or components consist of and
how they are interconnected.
Structural description allows having multiple levels of hierarchy and a component can be specified
either with a behavioral or structural description. In the latter case it is composed of sub-components,
etc. Finally, on the lowest level each primitive component is specified in behavioral way.
Behavioral Behavioral
In both cases the component declaration is treated as a general specification – something like a
catalog item. To use such items they must be instantiated in the structural specification.
Component instantiation is thus the basic statement in a structural architecture. Similar to other
architecture statements, component instances are concurrent with each other.
A component instance is not complete without a port map – a reference of the actual port name in
the higher-level structural description and formal port, which is specified in the component entity port
clause. Signals used in port maps can be either specified as port or internal to the system. The latter
must be declared in declarative section of the architecture.
Effective use of components is the focal point of the structural VHDL descriptions. The entire next
section will be devoted to this topic.
Generally, a component is any module specified either in behavioral or structural way. In its simplest
form, a component is an entity with the associated architecture. Before a component can be used, it
must be instantiated. Instantiation is selecting a compiled specification in the library and linking it with
the architecture where it will be used.
instance_name: entity Where ‘work’ is the name of the library where all
work.entity_name(architecture_name) user-defined items are saved.
Gate
And2 Clk
Clk In1
IntClk
Out1
En In2
Direct entity instantiation is the simplest way to specify a structural system. The only things that are
needed is a compiled specification of the component that will be instantiated and its instantiation
statements.
•= A mandatory label for the component that is necessary because one component can be instantiated
more than once in each architecture,
•= The keyword entity followed by the name of the library and the entity containing the specification of
the component,
•= Optionally, architecture name enclosed in brackets and the port map clause.
Since all user-defined specifications are compiled into the work library, the library in the component
instantiation statement will be called work as well.
The name of the architecture is needed only when there are several architectures in a single entity.
If the specification of a system that is being used as a component, consists of only one architecture, the
architecture name can be omitted.
entity SomeSystem is
MyGate port (…);
A
end entity SomeSystem;
C
B architecture Struct of SomeSystem is
begin
Mygate: entity …
….
end architecture Struct;
And2 Nand2
Or2 Nor2
Each port map specifies connections between ports of an entity (component) and signals in the
architecture where the component is instantiated.
These are two ways of port mapping: positional port association and named port association.
In the positional association, signals are listed in the same order as ports have been declared in the
component’s entity declaration. This way, the connections between signals and ports are specified by
their position. For example, the first signal on the list is connected to the first port, the second signal to
the second port, etc. Of course, you must use signals of the same type as their respective ports.
entity And2 is
port (In1, In2 : in bit ;
Out1 : out bit );
end entity And2;
The positional association of signals and ports seems like natural way. However, sometimes it may
cause problems in determining which signals are connected to which ports. This is particularly true when
there are many signals on the list.
A handy solution to this problem is the named association. In this type of association, signals are
assigned the names of the associated ports. This way the order of the ports is irrelevant and the direct
relationships between formal ports and actual signals can be observed.
The association between ports and signals is represented by the => symbol. Note that this symbol
has nothing to do with the direction the information flow through the port.
B entity SomeSystem is
In1 port (…);
A end entity SomeSystem;
C Out1
In2 architecture Struct of SomeSystem is
begin
MyGate: entity work.And2
port map (In1 => B,
In2 => C,
And2 Out1 => A);
…
end architecture Struct;
entity And2 is
port (In1, In2 : in bit ;
Out1 : out bit );
end entity And2;
So far we have discussed only single-bit signals. However, complex signals and ports can also be
used in component instantiation. VHDL allows great flexibility in assigning actual signals to the formal
ports of the complex types (arrays or records). Such signals/ports can be assigned element by element
or in slices. Any combination of signals and ports is allowed as long as types of the associated ports and
signals are compatible.
Note that all elements of a composite port must be associated with an actual signal.
Also, if elements of a composite port are individually associated signals, then all associations of this
port cannot be separated by other associations.
3 CF
2 ZF
Flags 1 OF
0 MF
7 OpCode(3)
6 OpCode(2)
5 OpCode(1)
4 OpCode(0)
Memory
3 OperandA(3)
2 OperandA(2)
1 OperandA(1)
0 OperandA(0)
3 OperandB(3)
2 OperandB(2)
Accumulator 1 OperandB(1)
0 OperandB(0)
Operational Register
entity OpReg is
Port(CF, ZF, OF, MF : in BIT;
OpCode, OperandA, OperandB: in BIT_Vector(3 downto 0));
…..
signal Flags : Bit_Vector(3 downto 0);
signal Memory : Bit_Vector (7 downto 0);
signal Accumulator : Bit_Vector(3 downto 0);
….
OperationalRegister : entity work.OpReg
Port map ( CF => Flags(3),
ZF => Flags(2),
OF => Flags(1),
MF => Flags(0),
OpCode => Memory(7 downto 4),
OperandA => Memory(3 downto 0),
OperandB => Accumulator,
How about situation when some of the ports are not connected? This can be handle by specifying
such ports as open in the port map.
In addition, an unused port can be omitted in the port map clause. This is perfectly legal in VHDL
but not recommended because there is no guarantee that the missing port is omitted on purpose. It may
happen, and during debugging is difficult to find the mistake. We advice to use the open status to all-non
used ports.
Comp1
If s named entity can be used as a component then why should consider other constructs for the
same purpose? Aren’t entity instantiations enough?
In case of simple structural descriptions where all the components are known in advance, direct
instantiation of entities is sufficient indeed.
However, when it comes to large designs their components are created concurrently and at some
stage it is necessary to relate to the components which structural or behavioral implementation is not yet
defined.
In such a situation for a structural specification it would be enough to have a declaration of the
component’s interface (as required by the system). Declaration of the component’s interface is called
component declaration and is located in the declarative part of the architecture body (or package
interface).
Component Component
Entity Entity
Architecture Architecture
Configuration
Component Declaration
The syntax of the component declaration and entity is similar. This is not a coincidence because
component and entity play similar role in defining the external interfaces of modules.
There is, however, an important difference between the two distinct VHDL constructs:
•= Entity declaration defines the interface of the “real” module, i.e. a system or circuit that physically
exists; the system is a separate design unit and can be individually simulated, analyzed or
synthesized.
•= Component declaration on the other hand defines the interface of “nonexistent” or “virtual” module;
it is specified within architecture and is rather an expectation of what the component interface should
be rather than a description of an existing circuit.
Component DFF is
Generic ( t_prop : time; -- propagation time
t_setup : time); -- setup time
begin
…
A component instantiation is very similar to an entity instantiation. It has the following main
elements:
•= A colon follows Instantiation label, this label is mandatory in order to identify the instance.
•= Optional component keyword, which indicates that it is a component instantiation.
•= Component name as it is specified in the component declaration.
•= Generic map clause, which provides actual values for the component’s generics. Generics can be
specified without a value and in such cases the actual value can be provided in the generic map
clause. If the component is specified without generics, this clause can be omitted.
•= Port map clause, which is specified in the same way as in case of direct instantiations.
Note: There is no semicolon after the component’s name and the generic map clause. All the above
elements from a single statement and the semicolon appear after the statement.
Q B Q B Q B Q B
i i i i
Rst t Rst t Rst t Rst t
D Clk 0 D Clk 1 D Clk 2 D Clk 3
Entity Reg4 is
Port ( RegD : in bit_vector(3 downto0);
Clk, Rst : in bit;
RegQ : out bit_vector(3 downto 0));
end entity Reg4;
component DFF is
generic (t_prop : time; t_setup : time);
port ( D, Clk, Rst : in bit;
Q : out bit);
end component DFF;
begin
The Component as we seen before is an easy way of organizing the project. It uses is very tied to
package uses because of the needs to be reused by other part of code or even by other program. The
combination of a package including component or/and function is the library.
Components are declared in package and in architecture. The description part are described in
entity + architecture.
Functions are declared in package. The description part is defined in package body.
entity component_2 is
port (…);
end component_2;
A declaration of a component and its instantiation is not enough to have a complete specification of
a structural architecture because implementation description of the components would still be missing.
The information that binds a component to real entities and architectures is specified in the
configuration that is a separate design unit that can be simulated and analyzed apart from other units.
When you take a closer look at the component binding you may notice that is very similar to direct
entity instantiation. However, the configuration approach is more flexible and easier to maintain if a
different implementation of the same component must be used. If there are any changes, they need only
to be introduced in the configuration file that is relatively short. The structural architecture will remain
unchanged. Using direct instantiations would require all changes to be introduced into the code.
Configurations can become quite complex if multiple levels of hierarchy are used. The example
below shows a simple configuration case.
for Struc
- Refers to a specific architecture within the entity. for architecture_name
- All the information in between, if some, refers to the (component configuration)
component configuration inside that architecture. end for;
end for
- Each binding indication is enclosed in the for…end for clauses.
end for
- End of the binding indication for…end for.
•= procedure declaration, which contains the procedure name and the parameter list required when
the procedure is called;
•= procedure body, which consists of the local declaration and statements required to execute the
procedure.
Declaration of a procedure are local to this declaration an can declare subprogram declarations,
subprogram bodies, types, subtypes, constants, variables, files, aliases, attribute declarations attribute
specifications, use clauses, group template and group declarations.
There are three modes available: in, out and inout. When in mode is declared and object class is
not defined, then by default it is assumed that the object is a constant. In case of inout and out modes,
the default class is variable. A procedure can be declared also without any parameters.
The procedure body defines the procedure’s algorithm composed of sequential statements. A
procedure can contain any sequential statements (including wait statements). A wait statement,
however, cannot be used in procedures which are called from a process with a sensitivity list or from
within a function.
The overload procedures are procedures with the same name but different number or different types
of formal parameters. The actual parameters decide which overload procedure will be called.
…
Procedure Calculate(In1, In2: in Real; signal Out1: inout Integer);
Procedure Calculate(In1, In2: in Integer; signal Out1: inout Real);
Important notes:
•= The procedure declaration is optional - Procedures body can exist without it declarative part.
•= Subprograms (procedures and functions) can be nested.
•= Subprograms can be called recursively.
•= Synthesis tools usually support procedures as long as they do not contain the wait statement.
A function call is a subprogram of the form of an expression that returns a value. It is a subprogram
that either defines an algorithm for computing values or describes a behavior. The important feature
of functions is that they are used as expressions that return values of specified type. This is the
main difference from another type of subprograms: procedures, which are used as statements.
library IEEE;
use IEEE.std_logic_1164.all;
PACKAGE my_functions IS
-- <function or_tree>
-- or of a std_logic_vector implemented in a tree structure
function or_tree (data : std_logic_vector) return std_logic;
end my_functions;
END cpu_functions;
A design is incomplete without verification. There are several ways to verify a VHDL design. The
most popular solution is using test benches. A test bench is an environment, where a design (called
design or unit under test) is checked by applying signals (stimuli) and monitoring its responses by
observing signal probes and monitors.
A test bench is composes of the instantiation of the Unit Under Test (UUT) and processes
supporting the stimuli applied to the UUT. In this way a hybrid specification is created that mixes both
structural and behavioral types of statements.
Stimuli for the UUT are specified inside the test bench architecture or can be read from an external
file. The reactions of the UUT, on the other hand, can be observed either trough the simulator outputs
(waveforms plot on the screen), or written to a file using VHDL text I/O operations.
The VHDL test bench is just another specification with its own entity and architecture. It has,
however, a special structure with some elements that are characteristic to this type of specifications.
A VHDL test bench does have one important feature, they do not have ports or generics. The
reason for that is very simple – test bench is not a real device or system that will have to communicate
with its environment. All the values for the input ports of a UUT are specified inside the test bench
architecture as stimuli. The outputs are observed through the simulator watch or waveform display and
are saved to a file. Why are we treating test bench as entity when it is practically only architecture?
Because in VHDL, the architecture definition cannot be specified without specifying its entity.
When a Unit Under Test and a Test Bench for it are complete, the verification can be started. Note
that it is the test bench, which is simulated, not the UUT. The UUT is only one of the component
instantiated in a test bench. There is no limitation on VHDL simulator used capability.
Design
Design Specification
Verification
Design Specification
Design Specification
Design Specification
A system that will be verified with a test bench does not need any modifications or additional
declarations. For that reason a test bench can be applied to any VHDL specification, even received from
an external source.
The unit under test has to be instantiated in the test bench architecture. This can be done in the
same way as in any structural specification – either through a direct instantiation or component
instantiation with component declaration and a configuration. The ports of the UUT instantiation will be
assigned stimuli signals.
As both processes (as a whole) and component instantiations are concurrent statements, it does not
matter whether the UUT will be instantiated first and the stimuli will be defined next or vice versa.
The heart of each test bench is a set of stimuli - a sequence of values for each UUT input signal
applied over time. As we said before, since the test bench does not communicate with the environment
through signals, all stimuli must be declared internally in the test bench architecture. They are declared
like any other signal inside the declarative part of the architecture.
The stimuli can be specified both as concurrent signal assignments (with input signal changes
specified as waveforms) or in a process that contains signal assignments separated by the wait for
statements, introducing delays between subsequent signal assignment. In the latter case, an empty wait
(without any condition) is added as the last statement in the process. Such a statement causes the
simulation to suspend indefinitely (otherwise it will run from the beginning of the process again).
Finally, the relationship between stimuli and the unit under test is reached through the assignments
in the port map clause of the UUT instantiation.
Example: Lets see a Mux2to1 code description with both of Test Bench that can be used: Sequential
or Concurrent.
-- Unit Under Test Declaration
entity Mux2to1_T is
generic ( MuxDel : time := 5 ns);
port ( A, B, : in std_logic_vector(1 downto 0);
Sel : in std_logic;
Y : out std_logic_vector(1 downto 0));
end entity Mux2to1_T;
The last element of successful device verification is getting the simulation results or reporting
results. This can be done in several ways, from using built-in simulator features such as displaying list of
signal values changing over time or waveform displays, to writing a complete simulation log to a file, or
using the VHDL report clause
The last option is easy to apply and is usually used to display a message when something goes
wrong. If this option is used and no reports are displayed during the simulation, then it can be assumed
that the UUT worked as expected.
A report clause consists of three elements: assert statement (checking a Boolean condition),
report statement (defining a message that will be displayed when the condition is false) and a severity
statement (informing the simulator how severe the erroneous situation is – from just a general note to a
system failure).
Assert ( LeftNum > RighNum)
report “RightNum greater then LeftNum”
severity warning;
In this example if the RighNum is bigger than the LeftNum then the monitor screen the message:
“RightNum greater then LeftNum”
The assertion statement is by nature sequential and as such is used inside processes. Its
application is not limited to test benches, but it is here where it is most frequently used.
A general rule of thumb in applying assertions for reporting incorrect responses of the UUT to the
stimuli is as follows:
•= Use an assert-report pair each time a new value of UUT output is expected;
•= Specify the error expected value as the condition in assertion;
•= Try to be specific in the report text string, a simple Error will tell you nothing during simulation;
write what is wrong and when it happened (i.e. in what situation on the inputs).
Remember that the new values are assigned only when process is suspended – do not expect a
change on the outputs right after assigning new values to the inputs!
package data_types_Example is
end data_types_Example;
library IEEE;
library work;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use work.data_types_Example.all;
package components_Example is
component extclk_1
port (
hds_ceo : out std_logic_vector(0 downto 0)
);
end component;
component dff_synch
port (
d : in std_logic_vector(0 downto 0);
pre : in std_logic_vector(0 downto 0);
clr : in std_logic_vector(0 downto 0);
q : out std_logic_vector(0 downto 0);
q_bar : out std_logic_vector(0 downto 0);
hds_cei : in std_logic_vector(0 downto 0);
lock : in std_logic_vector(0 downto 0)
);
end component;
component dff_asynch
port (
d : in std_logic_vector(0 downto 0);
pre : in std_logic_vector(0 downto 0);
clr : in std_logic_vector(0 downto 0);
q : out std_logic_vector(0 downto 0);
q_bar : out std_logic_vector(0 downto 0);
hds_cei : in std_logic_vector(0 downto 0);
lock : in std_logic_vector(0 downto 0)
);
end component;
end components_Example;
library IEEE;
library work;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use work.components_Example.all;
use work.data_types_Example.all;
entity extclk_1 is
port (
hds_ceo : out std_logic_vector(0 downto 0)
);
end extclk_1;
entity dff_synch is
port (
d : in std_logic_vector(0 downto 0);
pre : in std_logic_vector(0 downto 0);
clr : in std_logic_vector(0 downto 0);
q : out std_logic_vector(0 downto 0);
q_bar : out std_logic_vector(0 downto 0);
hds_cei : in std_logic_vector(0 downto 0);
lock : in std_logic_vector(0 downto 0)
);
end dff_synch;
library IEEE;
library work;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use work.components_Example.all;
use work.data_types_Example.all;
entity dff_asynch is
port (
d : in std_logic_vector(0 downto 0);
pre : in std_logic_vector(0 downto 0);
clr : in std_logic_vector(0 downto 0);
q : out std_logic_vector(0 downto 0);
q_bar : out std_logic_vector(0 downto 0);
hds_cei : in std_logic_vector(0 downto 0);
lock : in std_logic_vector(0 downto 0)
);
end dff_asynch;
library IEEE;
library work;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use work.components_Example.all;
use work.data_types_Example.all;
end hds_body_1;
library IEEE;
library work;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use work.components_aptix.all;
use work.data_types_aptix.all;
P2 : process ( clock )
begin
if ( clock'event and clock = '1' ) then
if ( pre(0) = '1' ) then
dff_state <= "1";
else
dff_state <= d;
end if;
end if;
end process P2;
end hds_body_2;
Entity SER_PAR is
Port (
SERIAL_IN : IN BIT;
CLOCK : IN BIT;
RESET : IN BIT;
PARALLEL_OUT : OUT PARALLEL_TYPE;
PARITY_ERROR : OUT BIT;
READ_ENABLE : OUT BIT
);
end SER_PAR;
SYNCH: PROCESS
BEGIN
wait until CLOCK'event and CLOCK ='1';
CURRENT_STATE <= NEXT_STATE;
CURRENT_HIGH_BIT <= NEXT_HIGH_BIT;
CURRENT_PARALLEL_OUT <= NEXT_PARALLEL_OUT;
END PROCESS SYNCH;
END BEHAVIOR;
------------------------------------------------------------------------------
-- Description of the package: funzioni
------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
-- ------------------------------------------------------------------------ --
-- SIMULATION FUNCTION PROTOTYPES: --
-- ------------------------------------------------------------------------ --
PACKAGE funzioni IS
END funzioni;
-- ------------------------------------------------------------------------ --
-- FUNCTIONS: --
-- ------------------------------------------------------------------------ --
PACKAGE BODY funzioni IS
BEGIN
CASE value IS
WHEN '0' | '1' => RETURN value;
WHEN 'H' => RETURN '1';
WHEN 'L' => RETURN '0';
END rat;
-- ------------------------------------------------------------------------ --
FUNCTION std_logic_vector_2_var (vect: std_logic_vector) RETURN std_logic_vector IS
BEGIN
FOR i IN vect'RANGE LOOP
mvl(i) := rat(vect(i));
END LOOP;
RETURN mvl;
END std_logic_vector_2_var;
-- ------------------------------------------------------------------------ --
FUNCTION setall0(width : integer) RETURN std_logic_vector IS
BEGIN
return vect;
END setall0;
END funzioni;
------------------------------------
-- VHDL core description
------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use work.funzioni.all;
entity parallel2serial is
GENERIC (datawidth : INTEGER := 8);
port(pi : in std_logic_vector(datawidth-1 downto 0); -- Parallel Data Input
sdi : in std_logic; -- Serial Data Input
l : in std_logic; -- Load
c : in std_logic; -- Clock
ce : in std_logic; -- Clock enable
sdo : out std_logic; -- Serial Data Output
po : out std_logic_vector(datawidth-1 downto 0)); -- Parallel Data Output
end parallel2serial;
begin
process
variable setup : boolean := TRUE;
variable vsdi : std_logic;
vpi := std_logic_vector_2_var(pi);
vsdi := rat(sdi);
vl := rat(l);
-- the { if (clk'event and clk='1'and cte='1') then } is not correct in Synopsys Style use wait until.
case vl is
when '0' => -- Shifting
for i in 0 to datawidth-2 loop
s(i) := s(i+1);
end loop;
s(datawidth-1) := vsdi;
po <= s;
sdo <= s(0);
end process;
end behv;