1
votes

I want to organize a working bus functional model and push commonly used procedures (which look like CPU subroutines) out into a package and get them out of the main cpu model, but I'm stuck.

The procedures don't have access to the hardware bits when they're pushed out in a package. In Verilog, I would put commonly used procedures out into an include file and link them into the CPU model as required for a given test suite.

More details:

I have a working bus functional model of a CPU, for simulation test benching. At the "user interface" level I have a process called "main" running inside the CPU model which calls my predefined "instruction set" like this:

cpu_read(address, read_result);

cpu_write(address, write_data);

etc.

I bundle groups of those calls up into higher level procedures like

configure_communication_bus;

clear_all_packet_counters; 

etc.

At the next layer these generic functions call a more hardware specific version which knows the interface timing for the design, and those procedures then use an input record and output record to connect to the hardware module ports and waggle the cpu bus signals as required.

cpu_read calls hardware_cpu_read(cpu_input_record, cpu_output_record, address);

Something like this:

procedure cpu_read (address : in std_logic_vector(15 downto 0);
                    read_result : out std_logic_vector(31 downto 0));
    begin
        hardware_cpu_read(cpu_input_record, cpu_output_record, address, read_result);
    end procedure;

The cpu_input_record and cpu_output_record are declared as signals of type nnn_record in the cpu model vhdl file.

So this is all working, but every single one of these procedures is all stored in the cpu VHDL module file, and all in the procedure declaration section so that they are all in the same scope.

If I share the model with team members they will need to add their own testing subroutines, and those also are all in the same location in the file, as well, their simulation test code has to go into the "main" process along with mine.

I'd rather link in various tests from outside the model, and only keep model specific procedures in the model file..

Ironically I can push the lowest level hardware procedure out to a package, and call those procedures from within the "main" process, but the higher level processes can't be put out into that package or any other packages because they don't have access to the cpu_read_record and cpu_write_record.

I feel like there must be a simple way to clean up this code and make it modular, and I'm just missing something obvious.

I don't really think making a command interpreter and loading my test code into a behavioral ROM is the right way to go by the way. Nor is fighting with the simulator interface to connect up a C program, but I may break down and try this..

3

3 Answers

4
votes

Quick sketch of an answer (to the question I think you are asking! :-) though I may be off-beam...

To move the BFM subprograms into a reusable package, they need to be independent of the execution scope - that usually means a long parameter list for each of them. So using them in a testbench quickly gets tedious compared with the parameterless (or parameter-lite) versions you have now..

The usual workaround is to implement the BFM in a package, with long parameter lists.

Then write parameter-lite local equivalents (wrappers) in the execution scope, which simply call the package versions supplying all the parameters explicitly.

This is just boilerplate - not pretty but it does allow you to move the BFM into a package. These wrappers can be local to the testbench, to a process within it, or even to a subprogram within that process.

(The parameter types can be records for tidiness : these are probably declared in a third package, shared between BFM. TB, and synthesisable device under test...)

Thanks to overloading, there is no ambiguity between the local and BFM package versions, so the actual testbench remains as simple as possible.

Example wrapper function :

function cpu_read(address : unsigned) return slv_32 is
begin
   return BFM_pack.cpu_read ( 
      address     => address,
      rd_data_bus => tb_rd_data_bus,
      wait        => tb_wait_signal,
      oe          => tb_mem_oe,
      -- ditto for all the signals constants variables it needs from the tb_ scope
      );
end cpu_read;
1
votes

Currently your test procedures require two extra signals on them, cpu_input_record and cpu_output_record. This is not so bad. It is not uncommon to just have these on all procedures that interact with the cpu and be done with it. So use hardware_cpu_read and not cpu_read. Add cpu_input_record, cpu_output_record to your configure_communication_bus and clear_all_packet_counters procedures and be done. Perhaps choose shorter names.

I do a similar approach, except I use only one record with resolved elements. To make this work, you need to initialize the record so that all elements are non-driving (ie: 'Z' for std_logic). To make this more flexible, I have created resolution functions for integer, time, and real. However, this only saves you one signal. Not a real huge win. Perhaps half way to where you think you want to be. But it is more work than what you are doing.

For VHDL-201X, we are working on syntax to allow parameters/ports automatically map to a identically named signal. This will get you to where you want to be with any of the approaches (yours, mine, or Brian's without the extra wrapper subprogram). It is posted here: http://www.eda.org/twiki/bin/view.cgi/P1076/ImplicitConnections. Given this, I would add the two records to your procedures and call it good enough for now.

Once you get by this problem, you seem to also be asking is how do I write separate tests using the same testbench. For this I use multiple architectures - I like to think of these as a Factory Class for concurrent code. To make this feasible, I separate the stimulus generation code from the rest of the testbench (typically: netlist connections and clock). My presentation, "VHDL Testbench Techniques that Leapfrog SystemVerilog", has an overview of this architecture along with a number of other goodies. It is available at: http://www.synthworks.com/papers/index.htm

0
votes

You're definitely on the right track, in fact I have a variant like this (what you describe). The catch is, now I build up a whole subroutine using the "parameter light" procedures, and those are what I want to put in a package to share and reuse. The problem is that any procedure pushed out to a package can't call to the parameter light procedures in the main vhdl file.. So what happens is we have one main vhdl file with all the common CPU hardware setup routines, and every designer's test code all in the same vhdl file.. Long story short, putting our test subroutines into separate files is really what I was hoping for..