我多次创建了可以处理不同类型数据的 VHDL 块。 一个例子是堆流数据排序器 - https://opencores.org/projects/heap_sorter,另一个例子是用于高速数据采集的数据集中器 - https://arxiv.org/abs/2309.13690。
为了能够在同一设计中重用这些块来处理不同类型的数据,我通常将数据类型定义放在单独的包中,然后为特定数据类型创建单独的库,每个库都有自己的该包版本。
我的集中器的该方法的演示可作为 GHDL 模拟在 https://gitlab.com/WZabISE/xconcentrator/-/tree/8d094449b79a9318708dd34e59b7455bf097dcb5/sim_complex_type 中获得。
当然,这种方法在工作时远不方便。将类型传递给实体会更好,理论上这在 VHDL 2008 中应该是可能的。作为 MWE,我们可以使用多路复用器块。
文件 mux.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
entity mux is
generic (
type DATA_T;
NOF_INPUTS : integer);
port(
data_i : in array(NOF_INPUTS-1 downto 0) of DATA_T;
sel : in integer range 0 to NOF_INPUTS-1;
data_o : out DATA_T;
clk : in std_logic
);
end entity mux;
architecture rtl of mux is
begin -- architecture rtl
psel: process (clk) is
begin -- process psel
if clk'event and clk = '1' then
data_o <= data_i(sel);
end if;
end process psel;
end architecture rtl;
测试平台 mux_tb.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity mux_tb is
end entity mux_tb;
architecture sim of mux_tb is
-- First mux
constant NOF_INPUTS1 : integer := 4;
subtype DATA1_T is integer;
type DATA1_INS_T is array (NOF_INPUTS1-1 downto 0) of DATA1_T;
signal din1 : DATA1_INS_T := (10,-132,45,76);
signal dout1 : DATA1_T;
signal sel1 : integer range 0 to NOF_INPUTS1-1;
-- Second mux
constant NOF_INPUTS2 : integer := 3;
subtype DATA2_T is unsigned(4 downto 0);
type DATA2_INS_T is array (NOF_INPUTS2-1 downto 0) of DATA2_T;
signal din2 : DATA2_INS_T := (x"1",x"7",x"9");
signal dout2 : DATA2_T;
signal sel2 : integer range 0 to NOF_INPUTS2-1;
-- clock
signal clk : std_logic := '1';
begin -- architecture sim
-- component instantiation
DUT1: entity work.mux
generic map (
DATA_T => DATA1_T,
NOF_INPUTS => NOF_INPUTS1)
port map (
data_i => din1,
sel => sel1,
data_o => dout1,
clk => clk);
-- component instantiation
DUT2: entity work.mux
generic map (
DATA_T => DATA2_T,
NOF_INPUTS => NOF_INPUTS2)
port map (
data_i => din2,
sel => sel2,
data_o => dout2,
clk => clk);
-- clock generation
clk <= not clk after 10 ns;
-- waveform generation
WaveGen_Proc: process
begin
-- insert signal assignments here
wait until clk = '1';
sel1 <= 0;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 1;
sel2 <= 1;
wait until clk = '0';
wait until clk = '1';
sel1 <= 2;
sel2 <= 2;
wait until clk = '0';
wait until clk = '1';
sel1 <= 3;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 0;
sel2 <= 1;
wait;
end process WaveGen_Proc;
end architecture sim;
GHDL 的 makefile:
STD=synopsys
VSTD=08
ENTITY=mux_tb
RUN_OPTIONS= --stop-time=200ns --wave=${ENTITY}.ghw
SOURCES = \
mux.vhd \
mux_tb.vhd \
OBJECTS=$(SOURCES:.vhd=.o)
all: $(OBJECTS)
%.o : %.vhd
ghdl -a -g -C --std=${VSTD} --ieee=${STD} $<
all: ${ENTITY}.ghw test
show: ${ENTITY} ${ENTITY}.ghw
gtkwave ${ENTITY}.ghw ${ENTITY}.sav
${ENTITY}: $(SOURCES:.vhd=.o)
ghdl -e -g --mb-comments --std=${VSTD} -fexplicit --ieee=${STD} ${ENTITY}
${ENTITY}.ghw: ${ENTITY}
./${ENTITY} ${RUN_OPTIONS}
clean:
rm -f *.o *.vcd *.ghw *.cf ${ENTITY}
不幸的是,这种方法不起作用,因为我无法使用数组作为端口。 产生以下错误:
mux.vhd:12:18:error: type mark expected in a subtype indication
data_i : in array(NOF_INPUTS-1 downto 0) of DATA_T;
^
mux.vhd:12:18:error: ';' or ')' expected after interface
data_i : in array(NOF_INPUTS-1 downto 0) of DATA_T;
首先必须声明输入数据的类型。 所以也许一个解决方案是将该类型作为另一个泛型传递:
文件 mux.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
entity mux is
generic (
type DATA_T;
type DATA_INS_T;
NOF_INPUTS : integer);
port(
data_i : DATA_INS_T;
sel : in integer range 0 to NOF_INPUTS-1;
data_o : out DATA_T;
clk : in std_logic
);
end entity mux;
architecture rtl of mux is
begin -- architecture rtl
psel: process (clk) is
begin -- process psel
if clk'event and clk = '1' then
data_o <= data_i(sel);
end if;
end process psel;
end architecture rtl;
测试平台 mux_tb.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity mux_tb is
end entity mux_tb;
architecture sim of mux_tb is
-- First mux
constant NOF_INPUTS1 : integer := 4;
subtype DATA1_T is integer;
type DATA1_INS_T is array (NOF_INPUTS1-1 downto 0) of DATA1_T;
signal din1 : DATA1_INS_T := (10,-132,45,76);
signal dout1 : DATA1_T;
signal sel1 : integer range 0 to NOF_INPUTS1-1;
-- Second mux
constant NOF_INPUTS2 : integer := 3;
subtype DATA2_T is unsigned(4 downto 0);
type DATA2_INS_T is array (NOF_INPUTS2-1 downto 0) of DATA2_T;
signal din2 : DATA2_INS_T := (x"1",x"7",x"9");
signal dout2 : DATA2_T;
signal sel2 : integer range 0 to NOF_INPUTS2-1;
-- clock
signal clk : std_logic := '1';
begin -- architecture sim
-- component instantiation
DUT1: entity work.mux
generic map (
DATA_T => DATA1_T,
DATA_INS_T => DATA1_INS_T,
NOF_INPUTS => NOF_INPUTS1)
port map (
data_i => din1,
sel => sel1,
data_o => dout1,
clk => clk);
-- component instantiation
DUT2: entity work.mux
generic map (
DATA_T => DATA2_T,
DATA_INS_T => DATA2_INS_T,
NOF_INPUTS => NOF_INPUTS2)
port map (
data_i => din2,
sel => sel2,
data_o => dout2,
clk => clk);
-- clock generation
clk <= not clk after 10 ns;
-- waveform generation
WaveGen_Proc: process
begin
-- insert signal assignments here
wait until clk = '1';
sel1 <= 0;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 1;
sel2 <= 1;
wait until clk = '0';
wait until clk = '1';
sel1 <= 2;
sel2 <= 2;
wait until clk = '0';
wait until clk = '1';
sel1 <= 3;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 0;
sel2 <= 1;
wait;
end process WaveGen_Proc;
end architecture sim;
不幸的是,这并不奏效。 DATA_INS_T 类型被视为非数组。
mux.vhd:28:23:error: type of prefix is not an array
data_o <= data_i(sel);
VHDL 2008 中提供的另一个可能的解决方案是使用通用包。 所以我定义了通用包 mux_pkg.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package mux_pkg is
generic (
type DATA_T;
constant NOF_INPUTS : in integer
);
type DATA_INS_T is array (NOF_INPUTS-1 downto 0) of DATA_T;
end package;
我相应地修改了 mux.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
--use work.mux_pkg.all;
entity mux is
generic (
type DATA_T;
NOF_INPUTS : integer;
package my_pkg is new work.mux_pkg generic map (DATA_T => DATA_T, NOF_INPUTS => NOF_INPUTS)
);
port(
data_i : in my_pkg.DATA_INS_T;
sel : in integer range 0 to NOF_INPUTS-1;
data_o : out DATA_T;
clk : in std_logic
);
end entity mux;
architecture rtl of mux is
begin -- architecture rtl
psel: process (clk) is
begin -- process psel
if clk'event and clk = '1' then
data_o <= data_i(sel);
end if;
end process psel;
end architecture rtl;
测试平台 mux_tb.vhd 也进行了修改:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity mux_tb is
end entity mux_tb;
architecture sim of mux_tb is
-- First mux
constant NOF_INPUTS1 : integer := 4;
subtype DATA1_T is integer;
type DATA1_INS_T is array (NOF_INPUTS1-1 downto 0) of DATA1_T;
signal din1 : DATA1_INS_T := (10,-132,45,76);
signal dout1 : DATA1_T;
signal sel1 : integer range 0 to NOF_INPUTS1-1;
-- Second mux
constant NOF_INPUTS2 : integer := 3;
subtype DATA2_T is unsigned(3 downto 0);
type DATA2_INS_T is array (NOF_INPUTS2-1 downto 0) of DATA2_T;
signal din2 : DATA2_INS_T := (x"1",x"7",x"9");
signal dout2 : DATA2_T;
signal sel2 : integer range 0 to NOF_INPUTS2-1;
-- clock
signal clk : std_logic := '1';
begin -- architecture sim
-- component instantiation
DUT1: entity work.mux
generic map (
DATA_T => DATA1_T,
NOF_INPUTS => NOF_INPUTS1)
port map (
data_i => din1,
sel => sel1,
data_o => dout1,
clk => clk);
-- component instantiation
DUT2: entity work.mux
generic map (
DATA_T => DATA2_T,
NOF_INPUTS => NOF_INPUTS2)
port map (
data_i => din2,
sel => sel2,
data_o => dout2,
clk => clk);
-- clock generation
clk <= not clk after 10 ns;
-- waveform generation
WaveGen_Proc: process
begin
-- insert signal assignments here
wait until clk = '1';
sel1 <= 0;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 1;
sel2 <= 1;
wait until clk = '0';
wait until clk = '1';
sel1 <= 2;
sel2 <= 2;
wait until clk = '0';
wait until clk = '1';
sel1 <= 3;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 0;
sel2 <= 1;
wait;
end process WaveGen_Proc;
end architecture sim;
makefile 还需要一些小改动:
STD=synopsys
VSTD=08
ENTITY=mux_tb
RUN_OPTIONS= --stop-time=200ns --wave=${ENTITY}.ghw
SOURCES = \
mux_pkg.vhd \
mux.vhd \
mux_tb.vhd \
OBJECTS=$(SOURCES:.vhd=.o)
all: $(OBJECTS)
%.o : %.vhd
ghdl -a -g -C --std=${VSTD} --ieee=${STD} $<
all: ${ENTITY}.ghw test
show: ${ENTITY} ${ENTITY}.ghw
gtkwave ${ENTITY}.ghw ${ENTITY}.sav
${ENTITY}: $(SOURCES:.vhd=.o)
ghdl -e -g --mb-comments --std=${VSTD} -fexplicit --ieee=${STD} ${ENTITY}
${ENTITY}.ghw: ${ENTITY}
./${ENTITY} ${RUN_OPTIONS}
clean:
rm -f *.o *.vcd *.ghw *.cf ${ENTITY}
不幸的是,这种尝试也导致了错误:
mux_tb.vhd:37:17:error: can't associate 'din1' with port "data_i"
data_i => din1,
^
mux_tb.vhd:37:17:error: (type of 'din1' is data1_ins_t)
mux.vhd:15:5:error: (type of port "data_i" is data_ins_t)
mux_tb.vhd:48:17:error: can't associate 'din2' with port "data_i"
data_i => din2,
^
mux_tb.vhd:48:17:error: (type of 'din2' is data2_ins_t)
mux.vhd:15:5:error: (type of port "data_i" is data_ins_t)
ghdl:error: compilation error
所以,我的问题是。 VHDL 2008 中提供的新功能是否有助于解决重复使用同一块来处理不同类型数据的问题?
正如我在帖子中提到的,我有一个基于使用单独的库(每种类型的处理数据一个)的解决方案。但是,它不需要 VHDL 2008。在该解决方案中,我将类型的定义放入单独的包中。
文件 data1_pkg.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package data1_pkg is
constant NOF_INPUTS1 : integer := 4;
subtype DATA1_T is integer;
type DATA1_INS_T is array (NOF_INPUTS1-1 downto 0) of DATA1_T;
end package;
文件 data2_pkg.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package data2_pkg is
constant NOF_INPUTS2 : integer := 3;
subtype DATA2_T is unsigned(3 downto 0);
type DATA2_INS_T is array (NOF_INPUTS2-1 downto 0) of DATA2_T;
end package;
此外,我还为使用上述定义的多路复用器实体准备了包:
文件 my_pkg1.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.data1_pkg.all;
package my_pkg is
subtype DATA_T is DATA1_T;
subtype DATA_INS_T is DATA1_INS_T;
constant NOF_INPUTS : integer := NOF_INPUTS1;
end package;
并文件 my_pkg2.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.data2_pkg.all;
package my_pkg is
subtype DATA_T is DATA2_T;
subtype DATA_INS_T is DATA2_INS_T;
constant NOF_INPUTS : integer := NOF_INPUTS2;
end package;
它们都提供包 my_pkg,但它们将被编译成不同的库。
mux.vhd 文件现在很简单:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.my_pkg.all;
entity mux is
port(
data_i : in DATA_INS_T;
sel : in integer range 0 to NOF_INPUTS-1;
data_o : out DATA_T;
clk : in std_logic
);
end entity mux;
architecture rtl of mux is
begin -- architecture rtl
psel: process (clk) is
begin -- process psel
if clk'event and clk = '1' then
data_o <= data_i(sel);
end if;
end process psel;
end architecture rtl;
测试平台 mux_tb.vhd 根据处理数据的类型使用在单独库中定义的多路复用器:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library lib1;
use lib1.data1_pkg.all;
library lib2;
use lib2.data2_pkg.all;
entity mux_tb is
end entity mux_tb;
architecture sim of mux_tb is
-- First mux
signal din1 : DATA1_INS_T := (10,-132,45,76);
signal dout1 : DATA1_T;
signal sel1 : integer range 0 to NOF_INPUTS1-1;
-- Second mux
signal din2 : DATA2_INS_T := (x"1",x"7",x"9");
signal dout2 : DATA2_T;
signal sel2 : integer range 0 to NOF_INPUTS2-1;
-- clock
signal clk : std_logic := '1';
begin -- architecture sim
-- component instantiation
DUT1: entity lib1.mux
port map (
data_i => din1,
sel => sel1,
data_o => dout1,
clk => clk);
-- component instantiation
DUT2: entity lib2.mux
port map (
data_i => din2,
sel => sel2,
data_o => dout2,
clk => clk);
-- clock generation
clk <= not clk after 10 ns;
-- waveform generation
WaveGen_Proc: process
begin
-- insert signal assignments here
wait until clk = '1';
sel1 <= 0;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 1;
sel2 <= 1;
wait until clk = '0';
wait until clk = '1';
sel1 <= 2;
sel2 <= 2;
wait until clk = '0';
wait until clk = '1';
sel1 <= 3;
sel2 <= 0;
wait until clk = '0';
wait until clk = '1';
sel1 <= 0;
sel2 <= 1;
wait;
end process WaveGen_Proc;
end architecture sim;
编译和模拟现在通过 shell 脚本 build.sh 运行:
#!/bin/bash
# Do cleanup
rm -rf *.o *.cf lib1 lib2
mkdir lib1
mkdir lib2
STD=synopsys
VSTD=02
ENTITY=mux_tb
RUN_OPTIONS="--stop-time=200ns --wave=${ENTITY}.ghw"
LIB1_SRC="\
data1_pkg.vhd \
my_pkg1.vhd \
mux.vhd \
"
LIB2_SRC="\
data2_pkg.vhd \
my_pkg2.vhd \
mux.vhd \
"
SOURCES=" \
mux_tb.vhd \
"
for f in ${LIB1_SRC}; do
ghdl -a -g -C --work=lib1 --workdir=lib1 --std=${VSTD} --ieee=${STD} $f
done
echo Done lib1
for f in ${LIB2_SRC}; do
ghdl -a -g -C --work=lib2 --workdir=lib2 --std=${VSTD} --ieee=${STD} $f
done
echo done lib2
for f in ${SOURCES}; do
ghdl -a -P./lib1/ -P./lib2/ --std=${VSTD} -g -C --ieee=${STD} $f
done
echo Analyzed
ghdl -e -g -P./lib1/ -P./lib2/ --mb-comments --std=${VSTD} -fexplicit --ieee=${STD} ${ENTITY}
./${ENTITY} ${RUN_OPTIONS}
这个方法非常有效。但是它不使用新的 VHDL 2008 功能...