Conversion from numeric_std unsigned to std_logic_vector in vhdl - vhdl

I have a question related to conversion from numeric_std to std_logic_vector. I am using moving average filter code that I saw online and filtering my ADC values to stable the values.
The filter package code is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package filterpack is
subtype number is unsigned(27 downto 0);
type numbers is array(natural range <>) of number;
function slv_to_num(signal slv: in std_logic_vector) return number;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
);
end filterpack;
package body filterpack is
function slv_to_num(signal slv: in std_logic_vector) return number is
variable x: number := (others => '0');
begin
for i in slv'range loop
if slv(i) = '1' then
x(i+4) := '1';
end if;
end loop;
return x;
end function slv_to_num;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
) is
begin
h(0) <= x + h(1); -- h[n] = x[n] + h[n-1]
y <= h(0) - h(h'high); -- y[n] = h[n] - h[n-M]
end MAF_filter;
end package body filterpack;
In my top level file, I call the MAF_filter procedure.
Asign_x: x <= slv_to_num(adc_dat);
Filter: MAF_filter(x,h,y);
The adc_dat is defined as:
adc_dat : out std_logic_vector (23 downto 0);
I want to convert the output of the MAF_Filter to std_logic_vector (23 downto 0). Can anyone tell how can I convert filter output 'y' to 'std_logic_vector'?
Many Thanks!

What do you want to do with the 4 extra bits? Your type number has 28 bits, but your signal adc_dat has only 24.
If it's ok to discard them, you could use:
adc_dat <= std_logic_vector(y(adc_dat'range));
Also, is there a reason not to write your function slv_to_num as shown below?
function slv_to_num(signal slv: in std_logic_vector) return number is
begin
return number(slv & "0000");
end function slv_to_num;

The conversion has to solve 2 problems : the type difference you noted, and the fact that the two words are different sizes.
The type difference is easy : std_logic_vector (y) will give you the correct type. Because the two types are related types, this is just a cast.
The size difference ... only you have the knowledge to do that.
adc_dat <= std_logic_vector(y(23 downto 0)) will give you the LSBs of Y - i.e. the value of Y itself, but can overflow. Or as Rick says, adc_dat <= std_logic_vector(y(adc_dat'range)); which is usually better, but I wanted to expose the details.
adc_dat <= std_logic_vector(y(27 downto 4)) cannot overflow, but actually gives you y/16.

Related

Direction independent slicing

I'm creating a package with some functions I often use and some functions need to take slices of their parameters. I usually use downto direction for all my signals, but sometimes signals change their direction unexpectedly, e.g., appending a zero bit (sig & '0') seems to change the direction to positive.
Is there a way to slice arrays (std_logic_vector, unsigned, signed) independent of their direction? For example how would you implement a function taking the lowest two bits? The only implementation I came up with uses an additional constant with the expected direction:
function take_two(x : std_logic_vector) return std_logic_vector is
constant cx : std_logic_vector(x'length-1 downto 0) := x;
begin
return cx(1 downto 0);
end function;
I've also tried something like x(x'low+1 downto x'low) but Quartus doesn't like this.
The question is actually not on the input, but on the required output. What do you prefer?
If you look at how functions are implemented in for instance std_logic_1164-body.vhdl, your function would similarly be something like (in a complete example):
entity e is end entity;
library ieee;
architecture a of e is
use ieee.std_logic_1164.all;
signal test : std_logic_vector(7 downto 0) := "10010110";
signal output : std_logic_vector(2 downto 0);
function slice(s: STD_LOGIC_VECTOR; u, l : natural) return STD_LOGIC_VECTOR is
alias sv : STD_LOGIC_VECTOR (s'length-1 downto 0) is s;
variable result : STD_LOGIC_VECTOR (u downto l);
begin
for i in result'range loop
result(i) := sv(i);
end loop;
return result;
end function;
begin
output <= slice(test & '0', 5, 3); -- test becomes 'to' range.
-- output still becomes "101"
end architecture;

Combining `others` expression with `signed` cast

Let var stand for a signed vector (library IEEE.NUMERIC_STD.ALL) of size m.
Let foo be another variable of type std_logic_vector(n-1 downto 0), where n is smaller than m.
I want to concatenate a '0' left of foo, then pad it with zeroes in its right until it size is m and then store the result in var.
I tried
rdsor <= signed('0' & divisor & others=>'0');
But Xilinx complains with the following message on synthesis:
Syntax error near "others".
How do I do what I want?
Assuming rdsor is equivalent to your theoretical var and divisor equivalent to foo you could use two assignments in a process statement:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity jsevillamol is
end entity;
architecture fum of jsevillamol is
constant M: natural := 42;
constant N: natural := 23;
signal rdsor: signed (M - 1 downto 0);
signal divisor: std_logic_vector (N - 1 downto 0);
begin
-- rdsor <= signed('0' & divisor & others=>'0');
process (divisor)
begin
rdsor <= (others => '0');
rdsor (rdsor'LEFT downto rdsor'LEFT - divisor'LENGTH)
<= signed('0' & divisor);
end process;
end architecture;
This works because each element of rdsor is a separate signal and there is only one value for any particular time in a projected output waveform. By not providing an after time_expression in the waveform element of the second assignment the elements of rdsor slice will be assigned the second assignments expression values. (The elements of the first assignment are supplanted by the second). This method of overwriting the projected output waveform is commonly used in providing default values prior to incomplete condition coverage with if statements.
This example analyzes, elaborates and simulates, while doing nothing interesting it demonstrates index ranges are constructed properly.
Notice it avoids the issue of concatenation versus aggregation brought up by Matthew Taylor's answer.
For a single signal assignment in a method not sensitive to tool VHDL revision:
architecture fie of jsevillamol is
constant M: natural := 42;
constant N: natural := 23;
signal rdsor: signed (M - 1 downto 0);
signal divisor: std_logic_vector (N - 1 downto 0);
subtype other is signed (rdsor'LEFT - divisor'LENGTH - 1 downto 0);
begin
-- rdsor <= signed('0' & divisor & others=>'0');
rdsor <= '0' & signed(divisor) & other'(others => '0');
end architecture;
This uses concatenation and subsumes the others into an aggregate. There's a subtype declaration for the trailing '0's portion to allow the aggregate expression to be the target of a qualified expression.
This architecture also analyzes, elaborates and simulates proving index arithmetic is correct.
You would need to use others as part of an aggregate not part of a concatenation. Here's a solution using an aggregate and attributes (which relies on you using VHDL 2008):
rdsor <= (rdsor'LEFT => '0', (rdsor'LEFT-1) downto (rdsor'LEFT-divisor'LENGTH) => signed(divisor), others => '0');
https://www.edaplayground.com/x/5Yuw

Converting a std_logic_vector to integer within Process to test values?

What I'm trying to do is pretty simple, just generating a pulse from a basic counter. My code is shown below. My question is if there's an efficient way of comparing a std_logic_vector and an integer? I only need to compare them at that one instance in the process. Also, can you do aritmetic on a 4 bit signal as shown in my code? DO you need a specific library?
signal Top16: std_logic; -- 1 clk spike at 16x baud rate
signal Div16: std_logic_vector(3 downto 0);
DIVISOR: natural := 120 -- Can be 120 or 60, depending on user preference.
------------------------------------------------------------------------
process (RST, LCLK_MULT_BUFG)
begin
if RST='1' then
Top16 <= '0'; --1 bit signal
Div16 <= x"0"; -- 4 bit signal
elsif rising_edge(LCLK_MULT_BUFG) then
Top16 <= '0';
if Div16 = Divisor then -----> signal to integer comparison?
Div16 <= 0;
Top16 <= '1';
else
Div16 <= Div16 + 1; -----arithmetic on std_logic_vector??
end if;
end if;
EDIT:
The number of bits within the Div16 std_logic_vector will vary depending on the size of Divisor chosen (shown below). How to correctly format this? What libraries will be needed?
DIVISOR: natural := 120 -- Can be 120 or 60, depending on user preference.
constant COUNTER_BITS : natural := integer(ceil(log2(real(DIVISOR))));
signal Div16: std_logic_vector(COUNTER_BITS);
If at all possible, avoid the non-standard std_logic_unsigned library. It would be better to use numeric_std and declare Div16 as unsigned.
signal Div16: unsigned(3 downto 0);
Then your comparison and arithmetic should simply work. And of course it's synthesisable.
Your bonus question should also be synthesisable though DIVISOR ought to be a CONSTANT so that it can be evaluated at compile time, and I think you meant
signal Div16: unsigned(COUNTER_BITS - 1 downto 0);
For the arithmetic you can use std_logic_unsigned. This library contains the following functions:
function "+"(L: STD_LOGIC_VECTOR; R: INTEGER) return STD_LOGIC_VECTOR;
function "+"(L: INTEGER; R: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR;
For the comparison you can just leave it like this if you are using std_logic_unsigned. This library contains the following functions:
function "="(L: STD_LOGIC_VECTOR; R: INTEGER) return BOOLEAN;
function "="(L: INTEGER; R: STD_LOGIC_VECTOR) return BOOLEAN;
You could also define Div16 as unsigned and then use numeric_std. This library contains the following functions for comparison:
function "=" ( L: NATURAL; R: UNSIGNED) return BOOLEAN;
function "=" ( L: UNSIGNED; R: NATURAL) return BOOLEAN;
And for the addition:
function "+" ( L: UNSIGNED; R: NATURAL) return UNSIGNED;
function "+" ( L: NATURAL; R: UNSIGNED) return UNSIGNED;

VHDL adder, same word length?

In VHDL i want to add a number of 5 bits and a number of 8 bits.(Unsigned) And how many bits does the output have?
I want my code to answer the questions i just asked. My code currently look like this...
My code is:
library ieee;
use ieee-std_logic_1164.all;
entity adder is
port( a : in unsigned (7 downto 0);
b : in unsigned (4 downto 0); - - Need to convert this to 8 bit right? But how?
z : out unsigned(7 downto 0)); - - This one must be 8 bits right? Cuz a & b & z must have the same WL. Or am i wrong?
end adder;
archictecture add of adder is
begin
z <= a + b;
end archictecture;
In package numeric_std for function "+" (L, R: UNSIGNED) return UNSIGNED the length of the longest argument defines the return value length:
function "+" (L, R: UNSIGNED) return UNSIGNED is
constant SIZE: NATURAL := MAX(L'LENGTH, R'LENGTH);
variable L01 : UNSIGNED(SIZE-1 downto 0);
variable R01 : UNSIGNED(SIZE-1 downto 0);
begin
if ((L'LENGTH < 1) or (R'LENGTH < 1)) then return NAU;
end if;
L01 := TO_01(RESIZE(L, SIZE), 'X');
if (L01(L01'LEFT)='X') then return L01;
end if;
R01 := TO_01(RESIZE(R, SIZE), 'X');
if (R01(R01'LEFT)='X') then return R01;
end if;
return ADD_UNSIGNED(L01, R01, '0');
end "+";
The maximum of the left and right arguments length is SIZE, the range of the two arguments is is resized to SIZE -1 downto 0 as arguments to ADD_UNSIGNED.
function ADD_UNSIGNED (L, R: UNSIGNED; C: STD_LOGIC) return UNSIGNED is
constant L_LEFT: INTEGER := L'LENGTH-1;
alias XL: UNSIGNED(L_LEFT downto 0) is L;
alias XR: UNSIGNED(L_LEFT downto 0) is R;
variable RESULT: UNSIGNED(L_LEFT downto 0);
variable CBIT: STD_LOGIC := C;
begin
for I in 0 to L_LEFT loop
RESULT(I) := CBIT xor XL(I) xor XR(I);
CBIT := (CBIT and XL(I)) or (CBIT and XR(I)) or (XL(I) and XR(I));
end loop;
return RESULT;
end ADD_UNSIGNED;
The RESULT's length is that of the L argument which is the same of both arguments to UNSIGNED_ADD. There is no carry out implied in the result.
As in your case the result, assigned to z can be 8 bits.
Fix the comment delimiters in the port declarations, add a use clause to access package numeric_std, fix a '-' that should be a '.', spelling of architecture and add a test bench adding values for a and b set to all '1's and you can analyze, elaborate and run your design without error, telling you there isn't an array length error executing.
To get that ninth 'bit' as an output of the adder you can RESIZE one of your arguments to + to 9 bits or concatenate one argument with leading zeros to make a 9 bit value:
z <= "0" & a + b;
It'll demonstrate that the 9th bit is needed for an accurate result:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity adder is
port(
a: in unsigned (7 downto 0);
b: in unsigned (4 downto 0);
z: out unsigned (8 downto 0)
);
end adder;
architecture add of adder is
begin
z <= "0" & a + b;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_adder is
end entity;
architecture foo of tb_adder is
signal a: unsigned (7 downto 0) := (others => '1');
signal b: unsigned (4 downto 0) := (others => '1');
signal z: unsigned (8 downto 0);
function unsigned_image(inp: unsigned) return string is
variable image_str: string (1 to inp'length);
alias input_str: unsigned (1 to inp'length) is inp;
begin
for i in input_str'range loop
image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
end loop;
return image_str;
end;
begin
DUT:
entity work.adder
port map (
a => a,
b => b,
z => z
);
MONITOR:
process
begin
wait for 1 ns;
report "z = " & unsigned_image(z);
wait;
end process;
end architecture;
david_koontz#Macbook: ghdl -a adder.vhdl
david_koontz#Macbook: ghdl -e tb_adder
david_koontz#Macbook: ghdl -r tb_adder
adder.vhdl:54:9:#1ns:(report note): z = 100011110
Other than this correction:
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
Your code is fine.
Additionally, adding an 8 bit number and a 5 bit number produces a 9 bit number, because you can overflow. For example, "11111111" + "11111" overflows a 8 bit output, but doesn't overflow a 9 bit output.

VHDL: Is there a convenient way to assign ascii values to std_logic_vector?

In verilog, I can assign a string to a vector like:
wire [39:0] hello;
assign hello = "hello";
In VHDL, I'm having difficulty finding a method like this:
SIGNAL hello : OUT std_logic_vector (39 DOWNTO 0);
...
hello <= "hello";
I've been using:
hello <= X"65_68_6c_6c_6f";
which is unclear and time consuming for large strings.
I've looked at the textio package and thetxt_util package, but neither seem to be very clear on how to interpret a string and convert it to std_logic.
Is there a simple method of assigning ascii codes to std_logic in VHDL?
Here's a minimal example:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY test IS
PORT(
ctrl : IN std_logic;
stdout : OUT std_logic_vector (39 DOWNTO 0)
);
END ENTITY;
ARCHITECTURE rtl OF test IS
SIGNAL temp : std_logic_vector (39 DOWNTO 0);
BEGIN
stdout <= temp;
PROCESS(ctrl)
BEGIN
IF (ctrl = '0') THEN
temp <= "hello"; -- X"68_65_6C_6C_6F";
ELSE
temp <= "world";
END IF;
END PROCESS;
END rtl;
This one varies little for Morten's answer - it only uses one multiply, it copies the string instead of creating an alias, it uses an additional variable and it returns a standard logic vector with an ascending index range.
From a package called string_utils:
library ieee;
use ieee.numeric_std.all;
-- ...
function to_slv(s: string) return std_logic_vector is
constant ss: string(1 to s'length) := s;
variable answer: std_logic_vector(1 to 8 * s'length);
variable p: integer;
variable c: integer;
begin
for i in ss'range loop
p := 8 * i;
c := character'pos(ss(i));
answer(p - 7 to p) := std_logic_vector(to_unsigned(c,8));
end loop;
return answer;
end function;
You could add an argument with a default specifying ascending/descending index range for the return value. You'd only need to provided the argument for the non default.
A small general function is one way to do it, with a suggestion below:
library ieee;
use ieee.numeric_std.all;
...
-- String to std_logic_vector convert in 8-bit format using character'pos(c)
--
-- Argument(s):
-- - str: String to convert
--
-- Result: std_logic_vector(8 * str'length - 1 downto 0) with left-most
-- character at MSBs.
function to_slv(str : string) return std_logic_vector is
alias str_norm : string(str'length downto 1) is str;
variable res_v : std_logic_vector(8 * str'length - 1 downto 0);
begin
for idx in str_norm'range loop
res_v(8 * idx - 1 downto 8 * idx - 8) :=
std_logic_vector(to_unsigned(character'pos(str_norm(idx)), 8));
end loop;
return res_v;
end function;
To return an ascii value of a character, use this code:
some_variable <= character'pos('a'); --returns the 'a' ascii value
In your example you are trying to assign a string type to a std_logic_vector type.
That is simply not allowed. VHDL is strongly typed.
SIGNAL hello : OUT std_logic_vector (39 DOWNTO 0);
...
hello <= "hello";
If your goal is to convert from hexa to ascii for printing simulation result
you can simply do that:
character'val(to_integer(unsigned(my_std_logic_vector)))

Resources