VHDL constant value overflow (original) (raw)
August 29, 2018, 12:35pm 1
How do MyHDL people usually work around the VHDL size limit for integer literals? Consider the following MyHDL code:
Y_LEN = 71
Y_MIN, Y_MAX = (-2**(Y_LEN - 1), 2**(Y_LEN - 1))
y = Signal(intbv(0, Y_MIN, Y_MAX))
# An attempt to implement saturation somewhere in the code
if result < 0:
accumulator.next = Y_MIN
else:
accumulator.next = Y_MAX - 1
This converts to VHDL as follows:
if (result < 0) then
accumulator <= signed'("11111111111111111111111111111111111100000000000000000000000000000000000");
else
accumulator <= to_signed(34359738368 - 1, 71);
end if;
The first case translates to a bit string literal, which is fine (although hexadecimal would be much easier to read than binary at these lengths). The second case with the arithmetic operation translates to an integer literal that is too large – at least as far as the compiler is concerned. Tweaking the constants would take care of this, but all the added plus ones and minus ones are not exactly good for reducing off-by-one errors. Are there any other options?
mhavu August 30, 2018, 9:28am 2
Would it be difficult or undesirable for some reason to have MyHDL compute the result of constant expressions and replace them with bit string literals, just like in the case of a single constant?
josyb August 30, 2018, 11:28am 3
IMHO it should be fairly straightforward to do, and surely an improvement. It would even be nicer if his code would generate a constant declaration:
constant prefix_Y_MIN : signed(70 downto 0) := b"100...000...000...000";
constant prefix_Y_MAX : signed(70 downto 0) := x"3fffffffffffffffff";
constant prefix_Y_MAX_ALT : signed(70 downto 0) := (x"3fffffffffffffffff")(70 downto 0);
I’m not sure whether the hexadecimal notation will apply if the bits are a: not a multiple of 4 and b: more than 32. Perhaps the the alternative notation works?
BTW: 34359738368 != 2**70, neither is the binary string equal to -2**70
mhavu August 30, 2018, 1:30pm 4
Ah, yes! Constant declarations would be sweet. I could try implementing them myself, but I find the MyHDL source code a bit hard to navigate, so I don’t know where to look for it.
Hexadecimal string literals need to contain a multiple of four bits, but there is no limit to the length. Only integers are limited to 32 bits. (Or is it 34 bits in the spec? Vendor tools may have the 32-bit limit anyway.)
True. The saturation is at 36 bits. I copied a piece of code without thinking, so the Y_MIN and Y_MAX are not what they are supposed to be.
josyb August 30, 2018, 1:53pm 5
I wonder whether the community is ready for a Constant
type, but I think we can do it under the radar by tweaking the _toVHDL.py code.
It would be nice if you’d provide small but complete example code; it saves me from having to type (and think) to get a test running.
mhavu August 31, 2018, 6:07am 6
Would this Q0.x fixed-point gain be small enough?
"""Scale fixed-point signal"""
from myhdl import block, always_comb, always_seq
from myhdl import Signal, ResetSignal, intbv
@block
def rescale(clk, reset, x, gain, shift, y):
"""Rescale with fractional gain and bit shift
The rescale block operates with signed (two's complement) fixed-point
numbers [-1.0, 1.0) with the following roles:
Input: x
Output: y = gain * x << shift
"""
X_LEN = len(x)
Y_LEN = len(y)
GAIN_LEN = len(gain)
Y_MIN, Y_MAX_M1 = (-2**(Y_LEN - 1), 2**(Y_LEN - 1) - 1)
ZERO_SHIFT = GAIN_LEN - 1 + X_LEN - Y_LEN
RESULT_LEN = Y_LEN + shift
RESULT_MIN, RESULT_MAX = (-2**(RESULT_LEN - 1), 2**(RESULT_LEN - 1))
# Internal signals
result = Signal(intbv(0, RESULT_MIN, RESULT_MAX))
@always_comb
def logic():
"""Rescale"""
result.next = x * gain >> ZERO_SHIFT - shift
@always_seq(clk.posedge, reset=reset)
def propagate():
"""Update y, and saturate in case of overflow"""
if result[:Y_LEN].signed() > 0:
y.next = Y_MAX_M1
elif result[:Y_LEN].signed() < -1:
y.next = Y_MIN
else:
y.next = result
return logic, propagate
# Auxiliary functions
def convert(hdl):
"""Convert the design to HDL"""
clk = Signal(bool(0))
reset = ResetSignal(0, active=1, async=True)
gain, shift = [Signal(intbv(0)[32:]) for _ in range(2)]
x, y = [Signal(intbv(0)[32:]) for _ in range(2)]
rtl = rescale(clk, reset, x, gain, shift, y)
rtl.convert(hdl)
if __name__ == '__main__':
convert('VHDL')
It doesn’t have constant arithmetic expressions aside from the declaration, though, but one could change the following lines to make one:
Y_MIN, Y_MAX = (-2**(Y_LEN - 1), 2**(Y_LEN - 1))
y.next = Y_MAX - 1
Edit: The original example was purely combinational, but I’m not sure not sure whether it was actually synthesizable.
DrPi August 31, 2018, 11:45am 7
With VHDL 2008, you can specify the bit length of the literal : 3X"2" is equal to “010”.
The MyHDL VHDL converter already uses this notation.
We already spoke about this before in other threads. I think we need constants in MyHDL.
I already started to code such a functionality but only for VHDL, not for verilog.
josyb August 31, 2018, 12:03pm 8
I maybe should have said senate in stead of community
mhavu August 31, 2018, 12:21pm 9
Do you mean this one? What do you guys think about adding generics support in the constant type? In my opinion, the constructor for the constant type could have a named parameter with default value parameter=False
. If parameter
were set to True
, a generic would be generated instead of a constant.
DrPi August 31, 2018, 12:37pm 10
@mhavu
No, not this one.
There are few threads where it is discussed. I don’t remember which one exactly except this one : VHDL conversion - missing constant
From my point of view, the problem with MyHDL dev is with PRs that are very long to be accepted/rejected ( A future for the MyHDL community?)
mhavu October 26, 2018, 10:51am 11
I got bit by this again. MyHDL doesn’t always handle the large constants correctly. Take, for example, this piece of MyHDL-generated VHDL:
if (signed(unsigned(rescale1_result(68-1 downto drop))) >= 68719476735) then
scaled_output <= signed'("011111111111111111111111111111111111");
rescale2_overflow <= '1';
elsif (signed(unsigned(rescale1_result(68-1 downto drop))) < (- 68719476736)) then
scaled_output <= signed'("100000000000000000000000000000000000");
rescale2_overflow <= '1';
else
scaled_output <= resize(signed(unsigned(rescale1_result(68-1 downto (drop + 1)))) + to_signed(rescale1_result(drop), 2), 36);
rescale2_overflow <= '0';
end if;
68719476735 and -68719476736 are values of two MyHDL constants. They are over 32 bits long, so I would have expected to see them converted to bit string literals. However, the conversion result is a 37-bit wide integer that breaks the compilation in Quartus. Can someone point me to a place in the MyHDL source code where this happens, so I can fix it? (Or if @DrPi’s constant type is ready, I’d be glad to test it.)
DrPi October 26, 2018, 7:19pm 12
Can you provide a fully functional minimal example reproducing the problem ?
josyb October 28, 2018, 8:00am 13
In this case as the test-values are a power of 2 you could, as a work-around, use simple bit checking:
SCALING = 36
if not rescale_result[high] and rescale_result[high - 1 : drop + SCALING] != 0:
scaled_output.next = ...
elif rescale_result[high] and rescale_result[high - 1 : drop + SCALING] == 0:
scaled_output.next = ....
else:
...
This is how I did it in VHDL
mhavu October 29, 2018, 1:41pm 14
@DrPi, here’s a minimum working example:
from myhdl import block, always_comb, Signal, intbv
@block
def test_constant(x, y):
THRESHOLD = 2**(len(x) - 2)
@always_comb
def logic():
if x > THRESHOLD:
y.next = 1
else:
y.next = 0
return logic
if __name__ == '__main__':
x = Signal(intbv(0, -2**35, 2**35))
y = Signal(bool(0))
rtl = test_constant(x, y)
rtl.convert('VHDL')
The VHDL conversion results in:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use std.textio.all;
use work.pck_myhdl_010.all;
entity test_constant is
port (
x: in signed (35 downto 0);
y: out std_logic
);
end entity test_constant;
architecture MyHDL of test_constant is
begin
TEST_CONSTANT_LOGIC: process (x) is
begin
if (x > 17179869184) then
y <= '1';
else
y <= '0';
end if;
end process TEST_CONSTANT_LOGIC;
end architecture MyHDL;
The threshold constant 17179869184 is wider than 32 bits, but it is not expressed as a bit string literal.
@josyb, I used to do that (check that the high bits are either all 0 or all 1). If I remember correctly, it results in fewer FPGA resources being used as a bonus. However, if you want rounding, a new case is introduced (high bits all 0, low bits all 1), so the code would be much more readable with simple >= and < checks.
DrPi October 30, 2018, 10:02am 15
As a quick workaround, you can declare THRESHOLD like this :
THRESHOLD = Signal(intbv(2**(len(x) - 2), -2**35, 2**35))
I’m working on a better solution.
DrPi November 5, 2018, 10:30am 16
The fix is 4 lines of code in _toVHDL.py module.
In _AnnotateTypesVisitor() class, the new code for visit_Compare() function is the following :
def visit_Compare(self, node):
node.vhd = vhd_boolean()
self.generic_visit(node)
left, op, right = node.left, node.ops[0], node.comparators[0]
if isinstance(left.vhd, vhd_std_logic) or isinstance(right.vhd, vhd_std_logic):
left.vhd = right.vhd = vhd_std_logic()
elif isinstance(left.vhd, vhd_unsigned) and maybeNegative(right.vhd):
left.vhd = vhd_signed(left.vhd.size + 1)
elif maybeNegative(left.vhd) and isinstance(right.vhd, vhd_unsigned):
right.vhd = vhd_signed(right.vhd.size + 1)
if isinstance(left.vhd, (vhd_nat, vhd_int)) : # New
left.vhd = right.vhd # New
if isinstance(right.vhd, (vhd_nat, vhd_int)) : # New
right.vhd = left.vhd # New
node.vhdOri = copy(node.vhd)
josyb July 24, 2024, 10:35am 17
Hi all,
This thread has been recently (July 9th) mentioned on Matrix (previously: Gitter)
The issue is that the Vendors VHDL may restrict integers to 32 bits - GHDL on my machine allows 64 bits - the MyHDL simulator knows no boundaries
The good thing that since 0.11.43 we can declare a constant:
THRESHOLD = Constant(intbv(2 ** (len(w) - 2), min=-2 ** (len(w) - 1), max=2 ** (len(w) - 1))
giving this code:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use std.textio.all;
use work.pck_myhdl_011.all;
entity sconstant is
port(
w : in signed(65 - 1 downto 0);
a : out std_logic
);
end entity sconstant;
architecture MyHDL of sconstant is
constant THRESHOLD : signed(65 - 1 downto 0) := 65X"08000000000000000"; -- 9223372036854775808
begin
SCONSTANT_COMB : process(w) is
begin
if (w > THRESHOLD) then
a <= '1';
else
a <= '0';
end if;
end process SCONSTANT_COMB;
end architecture MyHDL;
Note that you explicitly have to declare an intbv.
The not so good news is that is not yet entered in the official MyHDL documentation so people have a hard time finding this gem.
The second not so good news is that the Verilog output is not always correct so that needs revisiting