VHDL signed data in std_logic_vector to unsigned data - casting

I have an ADC (LTC1407A-1) on an FPGA board that is populating a 14-bit register with 2's compliment signed data. I want to convert this data to be unsigned:
Source data range: -8192 to 8191
Target data range: 0 to 16383
However, whatever I try, I can't seem to get the desired result. My current section of working code for the VHDL register module is as follows:
library IEEE;
use ieee.std_logic_arith.all;
use ieee.std_logic_1164.all;
entity reg10 is
Port ( clk : in STD_LOGIC; --50MHz clock
reset : in STD_LOGIC; --asynchronous reset
i_load : in STD_LOGIC; --load signal
i_data : in STD_LOGIC_VECTOR (13 downto 0); --data signal
o_data : out STD_LOGIC_VECTOR (15 downto 0) --output data
);
end reg10;
architecture Behavioral of reg10 is
signal s_data : STD_LOGIC_VECTOR(9 downto 0);
signal f_data : STD_LOGIC_VECTOR(13 downto 0);
signal t_sign : signed (13 downto 0);
begin
process(clk, reset)
begin
if reset = '1' then
s_data <= "0000000000";
elsif clk'event and clk = '1' then
t_sign <= SIGNED(i_data);
f_data <= STD_LOGIC_VECTOR(t_sign);
s_data <= "00" & f_data(13 downto 6);
end if;
end process;
o_data <= "000010" & s_data(9 downto 0);
end Behavioral;
I have done plenty of searching around and find lots of examples where the conversion can be done, but, I don't understand the correct approach to take. I have tried assigning i_data as signed, casting between variables internally, and many other recommended solutions, but all to no avail.
signal t_sign : signed (13 downto 0);
f_data <= conv_std_logic_vector(i_data, 14);
The code buffers a changing input vector, and formats the data into an output vector for a VGA controller to display.
Help appreciated. Thanks.

Your i_data has a range of 13 downto 0. As Brian mentioned the conversion can be accomplished by adding 8192.
8192 is "1_0000_0000_0000" as a bit_string representing binary with a length matching i_data.
That means to convert by addition you're only flipping the MSB in addition with a result the length of the longest operand. Because you're also truncating in the s_data assignment you only need 8 flip flops.
For:
signal s_data: std_logic_vector (7 downto 0);
we could assign s_data as:
s_data <= (i_data(13) xor '1') & i_data(12 downto 6);
where the o_data assignment becomes:
o_data <= "00001000" & s_data;
Without concern for carry the addition simplifies to a single XOR gate to flip the sign converting two's complement to a binary magnitude expression.
f_data and t_data are not needed as J.H. Bonarius indicates unless two additional pipeline stages are desired.
You could also ask whether registering is needed at all when only introducing one gate delay.
If the register is eliminated:
o_data <= "00001000" & (i_data(13) xor '1') & i_data(12 downto 6);
and you can also note that at no time are 'bits' i_data(5 downto 0) used.
And because XOR with a constant '1' on one input is inversion:
o_data <= "00001000" & not i_data(13) & i_data(12 downto 6);
that operation can be logical not.
Note that an arithmetic package is not needed nor are type conversions (cast is inaccurate in VHDL, type conversion is allowed only between closely related types see IEEE Std 1076-2008 9.3.6 Type conversions).
You could also expect that synthesis would optimize adding 8192 to essentially the same result if the apparently unnecessary pipeline registers are eliminated. In a modification of your original model to add 8192, that would also imply some of those pipeline register 'bits' be optimized away.
Your design model also doesn't use i_load.

The first error is:
use ieee.std_logic_arith.all;
Don't do that. Do this:
use ieee.numeric_std.all;
The other library already treats std_logic as signed or unsigned. You shouldn't want that.
Then cast from signed to std_logic_vector to unsigned. e.g.:
[signal] foo <= unsigned(std_logic_vector(signed(bar)));
however, in you case the input is already std_logic, so just cast to unsigned. It should work, unless your input representation is non-standard. (I.e. has an offset). IF it has an offset, do something like:
[signal] foo <= unsigned(std_logic_vector(signed(bar)+offset));
N.B. <= [signal] assignments are assigned at the next delta (https://en.wikipedia.org/wiki/Delta_delay). So in a process that is evaluated within one delta (i.e. without wait statements), they are not applied until after the process ends. So in you example, even though f_data is assigned by the data in t_sign the line after t_sign is assigned, t_sign will not change until the end of the process, so its change will not directly effect f_data. Instead the process will trigger on the next clk and clk'event and clk = '1' will assign f_data the new processed value t_sign. In effect each <= will insert a register in your example.
edit:
I myself would probably use a cast to integer.
foo <= to_unsigned(to_integer(bar)+offset, foo'length);

Related

Why multiplying an integer by a number gives wrong answers in C Arduino? [duplicate]

This question already has answers here:
Arduino Uno is making errors when doing calculations
(2 answers)
Arduino computing error - giving negative values when it shouldn't
(2 answers)
Closed 3 months ago.
I am trying to calculate the value of the potentiometer equivalent to the angle, so I need to multiply by 180 and then divide by the range.
Upon doing so, I noticed that the number I'm getting is not the number I was expecting so I started debugging by multiplying by 180 only and realized the output was not as predicted.
Here is the simple code that outputs the weird readings:
`
#define POTENTIOMETER_PIN A0
int val;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
// put your main code here, to run repeatedly:
void loop()
{
val = analogRead(POTENTIOMETER_PIN);
Serial.println(val*180);
delay(250);
}
`
A value between (0 to 1023)*180 was expected, rather the serial monitor spits out values such as:
-18932
-18752
-18572
-18392
-18392
Take a look at the map() function for your use case
https://www.arduino.cc/reference/en/language/functions/math/map/
int val = 0; // assign at declaration
void loop()
{
val = analogRead(POTENTIOMETER_PIN); // read value
val = map(val, 0, 1023, 0, 180); // convert into 180 range
Serial.println(val); // display value
}
Internally, this makes use of a larger type than the humble int, using a long, which is (probably) 32-bits rather than 16
From the documentation above
long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
The reason this works better for you and can do the math correctly is because a 16-bit number doesn't contain enough precision for the multiplication, while map() uses a 32-bit number internally to represent the value while it does the math on it!
Specifically, if, during your logic you rely on 16-bit math, the value exceeds that ~32768 range, it will wrap around and become negative!
This is known as Integer Overflow, which the Wikipedia article linked has a great demonstration of using a car odometer
the 100,000th mile is overflowing and wraps around to 1 because the first digit doesn't have room and so is truncted
Some more labouring the point from the int docs on Arduino, specifically because while there is much debate (including why Arduino chooses to supply int at all) it's generally good practice to always specify exactly the type you you're after such as uint32_t, rather than relying on int, which frustratingly can have a different size on different device targets
https://www.arduino.cc/reference/en/language/variables/data-types/int/
On the Arduino Uno (and other ATmega based boards) an int stores a 16-bit (2-byte) value. This yields a range of -32,768 to 32,767 (minimum value of -2^15 and a maximum value of (2^15) - 1).
On the Arduino Due and SAMD based boards (like MKR1000 and Zero), an int stores a 32-bit (4-byte) value. This yields a range of -2,147,483,648 to 2,147,483,647 (minimum value of -2^31 and a maximum value of (2^31) - 1).
It would seem that your compiler defines int as being a 16 bit signed number.
Thus there is a limit of 32767 to -32768.
You are exceeding this and so the values 'wrap around'.
Try changing the code to use 'long' instead of 'int'.

VHDL conditional type generation (flavor of FW)

I'm doing a DIY project with robots with different sensors.
So I'm trying to have the maximum common code for all of them and here I get to the question:
it's possible to do something like if..generate in the packages?
the next code is wrong but it's for illustrate what would be the best way to do it:
constant robo_type : integer := 1;
-- 6 legs, motors with encoders
6_legs_2_parts : if robo_type = 0 generate
type leg_sens is record
angle1 is array (5 downto 0) of integer range 0 to 1000;
angle2 is array (5 downto 0) of integer range 0 to 1000;
end record;
end generate;
-- 6 legs, motors with encoders
6_legs_3_parts : if robo_type = 1 generate
type leg_sens is record
angle1 is array (5 downto 0) of integer range 0 to 1000;
angle2 is array (5 downto 0) of integer range 0 to 1000;
angle3 is array (5 downto 0) of integer range 0 to 1000;
end record;
end generate;
-- 4 legs motors, encoders & current
4_legs_2_parts : if robo_type = 2 generate
type leg_sens is record
angle1 is array (3 downto 0) of integer range 0 to 1000;
angle2 is array (3 downto 0) of integer range 0 to 1000;
amp1 is array (3 downto 0) of integer range 0 to 100;
amp2 is array (3 downto 0) of integer range 0 to 1000;
end record;
end generate;
Now I use something like this, and relay in that the compiler will optimize what I'm not using
-- generic leg_sens for all robots
constant numAngles : integer := 4;
type leg_sens is record
angle1 is array (numAngles -1 downto 0) of integer range 0 to 1000;
angle2 is array (numAngles -1 downto 0) of integer range 0 to 1000;
angle3 is array (numAngles -1 downto 0) of integer range 0 to 1000;
amp1 is array (numAngles -1 downto 0) of integer range 0 to 100;
amp2 is array (numAngles -1 downto 0) of integer range 0 to 100;
amp3 is array (numAngles -1 downto 0) of integer range 0 to 100;
end record;
I found this: https://electronics.stackexchange.com/questions/237770/vhdl-if-generate-in-the-preamble-is-it-possible but it isn't what I'm looking for.
So, I'm looking if it's possible to do conditionals inside the packages,
I didn't found anything on the IEEE Standard VHDL Language Reference Manual (IEEE Std 1076™-2008)
Thanks
VHDL-2008 allows composites to have unconstrained elements. Hence, you can define the following. No promises though if your synthesis tool will like you. Your best bet is to use your "leg_sens" type.
type AngleType is array (integer range <>) of range 0 to 1000; -- regular VHDL
type AngleArrayType is array (integer range <>) of AngleType; -- VHDL-2008
type AmplitudeType is array (integer range <>) of range 0 to 100 ;
type AmplitudeArrayType is array (integer range <>) of AmplitudeType ;
type RobotLegType is record
Angle : AngleArrayType ;
Amp : AmplitudeArrayType ;
end record RobotLegType ;
-- 6 legs, motors with encoders
subtype Legs6Parts2Type is
RobotLegType(
Angle(1 to 2)(5 downto 0),
Amp(1 to 0)(5 downto 0) -- Amp is intentionally a null array (hence empty)
) ;
-- 6 legs, motors with encoders
subtype Legs6Parts3Type is
RobotLegType(
Angle(1 to 3)(5 downto 0),
Amp(1 to 0)(5 downto 0) -- Amp is intentionally a null array (hence empty)
) ;
-- 4 legs motors, encoders & current
subtype Legs4Parts2AmpType is
RobotLegType(
Angle(1 to 2)(3 downto 0),
Amp (1 to 2)(3 downto 0)
) ;

How to ignore specific bits in a 32-bit integer

I am polling a 32-bit register in a motor driver for a value.
Only bits 0-9 are required, the rest need to be ignored.
How do I ignore bits 10-31?
Image of register bits
In order to poll the motor driver for a value, I send the location of the register, which sends back the entire 32-bit number. But I only need bits 0-9 to display.
Serial.println(sendData(0x35, 0))
If you want to extract such bits then you must mask the whole integer with a value that keeps just the bits you are interested in.
This can be done with bitwise AND (&) operator, eg:
uint32_t value = reg & 0x3ff;
uint32_t value = reg & 0b1111111111; // if you have C++11
Rather than Serial.println() I'd go with Serial.print().
You can then just print out the specific bits that you're interested in with a for loop.
auto data = sendData(0x35, 0);
for (int i=0; i<=9; ++i)
Serial.print(data && (1<<i));
Any other method will result in extra bits being printed since there's no data structure that holds 10 bits.
You do a bitwise and with a number with the last 10 bits set to 1. This will set all the other bits to 0. For example:
value = value & ((1<<10) - 1);
Or
value = value & 0x3FF;

casting a integer variable to float

#FRob's answer to my recent question (to_float() and dividing errors) led me to analyze the float_pkg_c.vhdl, particularly the to_float method.
When trying the following operation:
variable denum : integer;
variable num : integer;
variable dividend : float (4 downto -27);
begin
dividend := to_float(num, 4, 27) / to_float(denum, 4, 27);
...
I keep getting this error: "Error (10454): VHDL syntax error at float_pkg_c.vhdl(3840): right bound of range must be a constant"
Now, at the mentioned line:
for I in fract'high downto maximum (fract'high - shift + 1, 0) loop
The variable fract is calculated based on the parameter fraction_width, which is 27 in my case, therefore a constant.
However, the shift variable is calculated based on the arg parameter (basically, a log2 of the absolute value of arg), which is the num variable in my case, therefore not a constant.
So, the error is clear, but my question is: How can I cast a integer variable to float?
This is the definition of to_float:
function to_float (
arg : INTEGER;
constant exponent_width : NATURAL := float_exponent_width; -- length of FP output exponent
constant fraction_width : NATURAL := float_fraction_width; -- length of FP output fraction
constant round_style : round_type := float_round_style) -- rounding option
What is even more confusing to me is that arg in the above definition is not required ti be a constant.
After spending a few hours reading up on synthesizing loops and trying to translate the to_float with integer arg I had a thought:
library ieee;
library ieee_proposed;
use ieee_proposed.float_pkg.all;
use ieee.numeric_std.all;
entity SM is
end entity;
architecture foo of SM is
-- From float_pkg_c.vhdl line 391/3927 (package float_pkg):
-- -- to_float (signed)
-- function to_float (
-- arg : SIGNED;
-- constant exponent_width : NATURAL := float_exponent_width; -- length of FP output exponent
-- constant fraction_width : NATURAL := float_fraction_width; -- length of FP output fraction
-- constant round_style : round_type := float_round_style) -- rounding option
-- return UNRESOLVED_float is
begin
UNLABELLED:
process
variable denum : integer;
variable num : integer;
variable dividend : float (4 downto -27);
begin
denum := 42;
num := 21;
dividend := to_float(TO_SIGNED(num,32), 4, 27) / to_float(TO_SIGNED(denum,32), 4, 27);
assert dividend /= 0.5
report "dividend = " & to_string(dividend)
severity NOTE;
wait;
end process;
end architecture;
I don't think you really want to synthesize the integer version of to_float. Unfolding the loop gives you a bunch of ripple adds for decrementing shiftr and adjusting arg_int. Trying to get rid of those operations leads you to a bit array style representation of an integer.
Note there is no loop in the to_float who's arg type is signed. It's likely the TO_SIGNED calls are simply seen as defining the number of bits representing the size of integers instead of implying additional hardware. You end up with something converting bit fields and normalizing, clamping to infinity, etc..
You cast to float using the to_float function overload you are already using.
Your variables num and denum are uninitialized and default to integer'left which is -2**31. The to_float function tries to convert negative numbers to positive to stay within the natural range of arg_int but integer'high is limited to 2**31-1 and can't represent -integer'low. Set them to an initial value other than the default and see what happens.
From float_pkg_c.vhdl:
if arg < 0 then
result (exponent_width) := '1';
arg_int := -arg; -- Make it positive.

Convert hex- bin- or decimal string to long long in C++

I have this code which handles Strings like "19485" or "10011010" or "AF294EC"...
long long toDecimalFromString(string value, Format format){
long long dec = 0;
for (int i = value.size() - 1; i >= 0; i--) {
char ch = value.at(i);
int val = int(ch);
if (ch >= '0' && ch <= '9') {
val = val - 48;
} else {
val = val - 55;
}
dec = dec + val * (long long)(pow((int) format, (value.size() - 1) - i));
}
return dec;
}
this code works for all values which are not in 2's complement.
If I pass a hex-string which is supposed to be a negativ number in decimal I don't get the right result.
If you don't handle the minus sign, it won't handle itself.
Check for it, and memorize the fact you've seen it. Then, at
the end, if you'd seen a '-' as the first character, negate
the results.
Other points:
You don't need (nor want) to use pow: it's just
results = format * results + digit each time through.
You do need to validate your input, making sure that the digit
you obtain is legal in the base (and that you don't have any
other odd characters).
You also need to check for overflow.
You should use isdigit and isalpha (or islower and
isupper) for you character checking.
You should use e.g. val -= '0' (and not 48) for your
conversion from character code to digit value.
You should use [i], and not at(i), to read the individual
characters. Compile with the usual development options, and
you'll get a crash, rather than an exception, in case of error.
But you should probably use iterators, and not an index, to go
through the string. It's far more idiomatic.
You should almost certainly accept both upper and lower case
for the alphas, and probably skip leading white space as well.
Technically, there's also no guarantee that the alphabetic
characters are in order and adjacent. In practice, I think you
can count on it for characters in the range 'A'-'F' (or
'a'-'f', but the surest way of converting character to digit
is to use table lookup.
You need to know whether the specified number is to be interpreted as signed or unsigned (in other words, is "ffffffff" -1 or 4294967295?).
If signed, then to detect a negative number test the most-significant bit. If ms bit is set, then after converting the number as you do (generating an unsigned value) take the 1's complement (bitwise negate it then add 1).
Note: to test the ms bit you can't just test the leading character. If the number is signed, is "ff" supposed to be -1 or 255?. You need to know the size of the expected result (if 32 bits and signed, then "ffffffff" is negative, or -1. But if 64 bits and signed, "ffffffff' is positive, or 4294967295). Thus there is more than one right answer for the example "ffffffff".
Instead of testing ms bit you could just test if unsigned result is greater than the "midway point" of the result range (for example 2^31 -1 for 32-bit numbers).