M
M. Norton
Guest
Hello folks,
I'm posting this in hopes that folks have some suggestions on how to
handle this in a clean way. I find myself (not surprisingly) writing
testbenches for a lot of very similar bus interfaces. At the simplest
I've got an address signal, data out, data in, and a write enable
signal. On a more complex one I might have the full backend interface
to the Actel PCI core or the Altera Avalon interface.
Regardless, I would really like to be able to write my basic
interaction procedures once and just use them after that. However I'm
running into some difficulty with the language and scoping rules. So
for talking purposes, here's a little skeleton
use work.generic_bus_if_pkg.all; -- <procedure declaration area C :
preferred>
entity just_a_testbench is
end entity just_a_testbench;
architecture behavioral of just_a_testbench is
<procedure declaration area B>
signal be_addr : unsigned(15 downto 0);
signal be_rddata : std_logic_vector(31 downto 0);
signal be_wrdata : std_logic_vector(31 downto 0);
signal be_wren : std_logic;
begin
DUT : entity work.foo
port map (
....
be_addr => be_addr,
be_rddata => be_rddata,
be_wrdata => be_wrdata,
be_wren => be_wren,
....
);
BUS_CONTROL : process is
<procedure declaration region A>
begin
.....
be_read32();
be_write32();
.....
end process BUS_CONTROL;
end architecture behavioral;
Alright, I think that's enough to give an idea. So I'd really like my
procedure calls to be something I can abstract preferably into a
package. Also I'd really like to keep the parameter list as minimal
as possible. However I run into scope issues with signals. So if I
use region A for declaring my read/write procedures, I can directly
access all the signals running into the DUT. I don't have to pass
them in. I can simply have an address and data parameters. However
this means every testbench has to declare everything right there in
the process and that's ugly to me.
Now if I use B or C, then I no longer have scope on the signals. If I
want to use a procedure for reading and writing, I've got to pass them
into the procedure. With the example described, it's not really too
big a deal, but consider a PCI interface with all its myriad signals
or another example various PCI core backend interfaces with start,
stop, rdcyc, wrcyc, and various other signals for backpressure. It
seems to me that the parameter list could get unwieldy very quickly.
So I can put the procedures here, but that violates my desire to have
manageable calls.
So, I'm hoping there's some sort of clever way of passing a bus in and
out of procedure declarations to keep code readable and maintainable.
The only way I've thought that might work is to declare a record that
comprises the entire bus interface. Then I could pass in a single
token that represents the entire bus. I haven't really worked out how
that might work with different naming conventions (I'm usually pretty
consistent with signal names, but sometimes there are variations.)
There are very likely other ways to do it, but I haven't had a lot of
exposure to folks who do large scale testbenches and have already
worked out these tricks.
So, in summary does anyone have any technique or best practice for how
to organize bus interfaces for passing in and out of procedure calls?
Thanks for any ideas. I appreciate it.
Best regards,
Mark Norton
I'm posting this in hopes that folks have some suggestions on how to
handle this in a clean way. I find myself (not surprisingly) writing
testbenches for a lot of very similar bus interfaces. At the simplest
I've got an address signal, data out, data in, and a write enable
signal. On a more complex one I might have the full backend interface
to the Actel PCI core or the Altera Avalon interface.
Regardless, I would really like to be able to write my basic
interaction procedures once and just use them after that. However I'm
running into some difficulty with the language and scoping rules. So
for talking purposes, here's a little skeleton
use work.generic_bus_if_pkg.all; -- <procedure declaration area C :
preferred>
entity just_a_testbench is
end entity just_a_testbench;
architecture behavioral of just_a_testbench is
<procedure declaration area B>
signal be_addr : unsigned(15 downto 0);
signal be_rddata : std_logic_vector(31 downto 0);
signal be_wrdata : std_logic_vector(31 downto 0);
signal be_wren : std_logic;
begin
DUT : entity work.foo
port map (
....
be_addr => be_addr,
be_rddata => be_rddata,
be_wrdata => be_wrdata,
be_wren => be_wren,
....
);
BUS_CONTROL : process is
<procedure declaration region A>
begin
.....
be_read32();
be_write32();
.....
end process BUS_CONTROL;
end architecture behavioral;
Alright, I think that's enough to give an idea. So I'd really like my
procedure calls to be something I can abstract preferably into a
package. Also I'd really like to keep the parameter list as minimal
as possible. However I run into scope issues with signals. So if I
use region A for declaring my read/write procedures, I can directly
access all the signals running into the DUT. I don't have to pass
them in. I can simply have an address and data parameters. However
this means every testbench has to declare everything right there in
the process and that's ugly to me.
Now if I use B or C, then I no longer have scope on the signals. If I
want to use a procedure for reading and writing, I've got to pass them
into the procedure. With the example described, it's not really too
big a deal, but consider a PCI interface with all its myriad signals
or another example various PCI core backend interfaces with start,
stop, rdcyc, wrcyc, and various other signals for backpressure. It
seems to me that the parameter list could get unwieldy very quickly.
So I can put the procedures here, but that violates my desire to have
manageable calls.
So, I'm hoping there's some sort of clever way of passing a bus in and
out of procedure declarations to keep code readable and maintainable.
The only way I've thought that might work is to declare a record that
comprises the entire bus interface. Then I could pass in a single
token that represents the entire bus. I haven't really worked out how
that might work with different naming conventions (I'm usually pretty
consistent with signal names, but sometimes there are variations.)
There are very likely other ways to do it, but I haven't had a lot of
exposure to folks who do large scale testbenches and have already
worked out these tricks.
So, in summary does anyone have any technique or best practice for how
to organize bus interfaces for passing in and out of procedure calls?
Thanks for any ideas. I appreciate it.
Best regards,
Mark Norton