VUnit / vunit

VUnit is a unit testing framework for VHDL/SystemVerilog
http://vunit.github.io/
Other
739 stars 263 forks source link

Reading .mif files #236

Closed drizzd closed 7 years ago

drizzd commented 7 years ago

IP generators such as Xilinx Coregen produce VHDL wrapper files for simulating the core. Memory initialization files (.mif) which contain the initialization vectors are generated for certain cores, for example ROMs and RAMs. The VHDL wrapper file hardcodes the path to the .mif file. Here is an example ROM wrapper:

  FOR ALL : wrapped_my_rom USE ENTITY XilinxCoreLib.blk_mem_gen_v7_3(behavioral)
    GENERIC MAP (
      c_init_file_name => "my_rom.mif",
[...]

Simulating this in Modelsim with VUnit will produce the following error:

# ** Error: (vsim-7) Failed to open VHDL file "my_rom.mif" in r mode.
# No such file or directory. (errno = ENOENT)
#    Time: 0 ps  Iteration: 0  Instance: /tb_my_testbench/i_my_rom/U0/native_mem_module/mem_module
# ** Fatal: (vsim-7) Failed to open VHDL file "my_rom.mif" in r mode.
# No such file or directory. (errno = ENOENT)
#    Time: 0 ps  Iteration: 0  Process: /tb_my_testbench/i_my_rom/U0/native_mem_module/mem_module/line__3384 File: /sw/xilinx/v14.4i/14.4/ISE_DS/ISE/vhdl/src/XilinxCoreLib/BLK_MEM_GEN_V7_3.vhd
# FATAL ERROR while loading design
# Error loading design
Error loading design

Of course it cannot find it, because VUnit will start vsim in the vunit_out/modelsim directory. My first workaround will be to copy the .mif file to vunit_out/modelsim before running ui.main(). But it would be nice if I could use the VUnit API to determine the simulation directory, preferably in a callback which is invoked after VUnit has created it.

As far as I can tell, cores generated with Vivado have the same issue.

drizzd commented 7 years ago

Ok, so for example this works for me:

from shutil import copy
copy(mifPath, ui._simulator_factory.simulator_output_path)
kraigher commented 7 years ago

Is it possible to use an environment variable in the mif file path such as PROJECT_ROOT? An environment variable can be set in the run.py script.

Your suggested solution has theoretical problems with parallel simulation and mif files with common base name.

Unrelated to VUnit and maybe unhelpful but another solution is to never use mif files. I always infer block ram and ROMs. Inferring them leads to significantly faster simulation and enables generic/parameters to influence the memory.

joshrsmith commented 7 years ago

@kraigher In my experience the path to the mif file is hard coded in the simulation wrapper vhdl file. Unless you want to modify this file (which I avoid at all costs), then there is no way to specify a different name/path for the mif.

What is the problem with parallel simulation? Multiple simulators are not allowed to read from the same *mif at once? I do agree that there is a potential problem if you have mif files from different IP that end up getting named the same thing and then thrown into the same folder.

I agree that avoiding these types of files when possible is good practice, but sometimes it is just not worth it. For example, a FIR filter might use a mif file to simulate the configured coefficients.

joshrsmith commented 7 years ago

@drizzd So is this issue resolved then?

kraigher commented 7 years ago

The parallel simulation problem I was referring to was when different IP have a mif file named the same but with different content. Each simulation thread needs to have their own working directory to mitigate this.

drizzd commented 7 years ago

@joshrsmith The member _simulator_factory starts with an underscore, which usually indicates that it is not a public interface. It is also not documented in the VUnit public API. If you say that you will support this also in the future, then this issue is resolved.

Note that currently, the simulator is detected and the simulation directory is created when VUnit is instantiated. Maybe in the future VUnit will delay this until the simulation actually runs. In this case, we would need a callback like pre_config to communicate the simulation directory to the user.

@kraigher My plan is to require that .mif file names must be unique within a project. But of course it is dangerous if a conflict ever goes unnoticed. Don't we already have a similar problem with external libraries or anything else which gets added to modelsim.ini? Maybe a unique simulation directory is indeed the right solution.

Alternatively, I could take the VHDL wrapper and blindly replace the string "my_rom.mif" with "$PROJECT_ROOT/path/to/my_rom.mif". But I am not sure if this will always work.

I would also prefer to use inference and I have not given up on that front. But I get a lot of resistance against its use. I am told that inference has led to problems in the past. People seem to fear a loss of control over the synthesis result. In any event, at least a few .mif files are likely to stay.

I will also try to submit a support request to Xilinx. But when I tried yesterday, the Xilinx website ate my request.

kraigher commented 7 years ago

I is a true that the method suggested by @drizzd is not public and may change or disappear. If we make this public my initial thought is it will be through pre_config callback.

An alternative method is to "preprocess" the VHDL file to create a new VHDL file using a regex in the Python script which is then added to VUnit without modifying the original file.

BTW regarding parallel simulation and modelsim.ini it is no problem since it is never modified after the initial non-parallel part where all files are compiled.

drizzd commented 7 years ago

I just recalled that the VHDL wrapper generated by Vivado (as opposed to Coregen) does not have the .mif file in the source code. It is a only a stub with an empty architecture and several attributes. I don't know how it determines where to read the .mif file, but preprocessing may not help here. I will try to find out more on Monday.

drizzd commented 7 years ago

Just for clarification: the only way to get a .mif file conflict is by selecting different IP with same .mif basename but different content. There can be only one toplevel Testbench per VUnit instance (i.e. per run.py). Is this correct?

If so we could mitigate the danger of conflict by providing an interfaces which takes a list of files to copy to the simulation directory. It will do so only once for all configurations and it will error out if the basenames are not unique.

kraigher commented 7 years ago

It is possible to have many testbenches in a run.py. At work I have the entire project with ~100 testbenches accessible through the run.py.

drizzd commented 7 years ago

Ah, I see. My bad.

Nevertheless if we make the "copy files to simulation directory" interface global rather than per-testbench or per-configuration, then we can check if the files are unique.

drizzd commented 7 years ago

I discarded my "copy files to simulation directory" interface idea because this solution is too specific for VUnit. The user can do such checks on their own and hopefully the burden will convince them to use inference in the future.

I forgot to document the simulation_output_path parameter in add_config's docstring. I will fix this if you want to go with this change.

drizzd commented 7 years ago

Any thoughts on the pull request? It is a minimal implementation according to kraigher's comment "If we make this public my initial thought is it will be through pre_config callback."

I hint at the potential issues in the API documentation:

The function accepts an optional first argument `output_path` which is the filesystem path to the
       directory where test outputs are stored. An optional second argument
       `simulator_output_path` is the filesystem path to the simulator working directory.
       Please note that `simulator_output_path` is shared by all test runs. The user must take
       care that test runs do not read or write the same files asynchronously. It is therefore
       recommended to use `output_path` in favor of `simulator_output_path`.
curiousengineer commented 3 years ago

The problem can be completely removed if all paths to mif files are absolute paths. However, this is not possible for the automatically generated IP. The issue of memory files not being found by simulators due to the simulator path being at a different level in hierarchy than the synthesis tool is an old one. A proper solution would require some cooperation from the simulator and synthesis tool vendors!

As far as VUnit is concerned, the ideal solution would be that we can specify the memory files along with their location relative to the simulator path and that these files are copied before the simulator is fired up or atleast before the source files are compiled. The issue of different memory files having the same name is really something that the user should be aware of and deal with. VUnit can make detection of such conflicts easier of course.

The pre-simulation hook script is executed once all the files have been compiled. It is not the write place. It might be better to have a pre-elaboration hook as well.