我正在尝试用VHDL实现这个方程,它有一些常数的乘法和加法。方程式如下,
y<=-((x*x*x)*0.1666)+(2.5*(x*x))- (21.666*x) + 36.6653; ----error
我得到错误
HDLCompiler:1731 - found '0' definitions of operator "*",
can not determine exact overloaded matching definition for "*".
实体是
entity eq1 is
Port ( x : in signed(15 downto 0);
y : out signed (15 downto 0) );
end eq1;
我尝试使用函数RESIZE和整数中的x,但它给出了相同的错误。我应该使用另一种数据类型吗?x具有纯整数值,如2,4,6..等
由于x
和y
的数据类型为signed
,因此可以将它们相乘。然而,不存在符号与实数的乘积。即使有,结果也是真实的(不是有符号的或整数的)。
因此,首先,您需要弄清楚您想要什么(语义)。然后应该添加类型转换和转换函数。
y <= x*x; -- OK
y <= 0.5 * x; -- not OK
y <= to_signed(integer(0.5 * real(to_integer(x))),y'length); -- OK
这是合成前模拟可能很方便的另一种情况。对于实例,ghdl告诉您它找到的第一个错误是哪个"*"运算符:
ghdl-一个实现.vhdl
实现.vhdl:12:21:没有运算符"*"的函数声明
y <= -((x*x*x) * 0.1666) + (2.5 * (x*x)) - (21.666 * x) + 36.6653;
---------------^ character position 21, line 12
与x
相乘的表达式具有类型为signed
的两个操作数。
(稍后,我们还注意到,当分配给y
时,信号分配操作右侧的复表达式最终将被解释为具有窄子类型约束的signed
值)。
VHDL确定文字0.1666
的类型,它是一个抽象文字,即十进制文字或浮点文字(IEEE Std 1076-2008 5.2.5浮点类型,5.2.5.1概述,第5段):
浮点文字是匿名预定义类型的文字,在本标准中称为universal_rea。其他浮点类型没有文字。但是,对于每种浮点类型,都存在一个隐式转换,将类型为universal_real的值转换为浮点类型的相应值(如果有的话)(见9.3.6)
VHDL中只有一个预定义的浮点类型,请参阅5.2.5.2,并且类型universal_real的浮点文字隐式转换为类型real。
9.3.6类型转换第14段告诉我们:
在某些情况下,将执行隐式类型转换。只有当操作数是数字文字或属性,或者操作数是由物理类型的值除以相同类型的值组成的表达式时,才能应用将类型为universal_integer的操作数隐式转换为另一个整数类型,或将类型为universal-real类型的操作数显式转换为其他浮点类型;这样的操作数称为可转换通用操作数。当且仅当最内部的完整上下文为隐式转换确定了唯一的(数字)目标类型时,才应用可转换通用操作数的隐式转换,并且如果没有此转换,就没有对此上下文的合法解释。
因为您还没有包含包含另一个浮点类型的包,因此我们需要搜索一个具有一个类型为signed
的操作数的"*"
乘法运算符和一个返回类型为signed
的类型为REAL的运算符(或另一个具有相反操作数类型自变量的"*"
运算符),VHDL找到了其中的0个。
没有
function "*" (l: signed; r: REAL) return REAL;
或
function "*" (l: signed; r: REAL) return signed;
在包numeric_std中找到。
Phillipe提出了一种将signed
x
转换为整数的方法来克服这一问题。
从历史上看,合成不包括REAL类型,在2008版本的VHDL标准之前,您可能具有任意精度,而5.2.5第7段现在告诉我们:
除符合IEEE Std 754-1985或IEEE Std 854-1987的universal_real外,实现应为所有浮点类型选择一种表示;在任何一种情况下,对于所选择的表示,都需要64位的最小表示大小。
除非合成工具支持REAL的浮点类型并且符合-2008,否则这对我们没有帮助。
VHDL在2008版本中引入了float_generic_pkg包,该包执行符合合成条件的浮点运算,并通过转换为其浮点类型和从其浮点类型转换为signed
类型来兼容所使用的类型。
在我们建议将所有这些计算作为64位浮点数进行并综合之前,让我们再次注意,结果是16位signed
,它是std_ulogic的数组类型,表示16位整数。
您可以将右侧的乘法建模为以浮点或带符号执行的不同表达式表示,以确定误差何时显著。
因为y
使用的是16位带符号值,所以显著值意味着大小上的差异大于1。两种方法之间的翻转符号或意外0可能会告诉您存在精度问题。
我写了一个C程序来观察差异,它一开始就告诉我们16位不足以容纳数学:
int16_t x, y, Y;
int16_t a,b,c,d;
double A,B,C,D;
a = x*x*x * 0.1666;
A = x*x*x * 0.1666;
b = 2.5 * x*x;
B = 2.5 * x*x;
c = 21.666 * x;
C = 21.666 * x;
d = 36;
D = 36.6653;
y = -( a + b - c + d);
Y = (int16_t) -(A + B - C + D);
x
最左边值的输出:
x = -32767, a = 11515, b = 0, c = 10967, y = -584, Y = 0
x = -32767, A = -178901765.158200, B = 2684190722.500000, C = -709929.822000
x = -32767 , y = -584 , Y= 0, double = -2505998923.829100
输出的第一行是16位乘法,您可以看到所有三个乘法表达式都不正确。
第二行表示double有足够的精度,但Y(-(A+B-C+D))不适合16位数字。除非输入大小保持不变,否则无法通过增大结果大小来解决这一问题。然后,链式操作就变成了挑选最佳产品并跟踪规模的问题,这意味着你还不如使用浮点运算。
如果合适的话,你当然可以夹紧。输出的第三行上的双精度值是未截断的值。它比x'LOW
更负。
您也可以在16位数学域中进行箝位,尽管所有这些告诉您,除非在浮点中进行,否则这种数学在硬件域中没有任何意义。
因此,如果你试图在硬件中解决一个真正的数学问题,它将需要浮点,很可能使用float_generic_pkg包来完成,并且不会有意义地适合16位的结果。
如VHDL中运算符"+"的"0"定义所述,VHDL编译器无法为您的操作找到匹配的运算符,例如乘以x*x
。您可能想要使用numeric_std
(请参阅此处),以便使signed
(和unsigned
)的运算符可用。
但请注意,VHDL不是一种编程语言,而是一种硬件设计语言。也就是说,如果你的长期目标是将代码转移到FPGA或CPLD,这些功能可能不再工作,因为它们是不可合成的。
我之所以这么说,是因为当你试图与例如0.1666
相乘时,你会遇到更多的问题,因为VHDL通常不知道浮点数。