mtv produces a Verilog testbench (this file is called test.v by default). This Verilog testbench can instantiate and test a VHDL DUT (an entity or a configuration), but only if you have a dual-language simulator.
There is no standardised way for Verilog code to instantiate VHDL code, or vice-versa. However, there does appear to be a common set of requirements among different simulators. The specific requirements for ModelSim, for example, are as follows:
- the VHDL design unit is an entity/architecture pair or a configuration declaration
- the entity ports are of type bit, bit_vector, std_ulogic, std_ulogic_vector, vl_ulogic, vl_ulogic_vector, or their subtypes. The port clause may have any mix of these types
- The generics are of type integer, real, time, physical, enumeration, or string. String is the only composite type allowed.
If the VHDL DUT conforms to the requirements for your own simulator, then the DUT can be tested if your simulators.conf file contains an entry for your dual-language simulator. If your simulator is not listed, you can construct a new entry simply by listing the required steps to carry out a dual-language batch-mode simulation. The simulators.conf entry for dual-language ModelSim is reproduced here; this should be modified as required for other simulators:
# ----------------------------------------------------------------------------- # Run a mixed-language ModelSim simulation with the Maia-generated Verilog # testbench, and additional VHDL sources, as follows. See the 'modelsim' # section for additional options. # # $ export RTV_SIMULATOR=modelsim_mixed # $ rtv mytest.tv a.vhd b.vhd c.vhd ...etc # ----------------------------------------------------------------------------- modelsim_mixed verilog { # set additional variables $set workdir = work $set topmod = $workdir.$root # run simulation rm -rf $workdir vlib $workdir echo "(rtv: log) compiling..." $if($sim_args) vcom -work $workdir $sim_args vlog -work $workdir $vfile echo "run -all quit -f" > $workdir/runfile echo "(rtv: log) running vectors..." vsim -quiet $topmod < $workdir/runfile rm $vfile rm -rf $workdir rm -f transcript }
The configuration entry in this example is named modelsim_mixed. The name is arbitrary, but should not be the same as the name of any other entry in the configuration file. The 'verilog' refers to the code generator, and should be left unchanged. The commands in this entry are suitable for Unix-like operating systems. If your shell is actually Windows/cmd.exe, then you will need to use the DOS equivalents (see the MXE_DOS entry in simulators.conf for an example). The modelsim_mixed entry carries out these steps:
- it removes any existing work directory
- it creates a new work directory using vlib
- if there any any VHDL files on the rtv command line ($if($sim_args)), it compiles the VHDL DUT into the work directory (which has been given the name "work"), passing any rtv arguments after the VHDL filename directly to vcom
- it compiles the Verilog testbench produced by Maia, also into the work directory
- it creates a batch run script in the work directory
- it carries out a batch-mode simulation using vsim, taking the commands from the batch run script
- it removes the Maia output file
- it removes the work directory and the transcript. Any transcript output was stored in the Maia logfile.
Assuming that this entry is named modelsim_mixed, the testbench can now be run as:
> rtv --sim modelsim_mixed my_testbench.tv my_DUT.vhd [more VHDL files]
Or, if the RTV_SIMULATOR environment variable has the value modelsim_mixed, simply as
> rtv my_testbench.tv my_DUT.vhd [more VHDL files]
When rtv is run without switches, it expects the testbench file to have a .tv extension. Everything after the tv file is passed directly to vcom for compilation (this is the $sim_args argument); this might include multiple VHDL sources and vcom switches.
The final step is to ensure that the module declaration in your Maia DUT section correctly instantiates your VHDL entity or configuration. If we assume that the VHDL entity declaration is:
entity MUX8TO1 is port ( SEL : in unsigned(2 downto 0); I : in std_logic_vector(7 downto 0); O : out std_logic); end entity MUX8TO1;
then the module declaration is instead simply:
DUT { module MUX8TO1(input [2:0] SEL, input [7:0] I, output O); ... }
The HDL coding styles article contains a number of examples of VHDL DUTs which are tested in this way.
Instantiating a configuration declaration
The DUT section can alternatively instantiate a configuration declaration, rather than an entity. This complete example is for the MUX8TO1 entity from the 'HDL coding styles' article, which now has two alternative architectures. The VHDL source includes a configuration declaration which selects one of these architectures. The Maia testbench, shown below the VHDL code, instantiates the configuration and, in this case, tests architecture A.
library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; -- unsigned and to_integer entity MUX8TO1 is port ( SEL : in unsigned(2 downto 0); I : in std_logic_vector(7 downto 0); O : out std_logic); end entity MUX8TO1; architecture A of MUX8TO1 is begin -- architecture A uses a concurrent (selected) signal assignment with to_integer(SEL) select O <= I(0) when 0, I(1) when 1, I(2) when 2, I(3) when 3, I(4) when 4, I(5) when 5, I(6) when 6, I(7) when others; end architecture A; architecture B of MUX8TO1 is begin -- architecture B uses a process with a case statement process(SEL, I) is begin case SEL is when "000" => O <= I(0); when "001" => O <= I(1); when "010" => O <= I(2); when "011" => O <= I(3); when "100" => O <= I(4); when "101" => O <= I(5); when "110" => O <= I(6); when others => O <= I(7); end case; end process; end architecture B; ======================================================================--------- -- The rest of the code is only required if you want to explicitly select the -- required multiplexer architecture (A or B). In normal circumstances, the -- last analysed architecture (in this case, B) is used. ======================================================================--------- library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity VHDL_TOP is port ( SEL : in unsigned(2 downto 0); I : in std_logic_vector(7 downto 0); O : out std_logic); end entity VHDL_TOP; architecture ONLY of VHDL_TOP is component MUX8TO1 is port ( SEL : in unsigned(2 downto 0); I : in std_logic_vector(7 downto 0); O : out std_logic); end component MUX8TO1; begin U1 : MUX8TO1 port map (SEL, I, O); end architecture ONLY; configuration VHDL_CONFIG of VHDL_TOP is for ONLY for U1 : MUX8TO1 use entity work.MUX8TO1(A); end for; end for; end configuration VHDL_CONFIG;
The testbench is:
DUT { module VHDL_CONFIG (input [2:0] SEL, input [7:0] I, output O); [SEL, I] -> [O]; } void main() { bit3 sel; bit8 data; for all data // cycles data from 0 to 255 for all sel { // cycles sel from 0 to 7 bit expected = data.(sel); // or data >> sel [sel, data] -> [expected]; } }
If the VHDL code is saved as mux8to1_a6.vhd , and the testbench as mux8to1_a6.tv , then the test can be run as:
$ export RTV_SIMULATOR=modelsim_mixed $ rtv mux8to1_a6.tv mux8to1_a6.vhd
This should report 2048 passes, and no failures.
Current limitations
There are currently some limitations with VHDL support:
- when instantiating an entity in the DUT section, it is not possible to directly specify which architecture to test. In principle, the MUX8TO1 entity could be specified as MUX8TO1(arch-name) but this is not currently supported. The entity name must be specified without an architecture, and the last analysed architecture is tested. If this is not suitable, a configuration should be used.
- internal signals within the VHDL hierarchy cannot currently be tested or forced.