Generic function in VHDL to extract an arbitrary byte from a std_logic_vector of any length? - vhdl

How to write a generic function that will extra a byte from a std_logic_vector based on an index value?
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
entity tmp is
end entity;
architecture beh of tmp is
function get_byte(
idx: in integer;
dat: in std_logic_vector
) return std_logic_vector is
constant msb :integer := (idx+1)*8 - 1;
constant lsb :integer := idx*8;
variable ret :std_logic_vector(7 downto 0);
begin
ret := dat(msb downto lsb);
return ret;
end function;
begin
process
constant vec :std_logic_vector := X"ABCDEF1234567";
variable b1 :std_logic_vector(7 downto 0);
variable m :line;
begin
b1 := get_byte(1, vec);
report "just kidding! end of testbench" severity failure;
end process;
end architecture;
Here's the error from my attempt:
C:\Xilinx\Vivado\2021.2\bin\xvhdl.bat --incr --relax --work work tmp.vhd
C:\Xilinx\Vivado\2021.2\bin\xelab.bat tmp -snapshot simout
Vivado Simulator v2021.2
Copyright 1986-1999, 2001-2021 Xilinx, Inc. All Rights Reserved.
Running: C:/Xilinx/Vivado/2021.2/bin/unwrapped/win64.o/xelab.exe tmp -snapshot simout
Multi-threading is on. Using 10 slave threads.
Starting static elaboration
ERROR: [VRFC 10-1378] slice direction differs from its index type range [C:/Users/xxx/Desktop/tmp/tmp.vhd:19]
ERROR: [XSIM 43-3321] Static elaboration of top level VHDL design unit tmp in library work failed.

ERROR: [VRFC 10-1378] slice direction differs from its index type
range
Is resolved by specifying the 'to' vs 'downto' ranges on each std_logic_vector declaration. (the default if not shown is to assumed 0 'to' N, not 'downto' - so when not shown/making simulator choose you are sorta mixing types).

As you don't know how your function will be called and what its parameter will be, a very simple approach consists in creating local copies with known ranges:
function get_byte(idx: natural; dat: std_logic_vector) return std_logic_vector is
constant size: natural := dat'length;
constant ldat: std_logic_vector(size-1 downto 0) := dat;
begin
assert 8*idx+7 <= size report "out of range index" severity failure;
return ldat(8*idx+7 downto 8*idx);
end function get_byte;

Not sure why, but it works if I write it this way:
function get_byte(
idx :in integer; -- 0=MS-Byte ... n=LS-BYTE
dat :in std_logic_vector -- uncontrained slv is a "to"
-- not a "Downto" vector?
) return std_logic_vector is
constant msb :integer := (idx+1)*8 - 1;
constant lsb :integer := idx*8;
variable ret :std_logic_vector(7 downto 0);
begin
ret := dat(idx*8 + 0)
& dat(idx*8 + 1)
& dat(idx*8 + 2)
& dat(idx*8 + 3)
& dat(idx*8 + 4)
& dat(idx*8 + 5)
& dat(idx*8 + 6)
& dat(idx*8 + 7);
return ret;
end function;

Related

a library error: primary unit 'numeric_std' denoted by 'IEEE' must exist in library

I have a program MAX + plus II version 10.2 07/10/2002. Found in the book program code VHDL - project. This project is called the "N-level processor." My program MAX + plus II version 10.2 throws out a library error: primary unit 'numeric_std' denoted by 'IEEE' must exist in library. The program code is given below:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ste1_un is
port (clk : in std_logic;
pok : in integer range 0 to 255;
x : in unsigned (63 downto 0);
y : out unsigned (63 downto 0));
end ste1_un;
architecture ste1_un of ste1_un is
begin
process(clk)
variable poka : integer range 0 to 255;
variable res : unsigned (63 downto 0);
variable res1 : unsigned (63 downto 0) :=
X"0000000000000001";
begin
if clk'event and clk = '1' then
res1 :=x; -- X"0000000000000003";
poka := pok;
for i in 1 to poka loop
res := resize((res * res1), 64) ;
end loop;
y <= res;
res := X"0000000000000001";
end if;
end process;
end ste1_un;
I ask for help!

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.

Entries for Subprograms in VHDL

i have two functions in my code :
function derivative(error, previous_error, dt :in std_logic_vector(7 downto 0)) return std_logic_vector is
variable derivative_val: std_logic_vector(15 downto 0);
begin
derivative_val := div(sub(error,previous_error),dt);
return derivative_val;
end derivative;
function mul(num1,num2 : in std_logic_vector(7 DOWNTO 0)) return std_logic_vector is
variable v_TEST_VARIABLE1 : integer;
variable v_TEST_VARIABLE2 : integer;
variable n_times: integer:=1;
variable product: integer:=0;
begin
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
for n_times in 1 to v_TEST_VARIABLE2 loop
product:=product + v_TEST_VARIABLE1;
end loop;
return std_logic_vector(to_unsigned(product,16));
end mul;
In the later half I am trying to assign a variable.
variable derivative_term: std_logic_vector(15 downto 0) := x"0000";
derivative_term := mul(mul(Kp,Td), derivative(error, previous_error,dt));
On compilation, I am getting :
No feasible entries for subprogram "mul".
Is there any other way to use it?
thanks in advance.
The mul function takes arguments num* with type std_logic_vector(7 downto
0) thus length 8, and returns result with type std_logic_vector of length
16.
So when calling mul(mul(...), ...) the outer mul gets first argument with
length 16 of type std_logic_vector, which does not match the required
argument length for the function.
Instead of writing you own multiplication function, you could use the "*" from
ieee.numeric_std, which can be used as:
slv_16_0 <= std_logic_vector(unsigned(slv_8_0) * unsigned(slv_8_1));
It also handles unknown values, like 'X', and resulting length is sum of
length for the two arguments.

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)))

VHDL How to convert 32 bit variable to 4 x 8bit std_logic_vector?

I have a question which is probably in 2 parts:
I am using a (nominally 32 bit) integer variable which I would like to write to an 8 bit UART as 4 bytes (i.e., as binary data)
i.e. variable Count : integer range 0 to 2147483647;
How should I chop the 32 bit integer variable into 4 separate 8 bit std_logic_vectors as expected by my UART code, and how should I pass these to the UART one byte at a time ?
I am aware std_logic_vector(to_unsigned(Count, 32)) will convert the integer variable into a 32 bit std_logic_vector, but then what ? Should I create a 32 bit std_logic_vector, assign the converted Count value to it, then subdivide it using something like the following code ? I realise the following assumes the count variable does not change during the 4 clock cycles, and assumes the UART can accept a new byte every clock cycle, and lacks any means of re-triggering the 4 byte transmit cycle, but am I on the right track here, or is there a better way ?
variable CountOut : std_logic_vector(31 downto 0);
process (clock)
variable Index : integer range 0 to 4 := 0;
begin
if rising_edge(clock) then
CountOut <= std_logic_vector(to_unsigned(Count, 32);
if (Index = 0) then
UartData(7 downto 0) <= CountOut(31 downto 24);
Index := 1;
elsif (Index = 1) then
UartData(7 downto 0) <= CountOut(23 downto 16);
Index := 2;
elsif (Index = 2) then
UartData(7 downto 0) <= CountOut(15 downto 8);
Index := 3;
elsif (Index =31) then
UartData(7 downto 0) <= CountOut(7 downto 0);
Index := 4;
else
Index := Index;
end if;
end if;
end process;
Any comments or recommendations would be appreciated.
Thanks,
MAI-AU.
You seem to be on the right track. I believe there are two basic solutions to this problem:
Register the output value as a 32-bit vector, and use different ranges for each output operation (as you did in your code example)
Register the output value as a 32-bit vector, and shift this value 8 bits at a time after each output operation. This way you can use the same range in all operations. The code below should give you an idea:
process (clock)
variable Index: integer range 0 to 4 := 0;
begin
if rising_edge(clock) then
if (Index = 0) then
CountOut <= std_logic_vector(to_unsigned(Count, 32));
Index := Index + 1;
elsif (Index < 4) then
UartData <= CountOut(31 downto 24);
CountOut <= CountOut sll 8;
Index := Index + 1;
end if;
end if;
end process;
Also, please check your assignments, in your example CountOut is declared as a variable but is assigned to as a signal.
There's nothing wrong with the code you've shown. You can do something to separate the the assignment to UartData using Index to allow a loop.
library ieee;
use ieee.std_logic_1164.all;
entity union is
end entity;
architecture foo of union is
type union32 is array (integer range 1 to 4) of std_logic_vector(7 downto 0);
signal UartData: std_logic_vector(7 downto 0);
begin
TEST:
process
variable quad: union32;
constant fourbytes: std_logic_vector(31 downto 0) := X"deadbeef";
begin
quad := union32'(fourbytes(31 downto 24), fourbytes(23 downto 16),
fourbytes(15 downto 8),fourbytes(7 downto 0));
for i in union32'RANGE loop
wait for 9.6 us;
UartData <= Quad(i);
end loop;
wait for 9.6 us; -- to display the last byte
wait; -- one ping only
end process;
end architecture;
Or use a type conversion function to hide complexity:
library ieee;
use ieee.std_logic_1164.all;
entity union is
type union32 is array (integer range 1 to 4) of std_logic_vector(7 downto 0);
end entity;
architecture fee of union is
signal UartData: std_logic_vector(7 downto 0);
function toquad (inp: std_logic_vector(31 downto 0)) return union32 is
begin
return union32'(inp(31 downto 24), inp(23 downto 16),
inp(15 downto 8), inp( 7 downto 0));
end function;
begin
TEST:
process
variable quad: union32;
constant fourbytes: std_logic_vector(31 downto 0) := X"deadbeef";
begin
quad := toquad (fourbytes);
for i in union32'RANGE loop
wait for 9.6 us;
UartData <= Quad(i);
end loop;
wait for 9.6 us; -- to display the last byte
wait; -- one ping only
end process;
end architecture;
And gives the same answer.

Resources