I got frustrated writing processor accessed registers once again in a very traditional way, and decided to make it more convenient. This is the result, so far:
In VHDL, array indexes are typically integers with ranges, e.g. std_logic_vector(15 to 27), but in fact every constrained type can be used as an index:
type mytype is (something, used, as_index, values);
type myarray is array (mytype) of integer range 5 to 9;
This defines an array type, myarray, which has 4 entries, each being integer from 5 to 9.
You will then access the arrray by utilizing mytype type of variable/signal as index:
signal storage: myarray;
... variable i: mytype;
... myarray(i) <= myarray(i) + 1;
The key here is that the index type has to be constrained, otherwise the size of array is infinite which can not be supported.
And don't be too tempted to write something like this:
type myarray2 is array (std_logic_vector(7 downto 0)) of std_logic_vector(7 downto 0);
It works, but not as you would think in the first place.
std_logic is defined to have 9 different values (U,X,0,1,Z,W,L,H,-) and every combination of 8 of them will do, so instead of 2^8 entries, it will yield to 9^8 entries. The size of that array will be over 43 million bytes.
Anyway, this array indexing scheme proved to be quite powerful for register implementation. Especially if one needs to modify the design often, or if there are a lots of registers. Any comments appreciated...
BR, -Topi
registers.vhdl:
***********************
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.registers_pkg.all;
entity registers is
port(
reset_in: in std_logic;
clk_in: in std_logic;
address_in: in reg_add_type;
writedata_in: in reg_val_type;
readdata_out: out reg_val_type;
write_en_in: in std_logic;
read_en_in: in std_logic;
regs_out: out reg_array
);
end;
architecture rtl of registers is
signal regs: reg_array;
begin
regs_out <= regs;
-- register writes / modifications:
process(reset_in, clk_in)
variable i: reg_add_enum_type;
variable not_found: boolean;
variable r: reg_val_type;
variable mask: reg_val_type;
begin
if reset_in = '1' then
regs <= regs_reset_values;
elsif rising_edge(clk_in) then
if write_en_in = '1' then
reg_add_to_index(address_in, i, not_found);
if not not_found then
mask := reg_write_masks(i);
r := regs(i) and (not mask);
r := r or (writedata_in and mask);
regs(i) <= r;
end if;
end if;
end if;
end process;
-- register read:
process(reset_in, clk_in)
variable i: reg_add_enum_type;
variable not_found: boolean;
variable r: reg_val_type;
variable mask: reg_val_type;
variable m: integer;
begin
if reset_in = '1' then
readdata_out <= (others => '0');
elsif rising_edge(clk_in) then
if read_en_in = '1' then
reg_add_to_index(address_in, i, not_found);
if not not_found then
mask := reg_read_masks(i);
r := regs(i);
for m in mask'range loop
if mask(m) = '0' then
r(m) := '-';
end if;
end loop;
readdata_out <= r;
else
readdata_out <= (others => '-');
end if;
end if;
end if;
end process;
end;
***********************
registers_pkg.vhdl:
***********************
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package registers_pkg is
-- Register definitions here =>
-- Register value type is here.
subtype reg_val_type is std_logic_vector(7 downto 0);
-- List all registers here:
type reg_add_enum_type is (
reg_0,
reg_1,
reg_2,
something,
something_else,
yet_another
);
type reg_add_array is array (reg_add_enum_type) of integer;
-- List register address mapping of every register here:
constant reg_add_map: reg_add_array := (
reg_0 => 16#10#,
reg_1 => 16#11#,
reg_2 => 16#12#,
something => 16#20#,
something_else => 16#21#,
yet_another => 16#22#
);
type reg_mask_array is array (reg_add_enum_type) of reg_val_type;
-- List write_mask of every register here ('0' is Read Only):
constant reg_write_masks: reg_mask_array := (
reg_2 => (2 downto 0 => '1', others => '0'),
something_else => (3 downto 0 => '1', others => '0'),
others => (others => '1')
);
-- List read_mask of every register here ('0' is N.A.):
constant reg_read_masks: reg_mask_array := (
reg_0 => (0 => '0', others => '1'),
others => (others => '1')
);
type reg_array is array (reg_add_enum_type) of reg_val_type;
-- List reset_value of every register here:
constant regs_reset_values: reg_array := (
others => (others => '0')
);
-- <= Register definitions here
function reg_array_min(adds: reg_add_array) return integer;
function reg_array_max(adds: reg_add_array) return integer;
type regs_type is array (reg_add_enum_type) of reg_val_type;
type reg_mapping_type is array (reg_add_enum_type) of integer;
subtype reg_add_type is integer range reg_array_min(reg_add_map) to reg_array_max(reg_add_map);
procedure reg_add_to_index(add: in reg_add_type; index: out reg_add_enum_type; error: out boolean);
end;
package body registers_pkg is
procedure reg_add_to_index(add: in reg_add_type; index: out reg_add_enum_type; error: out boolean) is
variable i: reg_add_enum_type;
begin
for i in reg_add_map'range loop
if reg_add_map(i) = add then
error := false;
index := i;
return;
end if;
end loop;
assert false report "register address not found!" severity warning;
index := reg_add_map'right;
error := true;
end;
function reg_array_min(adds: reg_add_array) return integer is
variable f: boolean;
variable r: integer;
variable i: reg_add_enum_type;
begin
f := false;
for i in adds'range loop
if f then
if adds(i) < r then
r := adds(i);
end if;
else
f := true;
r := adds(i);
end if;
end loop;
assert f report "reg_array_min: adds empty!" severity failure;
return r;
end;
function reg_array_max(adds: reg_add_array) return integer is
variable f: boolean;
variable r: integer;
variable i: reg_add_enum_type;
begin
f := false;
for i in adds'range loop
if f then
if adds(i) > r then
r := adds(i);
end if;
else
f := true;
r := adds(i);
end if;
end loop;
assert f report "reg_array_max: adds empty!" severity failure;
return r;
end;
end;
***********************
I synthesized this on Altera Quartus, Lattice Diamond, and Xilinx WebPack.
ReplyDeleteResult was:
Altera: 56 registers
Lattice: 47 registers
Xilinx: 47 registers
It seems that Quartus, for some reason, does not recognize the constant registers set by reg_write_masks. The correct answer is 47 registers ;)
-Topi