Is there a way to emulate the data encapsulation features of a C++ class in VHDL-2008 using just VHDL functions and VHDL records? I've seen this type of thing done many times in languages such as "c", but very rarely for VHDL.
Specifically, I want to make all the class data public to every method of the class just to keep it simple.
Example C++:
class item_t {
public:
uint32_t addr;
uint32_t data;
bool valid;
};
class keeper_t {
public:
item_t data[100];
void put(uint32_t addr, uint32_t data);
uint32_t get(uint32_t addr);
};
My thoughts on how to emulate the above code in VHDL would be to create a record to hold the class data and to pass it into each function every time to hold the state of the class. The problem that I have with this technique in VHDL, is how to modify the "class" data structure passed into the function? VHDL is really very limited when it comes to returning a structure to the calling process. Example:
library ieee;
use ieee.std_logic_1164.all;
--------------------------------------------------
package ADT is
constant A_OK :integer := 0;
constant max_keepers :natural := 100;
type keeper_item_t is record
addr :std_logic_vector(31 downto 0);
data :std_logic_vector(31 downto 0);
valid :std_logic;
end record;
type keeper_t is array (0 to max_keepers) of keeper_item_t;
function keeper_new return keeper_t;
function keeper_add(signal k :inout keeper_t) return integer;
function keeper_get(signal k :inout keeper_t; signal kitem: out keeper_item_t) return integer;
end package ADT;
--------------------------------------------------
package body ADT is
function keeper_new return keeper_t is
variable rkeeper :keeper_t;
begin
rkeeper(0).addr <= X"AABBCCDD";
rkeeper(0).data <= X"10101010";
rkeeper(0).valid <= '0';
return rkeeper;
end function;
function keeper_add(signal k :inout keeper_t) return integer is
begin
k(0).addr <= X"12345678";
k(0).data <= X"BEEF1234";
k(0).valid <= '1';
return A_OK;
end function;
function keeper_get(signal k :inout keeper_t; signal kitem: out keeper_item_t) return integer is
variable kitem: keeper_item_t;
begin
kitem := k(0);
return A_OK;
end function;
end package body;
--------------------------------------------------
entity testbench is
end entity;
--------------------------------------------------
architecture sim of testbench is
begin
process
variable kp :keeper_t;
variable ki :keeper_item_t;
variable ok :integer;
begin
kp := keeper_new;
ok := keeper_put(kp);
ki := keeper_get(kp);
wait;
end process;
end architecture;
The above VHDL code doesn't compile and generated the following syntax error:
--line:20: function keeper_get(signal k :inout keeper_t; signal kitem: out
keeper.vhd:20:30: mode of a function parameter cannot be inout or out
keeper.vhd:21:30: mode of a function parameter cannot be inout or out
keeper.vhd:21:56: mode of a function parameter cannot be inout or out
To get around this problem, I'm thinking I need to pass in a VHDL "access pointer" instead of the keeper_t structure itself. if I do this then I am passing a readonly pointer into the function that in turn points to the actual data structure that can be modified?
type Keeper_Ptr_t is access keeper_t;
--Dereferencing a VHDL Access Type: The suffix .all is
-- used to de-reference a pointer, i.e. it gives
-- you the thing pointed to by the pointer.
So I try this method as well of using an access type:
library ieee;
use ieee.std_logic_1164.all;
--------------------------------------------------
package ADT is
constant A_OK :integer := 0;
constant max_keepers :natural := 100;
type keeper_item_t is record
addr :std_logic_vector(31 downto 0);
data :std_logic_vector(31 downto 0);
valid :std_logic;
end record;
type keeper_t is array (0 to max_keepers) of keeper_item_t;
type keeper_ptr_t is access keeper_t;
function keeper_new return keeper_t;
function keeper_add(kp: keeper_ptr_t) return integer;
function keeper_get(kp: keeper_ptr_t) return keeper_item_t;
end package ADT;
--------------------------------------------------
package body ADT is
function keeper_new return keeper_t is
variable rkeeper :keeper_t;
begin
rkeeper(0).addr <= X"AABBCCDD";
rkeeper(0).data <= X"10101010";
rkeeper(0).valid <= '0';
return rkeeper;
end function;
function keeper_add(kp: keeper_ptr_t) return integer is
begin
kp.all(0).addr <= X"12345678";
kp.all(0).data <= X"BEEF1234";
kp.all(0).valid <= '1';
return A_OK;
end function;
function keeper_get(kp: keeper_ptr_t) return keeper_item_t is
begin
return kp.all(0);
end function;
end package body;
--------------------------------------------------
entity testbench is
end entity;
--------------------------------------------------
architecture sim of testbench is
begin
process
variable kp :keeper_t;
variable ki :keeper_item_t;
variable ok :integer;
begin
kp := keeper_new;
ok := keeper_put(kp);
ki := keeper_get(kp);
wait;
end process;
end architecture;
This version fails with the error:
keeper.vhd:22:23: type of constant interface "kp" cannot be access type "keeper_ptr_t"
keeper.vhd:23:23: type of constant interface "kp" cannot be access type "keeper_ptr_t"