VHDL Operators, VHDL for Serial Comparator
Table of Contents
VHDL Operators, VHDL for Serial Comparator, WaveFormer Pro and DataSheet Pro Example Stimulus Code
VHDL Operators
The VHDL operators are divided into four categories:
- Arithmetic operators
- Shift operators
- Relational operators
- Logical operators
Arithmetic operator
For the sake of simplicity, let’s classify the operators into three categories.
- Basic arithmetic operators
- Advanced arithmetic operators
- Special arithmetic operators
Basic arithmetic operators are used in almost every program, we will constantly come across examples, and they are easy to use with very low to no syntax restrictions.
Advanced arithmetic operators are not as frequently used but are equally important and powerful if used correctly. Again it is easy to use with a few syntax restrictions.
Special arithmetic operators perform nearly identical operations to simple arithmetic operators, the only difference being they are used under special conditions, with a specific syntax.
Basic arithmetic operator
The four basic arithmetic operators include,
- Addition
+
- Subtraction
-
- Multiplication
*
- Division
/
We can use these operators to perform basic mathematics in VHDL, and the syntax is as follows.
Addition
A simple addition operator, the syntax is as follows
<numeric> + <numeric>;
A '+'
sign is used to specify the addition between two numeric values. The <numeric>
is any numeric value given for addition, the yield is a summation of both the numeric values.
Subtraction
A simple subtraction operator, the syntax is as follows
<numeric> - <numeric>;
A '-'
sign is used to specify the subtraction between two numeric values. The <numeric>
is any numeric value given for subtraction, the yield is the difference for both the numeric values.
Multiplication
A multiplicative operator, the syntax is as follows
<numeric> * <numeric>;
A '*'
sign is used to specify the multiplication between two numeric values. The <numeric>
is any numeric value given for subtraction, the yield is the multiplication of both the numeric values.
Division
A Division operator, the syntax is as follows
<numeric> / <numeric>;
A '/'
sign is used to specify the division between two numeric values. The <numeric>
is any numeric value given for subtraction, and the yield is the quotient after the division of both the numeric values.
Any kind numeric value is allowed such as an integer, float, binary etc. Unless a certain value type is specified.
Advanced arithmetic operator
Moving on to advanced mathematical operators. Here we have the following operators:
- Exponent
**
- Absolute value
abs
- Modulo
mod
- Remainder
rem
- Component
not
These operators are extremely important for advanced mathematics in VHDL. Let us have a look at the syntax for these operators.
Exponent
For the exponent operator, the syntax is as follows:
<numeric> ** <numeric>;
A '**'
sign is used to specify the use of exponentiation. The <numeric>
is any numeric value given for the operation, and the yield is the exponentiation of the numeric values. Let’s use an equation to understand the syntax better.
Absolute value
For the absolute value operator, the syntax is as follows:
abs <numeric>;
An 'abs'
is used to specify the absolute value. The <numeric>
is any numeric value given for the operation, and the yield is the absolute value, a.k.a. modulus for the given numeric value.
Modulo
A modulo operator, the syntax is as follows
<integer> mod <integer>;
A 'mod'
is used to specify the modulo operator. <integer>
values are used for the operation, and the yield is the modulo for the given integer values.
Remainder
For the remainder operator, the syntax is as follows:
<integer> rem <integer>;
An 'rem'
is used to specify the remainder operator. The <integer>
values are used for the operation, and the yield is the remainder for the given integer values.
Complement
For the complement operator, the syntax is as follows:
not <logic or boolean>;
A 'not'
is used to specify the complement operator. Logic or boolean data type (TRUE or FALSE) is used for this operation, the result is of the same data type and is a complement of the data provided.
Special arithmetic operator
Special arithmetic operators are used when special conditions arise. These operators include,
- Unary plus
+
- Unary minus
-
- Concatenation
&
Unary plus
For the unary plus operator, the syntax is as follows:
+ <numeric>;
An '+'
is used before the <numeric>
to specify a unary plus operator. The <numeric>
value is used for the operation, and the yield is a numeric value. When this operator is used, the numeric value returns unchanged.
Unary minus
For the unary plus operator, the syntax is as follows:
- <numeric>;
An '-'
is used before the <numeric>
to specify a unary minus operator. The <numeric>
value is used for the operation, and the yield is a numeric value. When this operator is used, the numeric value returns with the negated sign. Remember to use this operator with parenthesis in the case of any other operator preceding it. For example, 4 * -2 is invalid. It should be 4 * (-2).
Concatenation
For the concatenation operator, the syntax is as follows:
<array or element> & <array or element>;
An '&'
is used before the <array or element>
to specify a concatenation operator. The <array or element>
value is used for the operation, and the yield is a concatenation (joining) of the arrays or elements value.
Shift operator (VHDL)
Shift operators are used to shift an element of an array of numbers or alphabets left or right by a desired number of steps. Further elaboration with each operator is provided below. Shift operators include:
- Shift Left logical
sll
& Shift Right logicalsrl
- Shift Left arithmetic
sla
& Shift Right arithmeticsra
- Rotate Left
rol
& Rotate Rightror
Shift Left logical & Shift Right logical
<logical array> sll <integer>; --shift left logical <logical array> srl <integer>; --shift right logical
The sll
is the left logical shifter and srl
is the right logical shifter. The <integer>
is used as the number of times we want to shift <logical array>
left or right logically. The result of these operations is also a logical array.
Shift Left arithmetic & Shift Right arithmetic
<logical array> sla <integer>; --shift left arithmetic <logical array> sra <integer>; --shift right arithmetic
The sla
is the left arithmetic shifter and sra
is the right arithmetic shifter. The <integer>
is used as the number of times we want to shift <logical array>
left or right arithmetically. The result of these operations is also a logical array.
Rotate Left and Rotate Right
<logical array> rol <integer>; --rotate left <logical array> ror <integer>; --rotate right
The rol
is the left rotator and ror
is the right rotator. The <integer>
is used as the number of times we want to rotate<logical array>
left or right. The result of these operations is also a logical array.
Shift Functions
A more optimum method for the implementation of shifting is by using shift functions. Shift functions are found in the numeric_std
package of VHDL. These instructions have a simple syntax and are easy to understand. The keywords are shift_left()
and shift_right()
. The functions require two inputs: the signal to shift and the number of bits to shift by. There are two types of shifting:
- Arithmetic shift – Here, the positions left vacant after shifting are substituted with the right bits to keep the sign of the original number intact.
- Logical shift – Here, the positions left vacant after shifting are filled with ‘0’.
We can perform shifting logically and arithmetically use keywords unsigned
and signed
respectively.
Let us have a look at the syntax.
1. syntax for logical shift (unsigned) <initial_string> --contains the initial string <shifted_string> = shift_left(unsigned(<initial_string>), <integer>); <shifted_string> = shift_right(unsigned(<initial_string>), <integer>); 2. syntax for arithmetic shift (signed) <initial_string> --contains the initial string <shifted_string> = shift_left(signed(<initial_string>), <integer>); <shifted_string> = shift_right(signed(<initial_string>), <integer>); 3. example for shigting logically (unsigned) <initial_string> --contains the initial string <shifted_string> = shift_left(unsigned(<initial_string>), 1); --shifts the <initial_string> towards the left logically by 1 <shifted_string> = shift_right(unsigned(<initial_string>), 1); --shifts the <initial_string> towards the right logically by 1 4. example for shigting arithmetically (signed) <initial_string> --contains the initial string <shifted_string> = shift_left(signed(<initial_string>), 1); --shifts the <initial_string> towards the left arithmetically by 1 <shifted_string> = shift_right(signed(<initial_string>), 1); --shifts the <initial_string> towards the right arithmetically by 1
The <initial_string>
could be any kind of string std_logic
, signed
, unsigned
etc. The shift_left
indicates a shift towards the left and shift_right
indicated a shift towards the right. And the <integer>
is used to indical the number of steps we want the string to be shifted.
Relational operator
As the name suggests, relational operators are used to test the quantitative relationship of two numbers or/and characters. It could also be two strings/arrays of numbers or characters.
We use these relational operators to compare these elements, and the result is a yield of boolean values: 1=true and 0=false.
The relational operators include
- Test for equality
=
and inequality/=
- Test for less than
<
and less than or equal<=
- Test for greater than
>
and greater than or equal>=
Test for equality and inequality
These operators check if the given data is equal or not. In the case of the test for equality ‘=,’ if the given data is equal, the result is a boolean true, and if unequal, the result is a boolean false.
In the case of the test for inequality ‘/=,’ if data is inequal, the result is a boolean true, and if the data is equal, the result is a boolean false. Let us use an ‘if-else’ statement to better understand the concept.
<instance_A> <instance_B> --two data instances process (check_equality) --Equality begin if (<instance_A> = <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process; <instance_A> <instance_B> --two data instances process (check_inequality) --inEquality begin if (<instance_A> /= <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process;
The <instance_A>, <instance_B> are data instances of any data type. In the first process, the test for equality if <instance_A> is equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> and <instance_B> are not equal the <instance_C> is given a binary 0 value.
In the second process, the test for inequality if <instance_A> is not equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> and <instance_B> are equal the <instance_C> is given a binary 0 value.
Test for less than and less than or equal
These operators check the relation for the given data A and B. In the case of less than ‘<‘, if in the given data A is less than but not equal to B, the result is a boolean true. And if A is greater than or equal to B, the result is a boolean false.
In the case of test for less than or equal ‘<=’, if A is less than or equal to B, the result is a boolean true. And if A is greater than B, the result is a boolean false. Let us use an ‘if-else’ statement to better understand the concept.
<instance_A> <instance_B> --two data instances process (check_lessthan) --less than begin if (<instance_A> < <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process; <instance_A> <instance_B> --two data instances process (check_lessthan_equal) --less than or equaly begin if (<instance_A> <= <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process;
The <instance_A>, <instance_B> are data instances of any data type. In the first process, the test for less than if <instance_A> is less then and not equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> greater than or equal to <instance_B> only then the <instance_C> is given a binary 0 value.
In the second process, the test for less than or equal if <instance_A> is less than or equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> greater <instance_B> only then the <instance_C> is given a binary 0 value.
Test for greater than and greater than or equal
These operators check the relationship between given data A and B. In the case of greater than ‘>’, if A is greater than but not equal to B, the result is a boolean true. If A is less than or equal to B, the result is a boolean false.
In the case of test for greater than or equal ‘>=’, if A is greater than or equal to B, the result is a boolean true. If A is less than B, the result is a boolean false. Let us use an ‘if-else’ statement to better understand the application.
<instance_A> <instance_B> --two data instances process (check_greaterthan) --greaterthan begin if (<instance_A> > <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process; <instance_A> <instance_B> --two data instances process (check_greaterthan_equal) --greaterthan or equal begin if (<instance_A> >= <instance_B>) then <instance_C> <= '1'; else <instance_C> <= '0'; end if; end process;
The <instance_A>, <instance_B> are data instances of any data type. In the first process, the test for greater than if <instance_A> is greater then and not equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> less than or equal to <instance_B> only then the <instance_C> is given a binary 0 value.
In the second process, the test for greater than or equal if <instance_A> is greater than or equal to <instance_B> only then the <instance_C> is given a binary 1 value. If <instance_A> less <instance_B> only then the <instance_C> is given a binary 0 value.
Logical operator (in VHDL)
Logical operators perform logic operations between logical array or boolean data. And Logical operators in VHDL include:
- Logical AND
and
- Logical OR
or
- Logical NOT of AND
nand
- Logical NOT of OR
nor
- Logical Exclusive of OR
xor
- Logical Exclusive of NOT of OR
xnor
1. Logical AND
Performs logical AND operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> and <logical _array>; <boolean_storage> <= <boolean> and <boolean>;
The and
keyword is used to specify an AND between the two elements. The AND operation is performed and stored in their respective <logical_array_storage> or <boolean_storage>.
2. Logical OR
Performs logical OR operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> or <logical _array>; <boolean_storage> <= <boolean> or <boolean>;
The or
keyword is used to specify an OR between the two elements. The OR operation is performed and stored in their respective <logical_array_storage> or <boolean_storage>.
3. Logical NAND
Performs logical NAND operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> nand <logical _array>; <boolean_storage> <= <boolean> nand <boolean>;
The nand
keyword is used to specify a NAND between the two elements. The nand operation is performed and stored in their respective <logical_array_storage> or <boolean_storage>.
4. Logical NOR
Performs logical NOR operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> nor <logical _array>; <boolean_storage> <= <boolean> nor <boolean>;
The nor
keyword is used to specify a NOR between the two elements. The NOR operation is performed and stored in their respective <logical_array_storage> or <boolean_storage>.
5. Logical XOR
Performs logical XOR operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> xor <logical _array>; <boolean_storage> <= <boolean> xor <boolean>;
The xor
keyword is used to specify an XOR between the two elements. The XOR operation is performed and stored in their respective <logical_array_storage> or <boolean_storage>.
6. Logical XNOR
Performs logical OR operation with a logical array or boolean.
<logical_array_storage> <= <logical_array> xnor <logical _array>; <boolean_storage> <= <boolean> xnor <boolean>;
VHDL for Serial Comparator
Things to observe:
1. Flip-flop implementation: reset priority, event, rising edge sensitive.
2. If and case — sequential statements — are valid only within a process.
3. Concurrent assignment is a “process.”
4. Semantics of a process: sensitivity list, assignments:
5. b <= a;
6. c <= b;
does not behave as it would in C.
7. VHDL architecture broken into three processes:
a) State storage.
b) Next state generation.
c) Output generation.
VHDL for serial comparator. The inputs a and b are input lsb first.
– The Mealy machine uses rising edge sensitive flip-flops and an
asynchronous active low reset.
The output is 1 if b > a, otherwise 0.
library ieee;
use ieee.std_logic_1164.all;
entity comparator is port
(a, b, clk, reset : in std_logic; o : out std_logic );
end comparator;
architecture process_defn of comparator is
— Two states needed.
type state_type is (S0, S1); — State assignment.
attribute enum_encoding : string; attribute enum_encoding of state_type :
type is “0 1”;
signal state, next_state : state_type;
— For convenience, concatenate a and b. signal inputs : std_logic_vector (1 downto 0);
begin
Concurrent assignment executes the rhs changes.
– Concatenate a and b into inputs.
inputs <= a & b;
Processes execute whenever something on their sensitivity list
— changes. All assignments take place when the process exits.
This process implements the D flip-flop.
state_register : process (clk, reset) begin
— If/else construct only valid within a process. if (reset = ‘0’) then
state <= S0;
elsif (clk’event AND clk = ‘1’) then state <= next_state;
end if; end process;
— This process computes the next state.
next_state_process : process (inputs, state) begin
case state is
when S0 =>
if (inputs = “01”) then next_state <= S1;
else
next_state <= S0; end if;
when S1 =>
if (inputs = “10”) then next_state <= S0;
else
next_state <= S1; end if;
end case; end process;
— This process computes the output.
output_process : process (inputs, state) begin
case state is
when S0 =>
if (inputs = “01”) then o <= ‘1’;
else
o <= ‘0’; end if;
when S1 =>
if (inputs = “10”) then o <= ‘0’;
else
o <= ‘1’; end if;
end case; end process;
end process_defn;
A test bench is a virtual environment used to verify the correctness or soundness of a design or model (e.g., a software product).
The term has its roots in the testing of electronic devices, where an engineer would sit at a lab bench with tools of measurement and manipulation, such as oscilloscopes, multimeters, soldering irons, wire cutters, and so on, and manually verify the correctness of the device under test.
In the context of software or firmware or hardware engineering, a test bench refers to an environment in which the product under development is tested with the aid of a collection of testing tools. Often, though not always, the suite of testing tools is designed specifically for the product under test.
A test bench or testing workbench has four components.
- INPUT: The entrance criteria or deliverables needed to perform work
2. PROCEDURES TO DO: The tasks or processes that will transform the input into the output
3. PROCEDURES TO CHECK: The processes that determine that the output meets the standards.
4. OUTPUT: The exit criteria or deliverables produced from the workbench
Free yourself from the time-consuming process of writing Verilog and VHDL test benches by hand. Generate them graphically from timing diagrams using SynaptiCAD’s TestBencher Pro, WaveFormer Pro, DataSheet Pro, VeriLogger, and BugHunter Pro products. With 3 levels of test bench generation you can choose the product that meets the type and complexity of your testing needs. For basic test benches, WaveFormer can import waveform data from just about anywhere and generate stimulus vector test benches in a matter of minutes. BugHunter and VeriLogger also support basic stimulus generation and also include a fast, interactive unit-level testing environment. For more testbench flexibility, the Reactive Test Bench generation Option can be added to generate single timing diagram based test benches that react to the model under test. And for the most complex testing needs, TestBencher Pro generates test benches that are complete bus-functional models that monitor and react during runtime simulations.
WaveFormer Pro and DataSheet Pro stimulus code example
BugHunter Pro and VeriLogger Extreme interactive testing example
WaveFormer Pro, Data Sheet Pro, and BugHunter come with the basic stimulus test bench generation features. Drawn waveforms are used to generate stimulus models. The BugHunter features are tightly integrated into the simulation environment to allow quick interactive testing of design models.
· Reactive Test Bench Option example
The Reactive Test Bench Generation Option is an option that can added to WaveFormer Pro, DataSheet Pro, and the BugHunter Pro products. This option allows users to create self-testing test benches from a single timing diagram which generate error reports and react to the model under test during simulation. It also enables generation of “clocked test benches” that update stimulus based on one or more clock signals.
· TestBencher Pro code example
The highest level of testbench generation is provided by TestBencher Pro, which allows a user to design bus functional models using multiple timing diagrams to define transactors and a sequencer process to apply the diagram transactions. TestBencher can added to BugHunter or purchased as a standalone product.
Code Generation Examples
In the following examples we will show you how some of our customers have used each of these products. We will also show some code samples so you can get an idea of exactly what type of code is generated for each product.
WaveFormer Pro and DataSheet Pro Example Stimulus Code
WaveFormer Pro and DataSheet Pro generate VHDL and Verilog stimulus models from waveforms that are displayed in the timing diagram window. Both of these products are timing diagram editors with features that are described in WaveFormer Pro and DataSheet Pro pages.
For generating quick and small test benches, the drawing environment can be used to develop the stimulus vectors. This is much faster and accurate than attempting to hand-code a small test bench, because the temporal relationships between edges are easier to see in a graphical timing diagram then in raw VHDL or Verilog code.
For large test benches, the waveform data can be imported from an outside source like a logic analyzer, simulator, or spreadsheet. For example, one customers designed an ASIC for use in an existing communications system. He used a logic analyzer to capture stimulus vectors from the communications system, then used WaveFormer to translate the data into a VHDL test bench which he used to test the ASIC design.
Once a timing diagram is finished, code generation is simply a file save operation using the Export > Export Timing Diagram menu option. Wave Former generates either a Verilog model or a VHDL entity/architecture model for the stimulus test bench. This test bench model can then be instantiated in a user’s project and compiled and simulated with the rest of the design. Below is an example of a timing diagram and some of the VHDL code that was generated from the timing diagram.
In the generated code, notice that the clock is a parameterized process. During simulation, a user can easily modify the operation of the test bench by changing the values of the clock variables.
WaveFormer also supports complex data types and user-defined types. Notice that SIG1 has a VHDL type of integer. In Wave Former, the VHDL and Verilog types of signals can be changed using the Signals Properties dialog. VHDL user-defined types can also be entered through the same interface.
— Generated by Wave Former Pro
Version library ieee, std;
use ieee.std_logic_1164.all;
entity stimulus is port (
-SIG0 : out std_logic := ‘Z’;
-SIG1 : out std_logic_vector(3 downto 0) := “ZZZZ”; SIG2 : out integer;
SIG3 : out MyColor;
CLK0 : out std_logic := ‘Z’); — more entity code
end stimulus;
architecture STIMULATOR of stimulus is
- — some signal and parameter declarations begin
- — clock and status setup code
- — Clock Process
CLK0_process : process variable CLK0_low : real; variable CLK0_high : real; begin
tb_mainloop : loop
wait until (tb_status = TB_ONCE)
or (tb_status = TB_LOOPING); CLK0_high := CLK0_Period * CLK0_Duty / 100.0; CLK0_low := CLK0_Period – CLK0_high;
— more clock code end loop;
end process;
— Sequence: Unclocked Unclocked : process begin
SIG0_driver <= ‘0’; SIG1_driver <= x”3″; SIG2_driver <= 1; SIG3_driver <= Yellow; wait for 45.0 ns; SIG1_driver <= x”F”; wait for 5.0 ns;
— more signal statements
wait;
end process;
end STIMULATOR;
BugHunter Pro and VeriLogger Extreme – Fast Unit-Level Testing
BugHunter Pro is the graphical debugging interface for VeriLogger Extreme and other commercial VHDL and Verilog simulators. It is unique in that we have integrated our test bench generation features very closely with the simulator engine. Model testing is so fast in BugHunter Pro that you can perform true bottom-up testing of every model in your design, a critical step often skipped in the verification process because it has traditionally been very time consuming.
Once finish writing an HDL model for your design, BugHunter Pro will extract the signals or the ports in the top-level module and automatically add them to the Diagram window. The output ports are displayed as purple (simulated) signals and input ports are displayed as black signals. Input signals waveforms can be graphically drawn, generated by equations, or copied from existing signals. When a simulation is requested, BugHunter automatically wraps a test bench around the top-level module and creates signals in this test bench to drive and watch the top-level module.
BugHunter can also be put into an interactive simulation mode, so that each time an input signal is changed, a new test bench is generated and a simulation is performed. This makes it easy to quickly test small parts of a design before the design is complete. It also allows you to quickly test ideas without being forced to generate a comprehensive test bench.
In the below example, we have hand coded a 4-bit Adder model and we wish to quickly test the model. First we put the “add4.v” file into the Project window and press the yellow build button. BugHunter then scans the model and checks for syntax errors and inserts the top-level ports into the timing diagram window. At this point, the user can begin to draw waveforms on the black input signals. Since BugHunter is in the “Sim Diagram & Project” mode, when the user presses the green run button, BugHunter will generate a test bench from the drawn input waveforms and perform the simulation.
Outputs of the simulation will be displayed in the same diagram as the input stimulus. In the interactive simulation mode, re-simuluations occur automatically whenever the user changes the input stimulus, making it easy to test a small change in the timing of an input signal. If the user wants to archive off a testbench and associated simulation results, all he has to do is save the timing diagram file and reload it at a later date. The completed test bench and wrapper code can be viewed in the report window.
BugHunter’s automatic test bench generation features are perfectly suited for testing small models. But as a design grows in complexity, more complex test benches are also needed to ensure the functionality of the overall design. TestBencher Pro was designed to meet this need. And TestBencher enables the rapid creation of bus-functional models for transaction-level testing of your complete system.
TestBencher Pro – Advanced Bus-Functional Models
The TestBencher Pro generates VHDL and Verilog test benches directly from timing diagrams using a bus functional approach to test bench designs. It is used to model complex test benches like a microprocessor or bus interface. With TestBencher, users can generate a test bench in a few hours that would normally take several weeks to test and code by hand.
Bus-functional models execute faster than complete functional models and can be created from data contained in data sheets. A bus-functional model is also easier to maintain and debug than raw test vector data. The code that is generated for each project is native VHDL or Verilog. This allows the generated code to compiled with the model under test and simulated using all major VHDL and Verilog simulators. Debugging the resulting system is easy since the test bench is structured into transactions and all of the generated code uses the same language as the code being tested.
Graphical Representation of Transactions
TestBencher Pro uses timing diagrams to represent the timing transactions of the test bench. By using timing diagrams, the engineer can work with a higher level abstraction, free from the tedious details of the underlying code. This graphical representation facilitates the collaboration of many engineers on a single test bench by removing the need to interpret source code. Any engineer familiar with the design specifications is able to look at a given timing diagram and have an immediate understanding of what the transaction is doing. This level of abstraction also provides a great aid in terms of maintainability.
In the example below, we have hand-coded a very simple timing transactor (a model that generates or responds to transactions) to show how difficult it is to understand even a small segment of code. Also shown is the timing diagram that can be used to generate this transactor. A glance at the timing diagram communicates the temporal relationships between the edges of the signals. The code segment has to be studied and possibly drawn out by hand to figure out the temporal relationships of the signals.
module testbench;
…
task write(addr,data,csb2dbus);
input [7:0] addr; input [15:0] data; input [1:0] csb2dbus;
begin
ABUS = addr;
@(posedge CLK0) //required abus2csb setup CSB = 1’b0;
repeat (csb2dbus) @CLK0; DBUS = data;
@(posedge CLK0) CSB = 1’b1; DBUS = ‘hz; ABUS = ‘hz;
end endtask
…
Endmodule
Code complexity greatly increases when response checking code and parallel execution blocks are added to a transactor. In the previous example, only one process block is needed to represent the transaction. However, if you wanted the transaction to sample the first edge transition of CSB then do a conditional delay of the csb2dus, the transactor has to be coded like a finite state-machine. This type of coding is very difficult to read, however the timing diagram is still easy to interpret.
Automatic Tracking of Signal and Port Code One of the most tedious aspects of working with HDL languages is maintaining the signal and port information between the test bench and the model under test. Signal information is repeated at several levels of the test bench, so a change in the signal information requires a tedious rewriting of the test bench code. Test bench code is more difficult to maintain than a regular design model because the code is not broken apart into simple units. Each timing transactor usually drives and monitors most of the input/output port signals of the model under test. TestBencher solves this problem by maintaining the signal and port information for all the timing transactions and the model under test.
With TestBencher Pro, a signal change is made in one place and then automatically propagated to all the places where that code needs to be represented. Without this capability, port-connection errors can easily arise leading to subtle, difficult to debug errors, similar to the problems that arise when pins are misconnected on a circuit board. Conceptual Modeling Constructs TestBencher is easy to use because we have taken great care to keep the number of constructs down to a minimum. There are 5 basic constructs that are used to create a transaction. It is easier to learn the functionality of these 5 graphical constructs than it is to figure out how to out how to code manual equivalents into a transactor model.
- · Drawn Waveforms – describes stimulus and expected response
- · State Variables – parameterize state values
- · Delays – parameterize time delays between edge transitions
- · Samples – verify and react to output from model under test
- · Markers – models looping contructs or to insert native HDL subroutine calls
These graphical constructs look and act the way that you expect a timing diagram to work, making it very easy to create a timing diagram that generates code that you expect to be generated. Normally an engineer must manually perform this conversion from data sheet timing diagrams to HDL code.
Easier to Maintain Test Benches TestBencher Pro’s test benches are easier to maintain than hand coded test benches for several reasons:
1. Graphical representation of transactions facilitates the ability of a non-author to understand the basic operation of the test bench.
2. A project window contains all of the related test bench and model under test files so that an engineer can quickly move through the test bench and MUT code.
3. Limited number of files generated (1+N transactions). One file is generated for the top-level test bench, and one file is generated for each timing transaction.
4. Fast generation of code – each time a transaction is saved, the code for that transaction is re-generated so that you can immediately assess the effects of changes in the timing diagram.
5. Generation of optimized test bench code for fast test bench execution.
6. All generated code is well documented – both in comments and in naming constructs, making the generated code easier to understand.
7. The use of generated code guarantees consistent code architecture. This provides readability from one transactor to the next, and from one project to the next.
8. GUI environment isolates key parameters of the test bench for easy modification.
9. Generated code automates the checking of simulation results, freeing the engineer from needing to manually view waveform results to ensure proper operation of his design.
TestBencher Pro abstracts coding details away from the user, and by doing so reduces the amount of time needed for test bench generation. By automating the most tedious aspects of test bench development, high paid engineers can focus on the design and operation of the test bench rather than the painstaking aspects of code development.
Reactive Test Bench Option
The Reactive Test Bench Option is a sub-set of the TestBencher Pro product. It enables the creation of self-testing testbenches using a single timing diagram, rather than the multi-diagram bus-functional models created by TestBencher Pro. The Reactive test benches can respond to the model under test during simulation and also generate reports that describe the performance of the simulation. The Reactive Test Bench Generation Option can added to WaveFormer Pro, WaveFormer Lite, DataSheet Pro, and BugHunter Pro.
With Reactive Test Bench Option, the user draws both the stimulus waveforms (black) and the expected output of the model under test (blue waveforms). Samples are add to the blue expected waveforms to generate specific tests at those points in the diagram
Below is a picture of the generated code for the sample that is used to check the output of the read cycle.